最好能讲解的详细点, 多谢
编译,汇编,链接,可重定位目标文件,符号,符号表
1
ian19znj 2018-07-27 09:53:26 +08:00
因为需要知道函数,变量以及宏的声明,否则无法调用。
|
2
msg7086 2018-07-27 10:22:48 +08:00
不一定要引入这个模块的.h 文件。
只要在调用前申明你所用到的文件外的函数、变量还有宏之类的就行了。 .h 文件只是简化这个过程。 |
3
LuckyKoala 2018-07-27 10:40:12 +08:00 via Android
分离接口和实现,把一个模块的函数,变量声明放到头文件中,具体实现也在同名.c 文件中。
算是规范吧,你不用头文件也是可以的。 稍大一点的项目,不同的开发者开发时只负责自己的一部分,但是需要知道其它部分的接口,这个时候就可以查看头文件,但不查看具体实现,避免过分依赖其他模块的实现。 |
4
LuckyKoala 2018-07-27 10:46:58 +08:00 via Android
https://gcc.gnu.org/onlinedocs/gcc-3.0.2/cpp_2.html
头文件有两个来源,一个是系统提供的接口,还有一个就是用户自定义的。 使用头文件还有一个好处就是所有一组的声明放在一个头文件中,外部通过 include 头文件来包含内容,需要修改声明的时候,修改一个头文件就行了,对应的引用这个头文件的地方也就应用修改了。 |
5
GPIO 2018-07-27 10:47:48 +08:00
码农翻身之前有篇文章叫《真正的程序员都应该搞清楚编译和链接》,本来想发链接给你的,不过要手机号验证所以作罢,你自己搜一下。
|
6
GeruzoniAnsasu 2018-07-27 10:50:09 +08:00
其实可以完全不需要头文件
你试试把需要的函数一个一个手动声明,也是照样可以正常链接的 但这不是,慢嘛,有能复用的声明干嘛要手动重写一遍? |
7
thomaswang OP @LuckyKoala 当 main 这个函数编译的时候,会有个 elf,里面有符号表,里面有一个符号指到其他模块的函数,这里面如何用到别的模块的函数定义,肯定是用了,但是不知道如何用的
|
8
Nitroethane 2018-07-27 10:59:36 +08:00 via Android
可以看看“深入理解计算机系统”的“链接”一章,讲得很清楚
|
9
LuckyKoala 2018-07-27 11:38:17 +08:00 via Android
@thomaswang https://en.m.wikipedia.org/wiki/Executable_and_Linkable_Format 这是 wiki 上对 elf 文件格式的介绍,至于如何链接的,你可以找找链接相关的文章看看。
系统学习的话,楼上提到的《深入理解计算机系统》我也很推荐。 |
10
thomaswang OP @Nitroethane 看完,你没有发现,不需要引入.h 文件理论上也是可以链接的吗,可是为什么不能呢,引入的作用是什么呢
|
11
yksoft1 2018-07-27 11:48:06 +08:00
不要.h 里的声明的话 C 编译器不知道这个外部符号的类型(返回值),会默认设为 int 并产生警告。C++里面对于函数以外的符号没有声明,直接就是错误了。
|
12
LuckyKoala 2018-07-27 11:50:51 +08:00 via Android
@thomaswang 为什么不能?你怎么写的?
不包含头文件的话,自己加上需要的声明就可以了。 加入 “ int printf(const char *format, ...);” 就可以调用 printf |
13
usufu 2018-07-27 11:51:28 +08:00
程序员的自我修养,这本书看完就知道了。
|
14
GeruzoniAnsasu 2018-07-27 12:05:30 +08:00
@thomaswang 不是 main 函数编译会有个 elf
每个.c 都会单独编译成待链接的.o 文件,其中包含本文件定义的强弱符号以及需要链接外部的未定义符号,所有未定义符号都会在链接阶段在所有链接文件中查找,并把对应的调用替换成实际函数地址 如果期望的某个未定义符号没有找到,一般就会报链接错误,但其实也可以用-undefined dynamic_lookup 强制所有未定义符号在运行期动态查找,标准库中的函数“自带这种定义”(不准确) 从原理上来说,main 函数其实也可以不用写的,只要 elf 文件头指定入口点就足够,但一般程序必须写 main 的原因是,编译器额外链接了某个.o,叫 /crt?+\.o/ 什么的,这个.o 自带一个 main 符号的引用,所以不写 main 链接这个.o 的时候查找 main 符号失败会像上面说的报链接错误,如果用参数选项控制不去链接额外的这个 crt 入口.o,就不一定需要 main 了,你可以再试试上面说的把 main 符号强制在运行期查找,会发现能链接出可执行文件,但这个程序你不 preload 一个带 main 的 lib 是跑不起来的 除了强弱符号,还有一类未定义符号,用 nm 查看类型是 U,这类符号会在 elf 被加载时由 ld.so 调用 dlresolve 在动态库中查找 |
15
GeruzoniAnsasu 2018-07-27 12:05:48 +08:00
------
emmmm 最后一段忘记删了 |
16
iceheart 2018-07-27 12:45:23 +08:00 via Android
1.编译过程
把 .c 源代码编译成机器码,名字是.o 2.链接过程 把编译好的各个.o 文件链接成一个可执行文件 3. 每个.c 编译过程都是独立互不相关的。也就是说,编译器编译 a.c 的时候编译器不知道有 b.c 存在,编译 b.c 的时候编译器不知道有 a.c 的存在。 基于以上关系说明,问题来了: => a.c 里使用了 b.c 里的一个函数,编译器编译 a.c 的时候不知道有 b.c,咋办? 编译器采取了一个办法: 1.使用一种约定,来描述 b.c 里边 a.c 要使用的函数,编译的时候根据约定生成调用指令。 2.让链接程序根据这些约定把 a.o,b.o 链接成可执行文件。 这个约定是什么?就是.h 头文件里的结构声明和函数声明。 |
17
thomaswang OP @iceheart 多谢你来解我疑惑,你是很明白的
a.c 在编译阶段,不需要约定也是可以,每个.o 文件都有一个符号表,里面有自己的函数符号,也有调用的别人的函数符号,当链接的时候,每个.o 文件到其他所有的.o 文件找自己符号表里面的调外部的函数符号,这样就可以了,是吧 |
18
thomaswang OP |
19
zuoxiaomo 2018-07-27 15:55:03 +08:00
@thomaswang @iceheart 建议见面后互相给对方起一个名字。。。
|
20
thomaswang OP @zuoxiaomo 何意
|
21
j5shi 2018-07-27 18:31:10 +08:00 via iPhone
谁说必须 include 那个头文件,其实有很多方法可以绕开
|
22
iceheart 2018-07-27 19:54:18 +08:00 via Android
说头文件是接口约定,只是一种方便理解的说法。这些东西完全可以复制到.c 里边,然后扔掉头文件。
但是这个约定还是在的,貌似大家都管他叫叫 ABI。 |
23
thomaswang OP @iceheart 吃饭的事 ok 哇,我就是想找个智者聊聊,我最近在职看机会
|