日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【软件开发底层知识修炼】五 gcc-C语言编译器

發布時間:2023/12/10 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【软件开发底层知识修炼】五 gcc-C语言编译器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

學習交流加

  • 個人qq:
    1126137994
  • 個人微信:
    liu1126137994
  • 學習交流資源分享qq群:
    962535112

文章目錄

  • 1、GCC與gcc
  • 2、gcc的幕后工作
  • 3、實用的gcc選項
    • 3.1、預處理選項-解決宏錯誤
    • 3.2、-S參數-輔助編寫匯編程序的好方法
    • 3.3、獲取系統頭文件路徑
    • 3.4、產生映射文件
    • 3.5、通過選項定義宏
    • 3.6、生成依賴關系
    • 3.7、指定鏈接庫
  • 4、總結

前面的四篇文章終于把處理器系列學完了(點擊查看上一篇文章:高速緩存與TLB)。收獲很大!!! 接下來就該學習底層軟件部分知識。今天學習gcc的基本概念與簡單用法。

1、GCC與gcc

  • GCC (GNU Compiler Collection)

    • GNU 編譯器集合,包含眾多語言的編譯器,包括C,C++,Java等等
    • GCC多用于嵌入式操作系統的編譯,如Linux,VxWorks,Android等等
  • gcc 單指GCC中的C語言編譯器

    • gcc 多用于內核開發以及少數應用程序開發

2、gcc的幕后工作

想了解更多更詳細的關于編譯鏈接深層次內容,請閱讀書籍《CSAPP》第7章與《程序員的自我修養》,因為這里我的學習記錄只記錄結果與常用的幾個編譯方法。

我們先來看一個簡單的程序:

test.c源程序:

#include <stdio.h> #include "func.h"int g_global = 0; int g_test = 1;int main(int argc, char *argv[]) {func();printf("&g_global = %p\n", &g_global);printf("&g_test = %p\n", &g_test);printf("&func = %p\n", &func);printf("&main = %p\n", &main);return 0; }

func.h頭文件:

#include <stdio.h>void func() { #ifdef TESTprintf("TEST = %s\n", TEST); #endifreturn; }

在Linux下使用gcc進行編譯:

gcc test.c -o test

然后運行:

./test

結果如下:

&g_global = 0x804a020 &g_test = 0x804a014 &func = 0x80483c4 &main = 0x80483c9

很明顯,上述程序很簡單,大一的新生都知道為什么。但是今天我們不是學習這個程序的,而是想要了解,運行 gcc test.c -o test 這個命令后,是如何一步一步生成可執行文件test的。

