C代碼編譯成可執行程序的過程


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.c

生成的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 }
demo1.i

通過案例可以發現:#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.c

生成的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
demo.s

通過案例可以看出,我們寫的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.c

生成的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 }
demo4.c

通過file命令查看可執行程序的信息:

運行結果:add = 2

還可以通過“size [可執行程序]”命令,來查看程序的text段、data段、bss段的大小。

在程序還沒運行前,我們是可以確定它的text段、data段、bss段的大小。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM