GCC 使用-C語言編譯過程

任何一種高級語言,要想在機器上執行,必須翻譯爲機器能讀懂的機器語言。編譯器就至關於翻譯官,將高級語言翻譯爲機器語言。java

GCC 最初只用了編譯 C 語言程序,全稱是 GNU C Compiler。後來擴展爲能夠編譯多種語言,因此又稱爲 GNU Compiler Collection。GCC 是開源免費的編譯器。linux

GCC 常見用法

GCC 是一組工具的集合,這些工具能夠執行預處理、編譯、彙編、連接等任務。GCC 經過文件後綴名來識別文件並調用合適的工具,例如對於 .c 文件會調用 C 編譯器,對於 .cpp 文件會調用 C++ 編譯器。c++

  • 不帶任何參數的編譯,會獲得默認的 a.out 文件,經過 ./a.out 能夠執行:
# gcc 1.c
# ./a.out
  • 能夠經過 -o 輸出文件名 指定生成的可執行文件名稱:
# gcc -o build 1.c
# ./build
  • 能夠經過 --save-temps 選項保留編譯過程當中產生的全部中間文件:
    各個文件的產生順序爲:.c 源文件 -> .i 預處理文件 -> .s 彙編文件 -> .o 目標對象文件 -> a.out 可執行文件。能夠經過不一樣的選項只生成指定類型的文件。
[root@VM_139_38_centos define]# ls
main.c
[root@VM_139_38_centos define]# gcc --save-temps main.c 
[root@VM_139_38_centos define]# ll
total 44
-rwxr-xr-x 1 root root  8568 Jan 19 15:32 a.out
-rw-r--r-- 1 root root   325 Jan 19 14:39 main.c
-rw-r--r-- 1 root root 16901 Jan 19 15:32 main.i
-rw-r--r-- 1 root root  1600 Jan 19 15:32 main.o
-rw-r--r-- 1 root root   561 Jan 19 15:32 main.s
  • 能夠經過 -v 選項輸出編譯過程當中產生的全部信息:
# gcc -v -o build 1.c
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,java,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.8.5-20150702/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=x86-64 --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.8.5 20150623 (Red Hat 4.8.5-28) (GCC) 
COLLECT_GCC_OPTIONS='-v' '-o' 'build' '-mtune=generic' '-march=x86-64'
 /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/cc1 -quiet -v 1.c -quiet -dumpbase 1.c -mtune=generic -march=x86-64 -auxbase 1 -version -o /tmp/ccgJDXWv.s
GNU C (GCC) version 4.8.5 20150623 (Red Hat 4.8.5-28) (x86_64-redhat-linux)
	compiled by GNU C version 4.8.5 20150623 (Red Hat 4.8.5-28), GMP version 6.0.0, MPFR version 3.1.1, MPC version 1.0.1
GGC heuristics: --param ggc-min-expand=97 --param ggc-min-heapsize=127047
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/include-fixed"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../x86_64-redhat-linux/include"
#include "..." search starts here:
#include <...> search starts here:
 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include
 /usr/local/include
 /usr/include
End of search list.
GNU C (GCC) version 4.8.5 20150623 (Red Hat 4.8.5-28) (x86_64-redhat-linux)
	compiled by GNU C version 4.8.5 20150623 (Red Hat 4.8.5-28), GMP version 6.0.0, MPFR version 3.1.1, MPC version 1.0.1
GGC heuristics: --param ggc-min-expand=97 --param ggc-min-heapsize=127047
Compiler executable checksum: fbe9869a2e70aadeaf82d7c32bbeabe0
COLLECT_GCC_OPTIONS='-v' '-o' 'build' '-mtune=generic' '-march=x86-64'
 as -v --64 -o /tmp/ccmzAIx7.o /tmp/ccgJDXWv.s
GNU assembler version 2.25.1 (x86_64-redhat-linux) using BFD version version 2.25.1-22.base.el7 
COMPILER_PATH=/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/:/usr/libexec/gcc/x86_64-redhat-linux/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/
LIBRARY_PATH=/usr/lib/gcc/x86_64-redhat-linux/4.8.5/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/:/lib/../lib64/:/usr/lib/../lib64/:/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-o' 'build' '-mtune=generic' '-march=x86-64'
 /usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o build /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. /tmp/ccmzAIx7.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o

