C代碼通過編譯器編譯成可執行代碼,經歷了四個階段,依次為:預處理、編譯、匯編、鏈接。
接下來詳細講解各個階段
一、預處理
1、任務:進行宏定義展開、頭文件展開、條件編譯,不檢查語法。
2、命令:gcc -E [源文件] -o [預處理文件]
3、案例:用gcc編譯器預處理demo1.c代碼,預處理后的文本放到demo1.i中。(gcc -E demo1.c -o demo1.i)
demo1.c代碼如下:

1 #include <stdio.h> 2 3 #define add(a, b) (a + b) 4 #define sub(a, b) (a - b) 5 6 int main(void) 7 { 8 int a, b, c, d; 9 #ifndef __cplusplus 10 a = b = c = d = 1; 11 #else 12 a = b = c = d = 2; 13 #endif 14 printf("num = %d\n", sub(add(a, b), add(c, d))); 15 return 0; 16 }
生成的demo1.i代碼如下:

1 # 1 "demo.c" 2 # 1 "<command-line>" 3 # 1 "/usr/include/stdc-predef.h" 1 3 4 4 # 1 "<command-line>" 2 5 6 此處省略800行... 7 8 extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__)); 9 # 943 "/usr/include/stdio.h" 3 4 10 11 # 2 "demo.c" 2 12 13 14 int main(void) 15 { 16 int a, b, c, d; 17 18 a = b = c = d = 1; 19 20 21 22 printf("num = %d\n", ((a + b) - (c + d))); 23 return 0; 24 }
通過案例可以發現:#define宏定義、stdio.h頭文件、#ifdef條件編譯都被替換了,並且你還可以故意寫一句有語法錯誤的代碼,但是卻不會報錯。由於stdio.h頭文件長達800多行,因此在demo1.i中只截取開頭和結尾的幾行。
二、編譯
1、任務:檢查語法,將預處理過的文件編譯生成匯編文件。
2、命令:gcc -S [源文件] -o [匯編文件]
3、案例;用gcc編譯器編譯demo2.c代碼,編譯后的匯編代碼放到demo2.s中。(gcc -S demo2.c -o demo2.s)
demo2.c代碼如下:

1 #include <stdio.h> 2 3 int main(int argc, char *argv[]) 4 { 5 printf("hello world\n"); 6 return 0; 7 }
生成的demo2.s代碼如下:

1 .file "demo2.c" 2 .section .rodata 3 .LC0: 4 .string "hello world" 5 .text 6 .globl main 7 .type main, @function 8 main: 9 .LFB0: 10 .cfi_startproc 11 pushq %rbp 12 .cfi_def_cfa_offset 16 13 .cfi_offset 6, -16 14 movq %rsp, %rbp 15 .cfi_def_cfa_register 6 16 subq $16, %rsp 17 movl %edi, -4(%rbp) 18 movq %rsi, -16(%rbp) 19 movl $.LC0, %edi 20 call puts 21 movl $0, %eax 22 leave 23 .cfi_def_cfa 7, 8 24 ret 25 .cfi_endproc 26 .LFE0: 27 .size main, .-main 28 .ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2" 29 .section .note.GNU-stack,"",@progbits
通過案例可以看出,我們寫的c代碼被編譯成了匯編代碼。故意寫錯一個語法點,編譯器將報錯。
三、匯編
1、任務:將匯編文件生成目標文件(2進制文件)。
2、命令:gcc -s [源文件] -o [目標文件]
3、案例:用gcc編譯器匯編demo3.c代碼,編譯后的二進制代碼放到demo3.o中。(gcc -c demo3.c -o demo3.o)
demo3.c代碼如下:

1 #include <stdio.h> 2 3 int main(int argc, char *argv[]) 4 { 5 printf("hello world\n"); 6 return 0; 7 }
生成的demo3.o代碼如下:
通過匯編階段,文本代碼變成了二進制代碼,也就是計算機可以識別的代碼。c語言中,二進制代碼文件是以.o為后綴名的。
四、鏈接
1、任務:找到依賴的庫文件,將目標文件鏈接為可執行程序。
2、命令:gcc -c [目標文件] -o [可執行程序] -l[動態庫名]
3、案例:通過gcc編譯器讓demo4鏈接自己制作的libadd.so動態庫,並把demo4編譯成可執行程序。gcc demo4.c -o demo4 -L./ -ladd
demo4.c代碼如下:

1 #include <stdio.h> 2 #include "add.h" 3 4 int main(int argc, char *argv[]) 5 { 6 printf("add = %d\n", add(1, 1)); 7 return 0; 8 }
通過file命令查看可執行程序的信息:
運行結果:add = 2
還可以通過“size [可執行程序]”命令,來查看程序的text段、data段、bss段的大小。
在程序還沒運行前,我們是可以確定它的text段、data段、bss段的大小。