「C語言」C輸出hello world!系統發生了什麼?

    本篇文章所有摘抄自學長博客供之後學習:linux

        http://efraim.me/2015/12/05/tech-linux-2015-12-05/macos

    排版因與博客園編輯器不一樣而稍做修改。編程

 

輸出hello world!系統發生了什麼?網絡

 

經典的hello world!編輯器

1 #include <stdio.h>
2 
3 int main ()
4 {
5 printf("hello world!");
6 }

該段程序,在hello world過程當中,系統發生了什麼?函數

 

0X00 新建hello.c學習

hello.c文件,是文件由0/1的位(bit)序列,8位組成一組,稱爲字節,一個個字節表示文件中的一個個字符。ui

由此引出,系統中的全部信息(磁盤文件,系統程序,網絡傳輸的數據等等),都是一些0/1序列,當咱們打開hello.c文件時,系統會按某種規則文件進行解析,最後呈現出人類能看懂的字符,而不是二進制0/1。編碼

區分不一樣數據對象的惟一方法,就是系統讀取這個文件時上下文(能夠理解爲當時的環境),好比,在不一樣的上下文中,一個一樣的字節序列,有可能表示一個整數,浮點數,字符數或者機器指令。spa

 

0x01 解析hello.c

C語言是高級語言,因此這個形式的代碼能讓人讀懂。但系統不認識,爲了讓系統能認識,須要將hello.c轉化成一系機器能認識的語言指令,而後將這些指令按照一種稱爲可執行目標程序的格式進行打包,並以二進制磁盤文件形式存放,目標程序也能夠稱爲可執行文件。

這裏,我用GCC編譯器,編譯解析hello.c

編譯流程圖

 

 

  • 預處理階段:-E

預處理器(cpp)主要處理根據以字符#開頭的命令,修改原始的C程序。好比hello.c中第一行的#include <stdio.h> 命令會告訴預處理其讀區系統頭文件stdio.h的內容.並把它直接插入到程序文本中。此過程,會獲得以.i做爲擴展名。

`$ gcc -E hello.c -o hello.i`//預編譯hello.c文件 輸出hello.i文件

hello.i文件內容都有些什麼呢?

 1 //hello.i
 2 # 1 "hello.c"
 3 # 1 "<built-in>" 1
 4 # 1 "<built-in>" 3
 5 # 325 "<built-in>" 3
 6 # 1 "<command line>" 1
 7 # 1 "<built-in>" 2
 8 # 1 "hello.c" 2
 9 # 1 "/usr/include/stdio.h" 1 3 4
10 # 64 "/usr/include/stdio.h" 3 4
11 # 1 "/usr/include/sys/cdefs.h" 1 3 4
12 # 533 "/usr/include/sys/cdefs.h" 3 4
13 # 1 "/usr/include/sys/_symbol_aliasing.h" 1 3 4
14 # 534 "/usr/include/sys/cdefs.h" 2 3 4
15 # 599 "/usr/include/sys/cdefs.h" 3 4
16 # 1 "/usr/include/sys/_posix_availability.h" 1 3 4
17 # 600 "/usr/include/sys/cdefs.h" 2 3 4
18 # 65 "/usr/include/stdio.h" 2 3 4
19 # 1 "/usr/include/Availability.h" 1 3 4
20 # 162 "/usr/include/Availability.h" 3 4
21 # 1 "/usr/include/AvailabilityInternal.h" 1 3 4
22 # 163 "/usr/include/Availability.h" 2 3 4
23 # 66 "/usr/include/stdio.h" 2 3 4
24 # 1 "/usr/include/_types.h" 1 3 4
25 # 27 "/usr/include/_types.h" 3 4
26 # 1 "/usr/include/sys/_types.h" 1 3 4
27 # 33 "/usr/include/sys/_types.h" 3 4
28 # 1 "/usr/include/machine/_types.h" 1 3 4
29 # 32 "/usr/include/machine/_types.h" 3 4
30 # 1 "/usr/include/i386/_types.h" 1 3 4
31 # 37 "/usr/include/i386/_types.h" 3 4
32 typedef signed char __int8_t;
33 typedef unsigned char __uint8_t;
34 typedef short __int16_t;
35 typedef unsigned short __uint16_t;
36 typedef int __int32_t;
37 typedef unsigned int __uint32_t;
38 typedef long long __int64_t;
39 typedef unsigned long long __uint64_t;
40 typedef long __darwin_intptr_t;
41 typedef unsigned int __darwin_natural_t;
42 # 70 "/usr/include/i386/_types.h" 3 4
43 typedef int __darwin_ct_rune_t;

 

  • 編譯階段:-S

編譯器(ccl)將hello.i翻譯成文件文件hello.s文件,它包含一個彙編語言程序,彙編程序中的每條語句都以一種標準的文本格式確切地描述了一條條低級機器語言指令.因此該過程會檢查代碼規範,語法,詞法分析,具體以下圖.只有編譯成功以後,才能生成具體的彙編代碼。

編譯過程

 

$ gcc -S hello.i -o hello.s
 1 //hello.s
 2 .section    __TEXT,__text,regular,pure_instructions
 3 .macosx_version_min 10, 11
 4 .globl    _main
 5 .align    4, 0x90
 6 _main: ## @main
 7 .cfi_startproc
 8 ## BB#0:
 9 pushq    %rbp
10 Ltmp0:
11 .cfi_def_cfa_offset 16
12 Ltmp1:
13 .cfi_offset %rbp, -16
14 movq    %rsp, %rbp
15 Ltmp2:
16 .cfi_def_cfa_register %rbp
17 subq    $16, %rsp
18 leaq    L_.str(%rip), %rdi
19 movl    $0, -4(%rbp)
20 movb    $0, %al
21 callq    _printf
22 xorl    %ecx, %ecx
23 movl    %eax, -8(%rbp) ## 4-byte Spill
24 movl    %ecx, %eax
25 addq    $16, %rsp
26 popq    %rbp
27 retq
28 .cfi_endproc
29 
30 .section    __TEXT,__cstring,cstring_literals
31 L_.str: ## @.str
32 .asciz    "hello world!"
33 .subsections_via_symbols

 

  • 彙編階段:-c

彙編器(as)將hello.s 文件翻譯成機器語言指令,,把這些指令打包成一種叫作可重定向目標程序的格式,而且保存在hello.o文件中.該文件是一個二進制文件,他的字節編碼是機器語言指令而不是字符,若是用編輯器打開將是一段亂碼。

 

  • 連接階段:

注意,hello程序調用了printf函數,他是每一個C編譯器都會提供的標準C庫的一個函數.該函數存在於一個名爲printf.o的單獨的預編譯好的文件中,必須將該文件以某種方式合併到咱們的hello.o的文件中。鏈接器(ld),就複製處理這種合併。結果就獲得一個hello文件,是一個可執行文件。

相關文章
相關標籤/搜索