從C語言變成最終的可執(zhí)行文件,需要經(jīng)過四步:
預處理;
編譯;
匯編;
鏈接。
下面就以Linux環(huán)境為例,來分析下四個步驟。
? 預處理
? 寫一段簡單的代碼:
#include其中,井號鍵開頭的代碼有兩行:包含頭文件和宏定義。 預處理命令:#define OK 0 int main() { printf("hellowrld "); return OK; }
root@Turbo:t# gcc -E test.c -o test.i root@Turbo:t# ls test test.c test.i root@Turbo:t#預處理后的test.i代碼:
# 1 "test.c" # 1 "代碼量從原來的10行變成了七百多行,主要的變化有兩個:" # 1 " " # 31 " " # 1 "/usr/include/stdc-predef.h" 1 3 4 # 32 " " 2 # 1 "test.c" # 1 "/usr/include/stdio.h" 1 3 4 # 27 "/usr/include/stdio.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 1 3 4 # 33 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 3 4 # 1 "/usr/include/features.h" 1 3 4 # 461 "/usr/include/features.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 1 3 4 # 452 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/wordsize.h" 1 3 4 # 453 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4 # 1 "/usr/include/x86_64-linux-gnu/bits/long-double.h" 1 3 4 # 454 "/usr/include/x86_64-linux-gnu/sys/cdefs.h" 2 3 4 # 462 "/usr/include/features.h" 2 3 4 ............... # 5 "test.c" int main() { printf("hellowrld "); return 0; }
?
頭文件沒了;
return OK 變成了 return 0。
? 所以基本上可以得出預處理的作用: 處理所有以井號鍵開頭的代碼,包括頭文件、宏定義、條件編譯等等。
? 頭文件展開。以stdio.h為例,編譯器會去默認的目錄下(一般是/usr/include)找到這個文件,然后把里面的內容復制一份,粘貼到C文件中。這就是為什么預處理后的文件變成了七百多行。
? 宏定義替換。預處理的時候如果遇到了宏定義,直接把宏替換掉,比如代碼中的OK就變成了數(shù)字0。
? 條件編譯。下面的代碼就屬于條件編譯:
?
#ifndef _STDIO_H #define _STDIO_H #endif條件編譯會在預處理的時候做出判斷,滿足條件的代碼留下來,不滿足的去掉。
? 編譯
? 編譯的命令:
root@Turbo:t# gcc -S test.i -o test.s root@Turbo:t# ls test test.c test.i test.s root@Turbo:t#查看編譯后的文件test.s內容:
.file "test.c"
.text
.section .rodata
.LC0:
.string "hellowrld"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rdi
call puts@PLT
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
如果你搞過單片機的話,一定能看出來這是匯編代碼。
?
編譯的作用:
語法檢查;
把C代碼翻譯成匯編代碼。
? 匯編 匯編的命令:
root@Turbo:t# gcc -c test.s -o test.o root@Turbo:t# ls test test.c test.i test.o test.s查看匯編后test.o的內容:
?
?

很顯然, 這是一個二進制文件。
? 既然是二進制文件,那能不能執(zhí)行呢?答案是不行,原因也很簡單,因為此時代碼還不知道printf函數(shù)在哪。
? 匯編的作用:
? 把匯編代碼翻譯成二進制代碼。
? 鏈接
? 鏈接的命令:
?
root@Turbo:t# gcc test.o -o test root@Turbo:t# ls test test.c test.i test.o test.s root@Turbo:t# ./test hellowrld如果工程里面有多個C文件,就會產(chǎn)生多個.o文件,鏈接的時候,把所有.o文件加上就行。
? 鏈接的作用:
?
把所有需要的源文件合并到一起;
鏈接代碼中需要用到的庫(比如printf,需要鏈接C庫)。
審核編輯:湯梓紅
電子發(fā)燒友App










評論