为什么需要头文件
-
为了使用 hello 这个函数,我们刚才在 main.cpp 里声明了 void hello() 。
-
但是如果另一个文件 other.cpp 也需要用 hello 这个函数呢?也在里面声明一遍?
-
如果能够只写一遍,然后自动插入到需要用 hello 的那些 .cpp 里就好了……
![]()
-
没错,C 语言的前辈们也想到了,他们说,既然每个 .cpp 文件的这个部分是一模一样的,不如我把 hello() 的声明放到单独一个文件 hello.h 里,然后在需要用到 hello() 这个声明的地方,打上一个记号,#include “hello.h” 。然后用一个小程序,自动在编译前把引号内的文件名 hello.h 的内容插入到记号所在的位置,这样不就只用编辑 hello.h 一次了嘛~
-
后来,这个编译前替换的步骤逐渐变成编译器的了一部分,称为预处理阶段,#define 定义的宏也是这个阶段处理的。
-
此外,在实现的文件 hello.cpp 中导入声明的文件 hello.h 是个好习惯,可以保证当 hello.cpp 被修改时,比如改成 hello(int),编译器能够发现 hello.h 声明的 hello() 和定义的 hello(int) 不一样,避免“沉默的错误”。

为什么需要库(library)
- 有时候我们会有多个可执行文件,他们之间用到的某些功能是相同的,我们想把这些共用的功能做成一个库,方便大家一起共享。
- 库中的函数可以被可执行文件调用,也可以被其他库文件调用。
- 库文件又分为静态库文件和动态库文件。
- 其中静态库相当于直接把代码插入到生成的可执行文件中,会导致体积变大,但是只需要一个文件即可运行。
- 而动态库则只在生成的可执行文件中生成“插桩”函数,当可执行文件被加载时会读取指定目录中的.dll文件,加载到内存中空闲的位置,并且替换相应的“插桩”指向的地址为加载后的地址,这个过程称为重定向。这样以后函数被调用就会跳转到动态加载的地址去。
- Windows:可执行文件同目录,其次是环境变量%PATH%
- Linux:ELF格式可执行文件的RPATH,其次是/usr/lib等
![]()
编译链接的过程
-
预处理过程 预处理指定的执行 注释的消除
gcc -E hello.c -o hello.i -
编译 将c语言编译为汇编语言 检查程序中的语法错误
gcc -S hello.i -o hello.s -
汇编 将汇编语言的程序翻译为机器指令
gcc -c hello.s -
链接 将目标文件和运行时文件、库函数合成为可执行文件.(每个cpp会生成一个.o文件,要把所有用到的.o文件合到一起)
gcc hello.o -
程序的执行
./a.out
使用nm命令查看模块的符号表(函数名、全局变量、局部静态变量都是符号)
- nm sub.o 或者 nm a.out



查看搜索路径( 加 -v )
- gcc add.c -c -o add.i -v



浙公网安备 33010602011771号