從C語言變成最終的可執(zhí)行文件,需要經(jīng)過四步:
預(yù)處理;
編譯;
匯編;
鏈接。
下面就以Linux環(huán)境為例,來分析下四個步驟。
? 預(yù)處理
? 寫一段簡單的代碼:
#include其中,井號鍵開頭的代碼有兩行:包含頭文件和宏定義。 預(yù)處理命令:#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#預(yù)處理后的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。
? 所以基本上可以得出預(yù)處理的作用: 處理所有以井號鍵開頭的代碼,包括頭文件、宏定義、條件編譯等等。
? 頭文件展開。以stdio.h為例,編譯器會去默認(rèn)的目錄下(一般是/usr/include)找到這個文件,然后把里面的內(nèi)容復(fù)制一份,粘貼到C文件中。這就是為什么預(yù)處理后的文件變成了七百多行。
? 宏定義替換。預(yù)處理的時候如果遇到了宏定義,直接把宏替換掉,比如代碼中的OK就變成了數(shù)字0。
? 條件編譯。下面的代碼就屬于條件編譯:
?
#ifndef _STDIO_H #define _STDIO_H #endif條件編譯會在預(yù)處理的時候做出判斷,滿足條件的代碼留下來,不滿足的去掉。
? 編譯
? 編譯的命令:
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內(nèi)容:
.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:如果你搞過單片機(jī)的話,一定能看出來這是匯編代碼。
?
編譯的作用:
語法檢查;
把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的內(nèi)容:
?
?
很顯然, 這是一個二進(jìn)制文件。
? 既然是二進(jìn)制文件,那能不能執(zhí)行呢?答案是不行,原因也很簡單,因為此時代碼還不知道printf函數(shù)在哪。
? 匯編的作用:
? 把匯編代碼翻譯成二進(jìn)制代碼。
? 鏈接
? 鏈接的命令:
?
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庫)。
審核編輯:湯梓紅
評論
查看更多