编译与链接过程详解

来源:互联网 发布:mac atom菜单栏不见了 编辑:程序博客网 时间:2024/04/28 17:31

1.常见预定义符号的含义
FILE //进⾏编译的源⽂件
LINE //⽂件当前的⾏号
DATE //⽂件被编译的⽇期
TIME //⽂件被编译的时间
STDC //如果编译器遵循ANSI C,其值为1,否则未定义

注:这些预定义符号的首尾为两个下划线,如果是两个单词,中间以一个下划线连接;如果在源代码使用这些预定义符号,它们会在预处理阶段被转换。
在Windows系统下测试结果:
这里写图片描述

2.预处理符号#与##
一般用法 ,我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起.
C语言中的#号和##号的作用详情见:
http://blog.chinaunix.net/uid-27666459-id-3772549.html

3.宏和函数的区别
a.宏会在编译器在对源代码进行编译的时候进行简单替换,不会进行任何逻辑检测,即简单代码复制而已。
b.宏进行定义时不会考虑参数的类型。
c.参数宏的使用会使具有同一作用的代码块在目标文件中存在多个副本,即会增长目标文件的大小。
d.参数宏的运行速度会比函数快,因为不需要参数压栈/出栈操作。
e.参数宏在定义时要多加小心,多加括号。
f.函数只在目标文件中存在一处,比较节省程序空间。
g.函数的调用会牵扯到参数的传递,压栈/出栈操作,速度相对较慢。
h.函数的参数存在传值和传地址(指针)的问题,参数宏不存在。

4.编译链接的整个过程和详细的每个过程
因为Windows的VS为集成开发环境,集编译、汇编、链接于一体,而Linux通过单独工具进行编译、汇编、链接,所以要想详细的了解编译链接过程需要在Linux系统下进行。电脑只能识别二进制序列,而我们的代码是利用C语言写的,需要转换位机器所能识别的二进制序列,编译链接过程就是将代码转换成机器所能识别的二进制序列(目标文件)并生成可执行文件的过程。
整个过程为:
这里写图片描述
1.预处理:其中预处理阶段主要完成4个任务
a.宏替换
b.头文件展开
c.去注释
d.条件编译
linux下使用的gcc选项:
gcc -E test.c -o test.i
其中test.c为存放的源文件代码,生成目标文件test.i。
test.i的源文件代码:

#include <stdio.h>#define M 4int main(){   printf("helllo word\n");  //printf hello word.   printf("%d\n",M);   #if M   printf("hello word\n");   #endif   return 0;}

由于经预处理之后代码文件太长,所以只截取部分代码:

# 938 "/usr/include/stdio.h" 3 4# 2 "test.c" 2int main(){   printf("helllo word\n");   printf("%d\n",4);   printf("hello word\n");   return 0;}

明显可以看出完成了预处理的a、b、c、d四个任务.
2.编译:经过预处理阶段生成test.i文件之后,在经过编译过程,生成test.s文件,即汇编语言代码。
Linux下的gcc命令选项为-S:
gcc test.c -S -o test.s
生成的目标文件tset.s文件即汇编语言代码如下:

.file   "test.c"    .section    .rodata.LC0:    .string "helllo word".LC1:    .string "%d\n".LC2:    .string "hello word"    .text.globl main    .type   main, @functionmain:    pushl   %ebp    movl    %esp, %ebp    andl    $-16, %esp    subl    $16, %esp    movl    $.LC0, (%esp)    call    puts    movl    $.LC1, %eax    movl    $4, 4(%esp)    movl    %eax, (%esp)    call    printf    movl    $.LC2, (%esp)    call    puts    movl    $0, %eax    leave    ret    .size   main, .-main    .ident  "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-4)"    .section    .note.GNU-stack,"",@progbits

3.汇编:经过编译阶段生成test.s文件之后,在经过汇编过程,生成test.o文件,即计算机能够识别的二进制乱码。
Linux下的gcc命令选项为-o:
gcc test.c -c -o test.o
生成的目标文件tset.o文件即二进制代码(部分)如下(使用od命令将二进制乱码有序排列):

0000100 000000 000000 176350 177777 134377 000014 000000 0423070000120 002044 000004 000000 002211 164044 177774 177777 0023070000140 010044 000000 164000 177774 177777 000270 000000 1444000000160 000303 000000 062550 066154 067554 073440 071157 0001440000200 062045 000012 062550 066154 020157 067567 062162 0000000000220 041507 035103 024040 047107 024525 032040 032056 0334560000240 031040 030460 030062 030463 020063 051050 062145 0440400000260 072141 032040 032056 033456 032055 000051 027000 0745630000300 072155 061141 027000 072163 072162 061141 027000 0641630000320 072163 072162 061141 027000 062562 027154 062564 0721700000340 027000 060544 060564 027000 071542 000163 071056 0621570000360 072141 000141 061456 066557 062555 072156 027000 0675560000400 062564 043456 052516 071455 060564 065543 000000 0000000000420 000000 000000 000000 000000 000000 000000 000000 000000*

4.链接:经过汇编阶段生成test.o文件之后,在经过链接之后,生成.out可执行文件。
Linux下的gcc命令为:
gcc test.c
生成a.out的可执行文件,打开之后:

helllo word4hello word

上述就是整个编译链接过程。
4.关于预处理阶段的条件编译详细说明。
条件编译指令将决定那些代码被编译,而哪些是不被编译的。可以根据表达式的值或者某个特定的宏是否被定义来确定编译条件。
详细说明:
http://www.cnblogs.com/rusty/archive/2011/03/27/1996806.html

0 0
原创粉丝点击