實際上,上述C程序從源文件到二進制可執行文件,有以下四個步驟:

  • 預處理 cpp
  • C編譯 cc
  • 匯編 as
  • 鏈接 ld
  • 大概編譯一個源程序為二進制文件的過程如下圖所示:

    當然,上面沒有列出鏈接器,在生成file.o后,還需要將file.o與系統的庫文件進行鏈接,生成最終的可執行文件。

    從而,我們就知道了,gcc其實內部包含了預處理器,編譯器,匯編器,鏈接器這四部分。

    這四部分這里只是來簡單介紹一下(網上一大堆,本文側重點不在此):

    • 預處理器:預處理,將源程序的宏定義與帶‘#’的部分展開
    • 編譯器:將預處理后得到的文件進行第一次編譯得到對應的匯編源程序
    • 匯編器:將第二步得到的匯編源程序進行第二次編譯即匯編,得到二進制文件(可重定位文件)
    • 鏈接器:將可重定位文件與相應的庫進行鏈接生成最終的可執行文件

    3、實用的gcc選項

    本文的重點來了,上述的內容過于簡單,而本節的內容雖然不難,但是并不被大多數人所了解,所以是本文的重點學習記錄。

    下面將要學習的gcc選項,在工作中具有很強的實用性。

    3.1、預處理選項-解決宏錯誤

    gcc -E file.c -o file.i

    實用上述編譯選項 -E 可以得到預處理后的文件,有時候我們在程序中定義的宏可能有錯誤,而這種錯誤又很難找,此時如果能得預處理后的文件,就可以方便定位錯誤。

    3.2、-S參數-輔助編寫匯編程序的好方法

    寫匯編程序很難,但是如果先寫成C語言,再將這個C語言轉化成匯編語言,就會很簡單。gcc編譯工具中,-S選項,可以達到這個目的。比如以下程序:
    foo.c程序:

    #include <stdio.h>void foo(){printf("This is foo().\n"); }

    我們使用如下命令進行編譯:

    gcc -S -O2 foo.c -o foo.s

    將會生成一個foo.c相同作用的匯編程序foo.s,如下:

    .file "foo.c".section .rodata.str1.1,"aMS",@progbits,1 .LC0:.string "This is foo().\n".text.p2align 4,,15 .globl foo.type foo, @function foo:pushl %ebpmovl %esp, %ebpsubl $24, %espmovl $.LC0, 4(%esp)movl $1, (%esp)call __printf_chkleaveret.size foo, .-foo.ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5.1) 4.4.5".section .note.GNU-stack,"",@progbits

    使用-S 參數時,我們可以根據需要使用-O優化選項。從foo.s的內容可以看出,“This is foo().\n” 這個字符串是放在.rodata段的。看來獲取C程序對應的匯編代碼,對C語言實現方面的細節,也有所幫助。

    3.3、獲取系統頭文件路徑

    gcc -v file.c

    獲取file.c使用的系統頭文件的位置

    3.4、產生映射文件

    如果我們想要知道程序中各個符號的內存布局的信息,可以使用如下命令:

    gcc -Wl,-Map=file.map file.c -o file

    3.5、通過選項定義宏

    有時候程序中需要的某一個常量會依賴工作環境的不同而改變,這個時候,我們可以將這個常量定義為宏,但是這樣,我們還是需要每次都在源程序中將宏的值改變,這也很麻煩,此時就可以利用編譯選項 -D,在編譯的命令行進行宏定義。

    還有就是程序中或許會存在下屬這樣的代碼:
    test.c程序:

    #include <stdio.h> #include "func.h"int g_global = 0; int g_test = 1;int main(int argc, char *argv[]) {func();printf("&g_global = %p\n", &g_global);printf("&g_test = %p\n", &g_test);printf("&func = %p\n", &func);printf("&main = %p\n", &main);return 0; }

    test.h頭文件:

    #include <stdio.h>void func() { #ifdef TESTprintf("TEST = %s\n", TEST); #endifreturn; }

    在頭文件中,有一處定義 # ifdef TEST …

    很明顯,上面的兩個文件,都沒有定義這個TEST,所以程序運行結果如下:

    &g_global = 0x804a020&g_test = 0x804a014&func = 0x80483c4&main = 0x80483c9

    但是可能在某個場合,又必須要使用TEST定義,那么此時,我們肯定不愿意在程序中改來改去,此時就利用編譯器的 -D選項,來定義這個TEST。如下編譯命令:

    gcc -D'TEST="test" ' test.c -o test

    運行程序后,結果如下:

    TEST = test &g_global = 0x804a020 &g_test = 0x804a014 &func = 0x80483c4 &main = 0x80483e1

    3.6、生成依賴關系

    大多數人應該知道make,如果不知道也沒有關系。
    在makefile中,make需要通過依賴關系來決定,每次構建時哪些文件需要重新編譯。使用gcc的-M選項,可以得到make所需要的源文件的依賴關系。-MM選項可以讓gcc生成不包含系統文件的依賴關系。

    比如有如下源文件:
    main.c源文件(main.h與foo.c的內容是什么都行)

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

    對其進行如下編譯

    gcc -M main.c

    將得到如下輸出:

    可以看到,這句是make所需要的main.c的依賴關系。

    如果使用如下命令的話:

    gcc -MM main.c

    將得到如下輸出:

    結果顯而易見!!!

    3.7、指定鏈接庫

    當一個可執行程序的生成,需要使用其他庫時,需要在鏈接時加以指定。這就需要用到gcc 的-l與-L選項。

    假設一個程序叫做main.c,它編譯成可執行程序不光需要系統的標準庫,還需要一個庫:libfoo.a 且這個libfoo.a與main.c在同一個目錄,那么在編譯main.c時,需要以下命令:

    gcc -o main -L. main.c -lfoo

    注意:

    • -L告訴gcc編譯器,當前可以從哪個目錄查找庫文件,此處-L后面跟了一個**點‘.’**表示當前目錄。
    • -l選項,告訴編譯器需要連接的庫名。這里并沒有寫“lib”前綴和“.a后綴”。-lfoo就是代表指定libfoo.a庫參與鏈接。

    更加詳細的內容參考《程序員的自我修養》

    4、總結

    今天學習了gcc的簡單概念,與gcc的常用的參數選項。

    本文章參考狄泰軟件學院相關課程與《專業嵌入式軟件》第4章的內容內容
    想學習的可以加狄泰軟件學院群,
    群聊號碼:199546072

    學習探討加個人:
    qq:1126137994
    微信:liu1126137994

    總結

    以上是生活随笔為你收集整理的【软件开发底层知识修炼】五 gcc-C语言编译器的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。