C語言真正的編譯過程

說實話,不少人作了好久的C/C++,也用了不少IDE,可是對於可執行程序的底層生成一片茫然,這無疑是一種悲哀,能夠想象到大公司面試正好被問到這樣的問題,有多悲催不言而喻,這裏正因爲換工做的緣故,因此打算系統的把以前用到的C/C++補一補。這裏權且當作拋磚引玉,大神飄過。面試

【總述】app

從一個源文件(.c)到可執行程序到底經歷了哪幾步,我想大多數的人都知道,到時到底每一步都作了什麼,我估計也沒多少人可以說得清清楚楚,明明白白。ide

其實總的流程是這樣的。函數

【第一步】編輯hello.c優化

#include <stdio.h>
#include <stdlib.h>
int main()
{
         printf("hello world!\n");
         return 0;
 }

【第二步】預處理翻譯

預處理過程實質上是處理「#」,將#include包含的頭文件直接拷貝到hell.c當中;將#define定義的宏進行替換,同時將代碼中沒用的註釋部分刪除等3d

具體作的事兒以下:調試

(1)將全部的#define刪除,而且展開全部的宏定義。說白了就是字符替換code

(2)處理全部的條件編譯指令,#ifdef #ifndef #endif等,就是帶#的那些blog

(3)處理#include,將#include指向的文件插入到該行處

(4)刪除全部註釋

(5)添加行號和文件標示,這樣的在調試和編譯出錯的時候才知道是是哪一個文件的哪一行

(6)保留#pragma編譯器指令,由於編譯器須要使用它們。

 

gcc -E hello.c -o a.c能夠生成預處理後的文件。經過查看文件內容和文件大小能夠得知a.c講stdio.h和stdlib.h包含了進來。

【第三步】編譯

編譯的過程實質上是把高級語言翻譯成機器語言的過程,即對a.c作了這些事兒

(1)詞法分析,

(2)語法分析

(3)語義分析

(4)優化後生成相應的彙編代碼

從 高級語言->彙編語言->機器語言(二進制)

gcc -S hello.c -o a.s能夠生成彙編代碼

彙編代碼以下

.file	"hello.c"
	.section	.rodata
.LC0:
	.string	"hello world!"
	.text
	.globl	main
	.type	main, @function
main:
.LFB2:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$.LC0, %edi
	call	puts
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE2:
	.size	main, .-main
	.ident	"GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-36)"
	.section	.note.GNU-stack,"",@progbits

 

gcc -c hello.c -o a.o將源文件翻譯成二進制文件。類Uinx系統編譯的結果生生成.o文件,Windows系統是生成.obj文件。

編譯的過程就是把hello.c翻譯成二進制文件

【第四步】連接

就像剛纔的hello.c它使用到了C標準庫的東西「printf」,可是編譯過程只是把源文件翻譯成二進制而已,這個二進制還不能直接執行,這個時候就須要作一個動做,

將翻譯成的二進制與須要用到庫綁定在一塊。打個比方編譯的過程就向你對你老婆說,我要吃雪糕。你只是給你老婆發出了你要吃雪糕的訴求而已,可是雪糕尚未到。

綁定就是說你要吃的雪糕你的老婆已經給你買了,你能夠happy。

gcc hello.c -o a能夠生成可執行程序。即gcc不帶任何參數。ldd就能夠看到你的可執行程序依賴的庫。

能夠看到a.o的大小是1.1k,畢竟他只是把源文件翻譯成二進制文件。a卻有7k,應該是他多了不少「繩子」吧。在運行的時候這些「繩子」就將對應的庫函數「牽過來」。很形象的比喻是否是?哈哈。libc.so.6 中就對我們用的printf進行了定義。

這就是編譯的整個流程

相關文章
相關標籤/搜索