#include <stdio.h>
int main(int argc, char* argv[])
{
printf("Hello World!\n");
return 0;
}
从源文件Hello.cpp编译链接成Hello.exe,需要经历如下步骤:
可使用以下命令,直接从源文件生成可执行文件
linux:
gcc -lstdc++ Hello.cpp -o Hello.out // 要带上lstdc参数,否则会报undefined reference to '__gxx_personality_v0'错误
g++ Hello.cpp -o Hello.out
注:后缀为.c的文件gcc把它当做c代码,而g++当做c++代码;gcc与g++都是调用器,最终调用的编译器为cc1(c代码),cc1plus(c++c代码)。
另外,链接阶段gcc不会自动和c++标准库链接,需要带上-lstdc++参数才能链接。
windows:
cl Hello.cpp /link -out:Hello.exe
预处理:主要是做一些代码文本的替换工作。(该替换是一个递归逐层展开的过程。)
(1)将所有的#define删除,并展开所有的宏定义
(2)处理所有的条件预编译指令,如:#if #ifdef #elif #else #endif
(3)处理#include预编译指令,将被包含的文件插进到该指令的位置,这个过程是递归的
(4)删除所有的注释//与/* */
(5)添加行号与文件名标识,以便产生调试用的行号信息以及编译错误或警告时能够显示行号
(6)保留所有的#pragma编译器指令,因为编译器需要使用它们
- linux:
cpp Hello.cpp > Hello.i
gcc -E Hello.cpp -o Hello.i
g++ -E Hello.cpp -o Hello.i
- 行号与文件名标识解释:
# 32 "/usr/include/bits/types.h" 2 3 4 // 表示下面行为types.h的第32行
typedef unsigned char __u_char;
typedef unsigned short int __u_short;
typedef unsigned int __u_int;
typedef unsigned long int __u_long;
- 以上,#行的行末的数字2 3 4的含义:
- 1 - 打开一个新文件
2 - 返回上一层文件
3 - 以下的代码来自系统文件
4 - 以下的代码隐式地包裹在extern "C"中
不产生行号与文件名标识:
cpp -P Hello.cpp > Hello.i
gcc -E -P Hello.cpp -o Hello.i
g++ -E -P Hello.cpp -o Hello.i
windows:
cl /E Hello.cpp > Hello.i
行号与文件名标识解释:
#line 283 "C:\\Program Files\\Microsoft Visual Studio\\VC98\\include\\stdio.h" // 表示下面行为stdio.h的第283行
void __cdecl clearerr(FILE *);
int __cdecl fclose(FILE *);
int __cdecl _fcloseall(void);
不产生行号与文件名标识:
cl /EP Hello.cpp > Hello.i
汇编:汇编代码->机器指令。
linux:
as Hello.s -o Hello.o
gcc -c Hello.cpp -o Hello.o
g++ -c Hello.cpp -o Hello.o
windows:
cl /c Hello.cpp > Hello.obj
至此,产生的目标文件在结构上已经很像最终的可执行文件了。
链接:这里讲的链接,严格说应该叫静态链接。多个目标文件、库->最终的可执行文件(拼合的过程)。
可执行文件分类:
linux的ELF文件 -- bin、a、so
windows的PE文件 -- exe、lib、dll
注:PE文件与ELF文件都是COFF文件的变种
linux:
ld -static /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/gcc/i586-suse-linux/4.1.2/crtbeginT.o -L/usr/lib/gcc/i586-suse-linux/4.1.2/ -L/usr/lib -L/lib Hello.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/i586-suse-linux/4.1.2/crtend.o /usr/lib/crtn.o -o Hello.out
注:-static:强制所有的-l选项使用静态链接; -L:链接外部静态库与动态库的查找路径;
-l:指定静态库的名称(最后库的文件名为:libgcc.a、libgcc_eh.a、libc.a);
--start-group ... --end-group:之间的内容只能为文件名或-l选项;为了保证内容项中的符号能被解析,链接器会在所有的内容项中循环查找。
这种用法存在性能开销,最好是当有两个或两个以上内容项之间存在有循环引用时才使用。
windows:
link /subsystem:console /out:Hello.exe Hello.obj
静态库本质上就是包含一堆中间目标文件的压缩包,就像zip等文件一样,里面的各个中间文件包含的外部符号地址是没有被链接器修正的。
查看静态库中的内容
linux:
ar -t libc.a
windows:
lib /list libcmt.lib
解压静态库中的内容
linux:【将libc.a中所有的o文件解压到当前目录下】
ar -x /usr/lib/libc.a
windows:【将libcmt.lib中的atof.obj解压到当前目录下】
lib libcmt.lib /extract:build\intel\mt_obj\atof.obj
生成静态库
linux:
ar -rf test.a main.o fun.o
windows:
lib /out:test.lib main.obj fun.obj