linux下c程序的链接、装载和库(2),linux

5. 重定义错误。

   
一个最终的可执行文件里,绝对不允许出现两个同名的全局变量,也不允许出现同名的全局函数。

    全局函数:只要不用 static 修饰符修饰的函数,全部都是全局的。

    全局变量:函数外声明定义,且不加 static 修饰符修饰的变量。

    例如,one.c 里有一个函数 function, 那么你如果想让 main.c 生成的
main.o 能够链接 one.o 的话,那么 main.c 里就不能再有一个函数叫做
function 了。否则就会报重定义错误。

    这就好像,你的班上有两个人都叫 小明,
这个情况确实麻烦,你肯定会用各种办法区分他们,比如说,大小明,小小明,这种方法,实际上,你已经为他们重新命名了。

6. 声明和定义。

    你也许发现了一个问题,那就是:main.c 里 有一句

    extern void function();

    这一句不是跟 one.c 里的 function 重名了么。那么编译器为啥不报错呢?

    想一想整个过程。

    第一步:

    gcc -c main.c -o main.o

    这一步就是编译 main.c
的过程,好吧,这一步,编译器完全不用关心在某个地方还有个
one.c,这个源文件里有一个同名的function。

    仔细解释一下这句话:

    extern void function();

    你写程序的时候,要有一种跟编译器时刻交流的感觉。

    这一句是你写给编译器看的,你就是要告诉编译器这样一个事实:

    –Hey!编译器!

    –main.c 这个源文件里要用到一个函数 function,是void
类型的,没有参数,你暂时不用管这个函数到底在哪,你先编译通过,这个函数最终会被你的哥们链接器链接过来!

    编译器:

    –ok!我相信我的哥们链接器!

    这个过程就叫做“声明”。如果没有这一句,没有这个过程,编译器就会在
main.c 里遇见一个陌生的函数调用 function,嗯,结果就是,败!

    而这个函数 function 的真正定义就是在 one.c
里。所谓定义,就是具体的实现,就是这个函数大括号里的东西。也可以这么说,没有大括号(即使没有
extern
修饰符)的地方就是声明,有大括号(即使大括号里是空的)的地方,那就是定义。

    还有一句,声明可以多次,比如说,每一个用到了函数 function
的地方,都要声明(你可以不用 extern
这个修饰符,试试吧)。但是,定义只有一个,这也符合上一小节说的:不能重定义!

7. 从硬盘到内存–装载。

    这一节不准备总结的太细。

   
你写好的源文件是放在硬盘上的,你编译成的目标文件也是放在硬盘上的,链接成的可执行文件也是放在硬盘上的。

    当你,运行这个可执行文件的时候,操作系统就会做一件事情:装载。

   
可以简单粗暴的假想,操作系统把你的可执行文件直接复制到内存的某个地方,然后,cpu开始在这个地方去找
main 函数,进而执行整个程序。

   
所以你的程序才会占内存的空间:变量会占,函数会占,动态开辟(例如malloc)的更会占。

8. 节省一点内存。

    你的同事十分能干,他用他闲暇的时间,积极地扩充他所维护的
one.c,使这个 one.c 更加丰富。比如说,他增加了一个函数

    int add(int a, int b)

    {

         //省略代码

    }

   
可是,你知道,函数最终是要占内存空间的。并且,你完全用不到这个新加的函数
add。

    问题就是,你链接的时候,已经把整个 one.o 链接到你的
可执行文件里了。这个可执行文件确实包含了 add
的具体实现的代码,也就是说,最终运行的时候,内存里确实会有这一部分,而且是完全没用的部分。

    你仅仅用到了 one.o 里的一个函数 function 就要链接整个
one.o,这就好像,你到饭店,只想吃一个汉堡,却不得不花钱买一份套餐,浪费。

    你不得不跟你的同事说明一下这个问题,最终你们商量出了一个办法。

   
你的同事决定,将他写的每一个函数单独放到一个源文件里去,比如说,function函数放到
function.c ,add函数放到 add.c。

   
这样,一个函数对应一个源文件,也对应一个目标文件,也就是说,一个目标文件里只有一个函数,没有其他的东西。

    你使用的时候,就能够随便挑选,去链接哪个目标文件了。

    比如说,你的同事已经有了这些:

    one.c 包含了 void one(){} 函数

    two.c 包含了 void two(){} 函数

    three.c 包含了 void three(){} 函数

    等等……。而且也提供了一份头文件叫做 all.h,
这个all.h包含了所有他编写的函数的声明。

    你使用的时候先包含这个all.h, 像这样

    #include “all.h”

   
然后,编译;然后链接的时候,你用到了哪个,就在你的gcc命令里加上那个目标文件就行。例如,用到了
void three() 这个函数:

    ld main.o three.o -o go

    这样就行了。

    但是即便是这样,
你还是觉得麻烦,你得在心里记录一下,你用了哪些函数,并且去手动敲下命令进而链接,这样容易出错。

    任何问题都是有可能解决的,这次也不例外。

    

5.
重定义错误。
一个最终的可执行文件里,绝对不允许出现两个同名的全局变量,也不允许出现同名…

相关文章