C 語言編譯過程

預處理

預處理時,會把全部的 include 替換爲所引入文件的代碼,把 define 宏定義都替換爲具體的定義元素。對 C 程序預處理後,獲得 .i 文件。對應 GCC 的選項是 gcc -E,例如web

gcc -E -o my.i 1.c

命令能夠獲得 my.i 這個文件。編程

編譯

對 C 程序編譯後,獲得彙編程序。對應 GCC 的選項是 gcc -S,例如bootstrap

gcc -S -o my.s 1.c

命令能夠獲得 my.s 這個彙編文件。這個步驟對應上面的輸出信息爲:centos

/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/cc1 -quiet -v 1.c -o /tmp/ccgJDXWv.s -quiet -dumpbase 1.c -mtune=generic -march=x86-64 -auxbase 1 -version

彙編

將上一步編譯獲得的彙編程序,轉爲 .o 輸出文件,這是能夠被計算機直接讀懂的二進制文件。可是須要注意,由於沒有連接進額外的資源,一般這個文件是不能直接執行的。對應 GCC 的選項是 gcc -c,這個選項會在彙編前自動調用編譯工具。例如bash

gcc -c -o my.out my.s

命令能夠獲得 my.out 這個輸出文件,此時還須要連接,還不能直接執行。app

連接

連接,能夠將多個 .o 輸出文件組成一個可執行文件,能夠自動連接所需的依賴。例如svg

gcc -o myExe my.out

對應 GCC 的選項是 gcc -o,若是指定的輸入是 .c 或 .s 後綴的文件,這個選項在連接前會自動調用編譯或/和彙編兩個工具。

一般使用 GCC 時,直接調用 gcc -o myOutName xx.c yy.c zz.c 便可自動完成上述全部步驟,獲得一個可執行文件。

C 語言編譯時的常見錯誤

預處理錯誤

C 語言有兩種方式引入頭文件:

  • 雙引號 「」:例如 #include "my.h",首先在當前目錄下尋找,找不到的話再去系統庫中尋找。用於本身實現的頭文件。
  • 尖括號 <>:例如 #include <stdio.h>,直接在系統庫中尋找。

項目中一般把頭文件放在單獨的目錄中,例如項目根目錄下的 inc 目錄。這時可用在使用 GCC 時經過 -I 頭文件位置 來指定要查找頭文件的目錄。

[root@VM_139_38_centos header]# gcc main.c 
main.c:2:16: fatal error: my.h: No such file or directory
 #include "my.h"
                ^
compilation terminated.
[root@VM_139_38_centos header]# gcc -I inc main.c 
[root@VM_139_38_centos header]# ./a.out 
Hello World! 123

編譯錯誤

編譯錯誤主要是語法錯誤。例如括號不配對,變量名寫錯。

連接錯誤

連接時缺乏所需的文件

連接時發現缺乏所需的文件,例如函數定義缺失,會報連接錯誤。例如對下面的代碼:

#include <stdio.h>

void fun();

int main()
{
	fun();
	return 0;
}

編譯時會有以下報錯,其中 collect2 命令是連接時用的程序:

/tmp/cc2lEZt4.o: In function `main':
main.c:(.text+0x15): undefined reference to `fun'
collect2: error: ld returned 1 exit status

解決方法也很簡單,在同一個文件中實現這個方法後,從新編譯便可。若是方法的實如今另外一個文件中,能夠在用 GCC 編譯時指定這個文件便可,例如方法聲明在 1.c 中,而方法體在 2.c 這個文件中,則用下面的命令編譯便可:

gcc -o my.out 1.c 2.c

固然,一般每一個文件都是單獨編譯的,最後連接到一塊兒:

gcc -c -o 1.o 1.c
gcc -c -o 2.o 2.c
gcc -o my.out 1.o 2.o

連接時多了所需的文件

例如,一個方法在多個文件中定義,而連接時須要把這幾個文件整合爲一個可執行文件,就會報錯:

fun.o: In function `fun':
fun.c:(.text+0x0): multiple definition of `fun'
main.o:main.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
相關文章
相關標籤/搜索