www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > 嵌入式 > 嵌入式教程
[導讀]GNU CC是GNU項目中符合ANSI C標準的編譯系統,能夠編譯用C、C++和Object C等語言編寫的程序。gcc不僅功能強大,而且可以編譯如C、C++、Object C、Java、Fortran、Pascal、Modula-3和Ada等多種語言,而且gcc是一個交叉平臺編譯器。

3.3gcc編譯器

GNUCC(簡稱為gcc)是GNU項目中符合ANSIC標準的編譯系統,能夠編譯用C、C++和ObjectC等語言編寫的程序。gcc不僅功能強大,而且可以編譯如C、C++、ObjectC、Java、Fortran、Pascal、Modula-3和Ada等多種語言,而且gcc是一個交叉平臺編譯器,它能夠在當前CPU平臺上為多種不同體系結構的硬件平臺開發(fā)軟件,因此尤其適合在嵌入式領域的開發(fā)編譯。本章中的示例,除非特別注明,否則均采用4.x.x的gcc版本。

表3.6所示為gcc支持編譯源文件的后綴及其解釋。

表3.6 gcc所支持后綴名解釋

后綴名

所對應的語言

后綴名

所對應的語言

.c

C原始程序

.s/.S

匯編語言原始程序

.C/.cc/.cxx

C++原始程序

.h

預處理文件(頭文件)

.m

Objective-C原始程序

.o

目標文件

.i

已經過預處理的C原始程序

.a/.so

編譯后的庫文件

.ii

已經過預處理的C++原始程序

3.3.1gcc編譯流程解析

如本章開頭提到的,gcc的編譯流程分為了4個步驟,分別為:

n 預處理(Pre-Processing);

n 編譯(Compiling);

n 匯編(Assembling);

n 鏈接(Linking)。

下面就具體來查看一下gcc是如何完成以上4個步驟的。

首先看一下hello.c的源代碼:

#include<stdio.h>

intmain()

{

printf("Hello!Thisisourembeddedworld!\n");

return0;

}

(1)預處理階段。

在該階段,對包含的頭文件(#include)和宏定義(#define、#ifdef等)進行處理。在上述代碼的預處理過程中,編譯器將包含的頭文件stdio.h編譯進來,并且用戶可以使用gcc的選項“-E”進行查看,該選項的作用是讓gcc在預處理結束后停止編譯過程。

注意

gcc指令的一般格式為:gcc[選項]要編譯的文件[選項][目標文件]

其中,目標文件可缺省,gcc默認生成可執(zhí)行的文件,名為:編譯文件.out

[root@localhostgcc]#gcc–Ehello.c–ohello.i

在此處,選項“-o”是指目標文件,由表3.6可知,“.i”文件為已經過預處理的C程序。以下列出了hello.i文件的部分內容:

typedefint(*__gconv_trans_fct)(struct__gconv_step*,

struct__gconv_step_data*,void*,

__constunsignedchar*,

__constunsignedchar**,

__constunsignedchar*,unsignedchar**,

size_t*);

#2"hello.c"2

intmain()

{

printf("Hello!Thisisourembeddedworld!\n");

return0;

}

由此可見,gcc確實進行了預處理,它把“stdio.h”的內容插入hello.i文件中。

(2)編譯階段。

接下來進行的是編譯階段,在這個階段中,gcc首先要檢查代碼的規(guī)范性、是否有語法錯誤等,以確定代碼實際要做的工作,在檢查無誤后,gcc把代碼翻譯成匯編語言。用戶可以使用“-S”選項來進行查看,該選項只進行編譯而不進行匯編,結果生成匯編代碼。

[root@localhostgcc]#gcc–Shello.i–ohello.s

以下列出了hello.s的內容,可見gcc已經將其轉化為匯編代碼了,感興趣的讀者可以分析一下這一個簡單的C語言小程序是如何用匯編代碼實現的。

.file"hello.c"

.section.rodata

.align4

.LC0:

.string"Hello!Thisisourembeddedworld!"

.text

.globlmain

.typemain,@function

main:

pushl%ebp

movl%esp,%ebp

subl$8,%esp

andl$-16,%esp

movl$0,%eax

addl$15,%eax

addl$15,%eax

shrl$4,%eax

sall$4,%eax

subl%eax,%esp

subl$12,%esp

pushl$.LC0

callputs

addl$16,%esp

movl$0,%eax

leave

ret

.sizemain,.-main

.ident"GCC:(GNU)4.0.0200XYZ19(RedHat4.0.0-8)"

.section.note.GNU-stack,"",@progbits

(3)匯編階段。

匯編階段是把編譯階段生成的“.s”文件轉成目標文件,讀者在此使用選項“-c”就可看到匯編代碼已轉化為“.o”的二進制目標代碼了,如下所示:

[root@localhostgcc]#gcc–chello.s–ohello.o

(4)鏈接階段。

在成功編譯之后,就進入了鏈接階段。這里涉及一個重要的概念:函數庫。

讀者可以重新查看這個小程序,在這個程序中并沒有定義“printf”的函數實現,且在預編譯中包含進的“stdio.h”中也只有該函數的聲明,而沒有定義函數的實現,那么,是在哪里實現“printf”函數的呢?最后的答案是:系統把這些函數的實現都放到名為libc.so.6的庫文件中去了,在沒有特別指定時,gcc會到系統默認的搜索路徑“/usr/lib”下進行查找,也就是鏈接到libc.so.6函數庫中去,這樣就能調用函數“printf”了,而這也正是鏈接的作用。

函數庫有靜態(tài)庫和動態(tài)庫兩種。靜態(tài)庫是指編譯鏈接時,將庫文件的代碼全部加入可執(zhí)行文件中,因此生成的文件比較大,但在運行時也就不再需要庫文件了。其后綴名通常為“.a”。動態(tài)庫與之相反,在編譯鏈接時并沒有將庫文件的代碼加入可執(zhí)行文件中,而是在程序執(zhí)行時加載庫,這樣可以節(jié)省系統的開銷。一般動態(tài)庫的后綴名為“.so”,如前面所述的libc.so.6就是動態(tài)庫。gcc在編譯時默認使用動態(tài)庫。

完成了鏈接之后,gcc就可以生成可執(zhí)行文件,如下所示。

[root@localhostgcc]#gcchello.o–ohello

運行該可執(zhí)行文件,出現的正確結果如下。

[root@localhostgcc]#./hello

Hello!Thisisourembeddedworld!

3.3.2gcc編譯選項分析

gcc有超過100個可用選項,主要包括總體選項、告警和出錯選項、優(yōu)化選項和體系結構相關選項。以下對每一類中最常用的選項進行講解。

(1)常用選項。

gcc的常用選項如表3.7所示,很多在前面的示例中已經有所涉及。

表3.7 gcc常用選項列表

選項

含義

-c

只編譯不鏈接,生成目標文件“.o”

-S

只編譯不匯編,生成匯編代碼

-E

只進行預編譯,不做其他處理

-g

在可執(zhí)行程序中包含標準調試信息

-ofile

將file文件指定為輸出文件

-v

打印出編譯器內部編譯各過程的命令行信息和編譯器的版本

-Idir

在頭文件的搜索路徑列表中添加dir目錄

前一小節(jié)已經講解了“-c”、“-E”、“-o”、“-S”選項的使用方法,在此主要講解另外2個非常常用的庫依賴選項“-Idir”。

n “-Idir”

正如上表中所述,“-Idir”選項可以在頭文件的搜索路徑列表中添加dir目錄。由于Linux中頭文件都默認放到了“/usr/include/”目錄下,因此,當用戶希望添加放置在其他位置的頭文件時,就可以通過“-Idir”選項來指定,這樣,gcc就會到相應的位置查找對應的目錄。

比如在“/root/workplace/gcc”下有兩個文件:

/*hello1.c*/

#include<my.h>

intmain()

{

printf("Hello!!\n");

return0;

}

/*my.h*/

#include<stdio.h>

這樣,就可在gcc命令行中加入“-I”選項:

[root@localhostgcc]gcchello1.c–I/root/workplace/gcc/-ohello1

這樣,gcc就能夠執(zhí)行出正確結果。

小知識

在include語句中,“<>”表示在標準路徑中搜索頭文件,““””表示在本目錄中搜索。故在上例中,可把hello1.c的“#include<my.h>”改為“#include“my.h””,就不需要加上“-I”選項了。

(2)庫選項。

gcc庫選項如表3.8所示。

表3.8 gcc庫選項列表

選項

含義

-static

進行靜態(tài)編譯,即鏈接靜態(tài)庫,禁止使用動態(tài)庫

-shared

1.可以生成動態(tài)庫文件

2.進行動態(tài)編譯,盡可能地鏈接動態(tài)庫,只有當沒有動態(tài)庫時才會鏈接同名的靜態(tài)庫(默認選項,即可省略)

-Ldir

在庫文件的搜索路徑列表中添加dir目錄

-lname

鏈接稱為libname.a(靜態(tài)庫)或者libname.so(動態(tài)庫)的庫文件。若兩個庫都存在,則根據編譯方式(-static還是-shared)而進行鏈接

-fPIC(或-fpic)

生成使用相對地址的位置無關的目標代碼(PositionIndependentCode)。然后通常使用gcc的-static選項從該PIC目標文件生成動態(tài)庫文件

我們通常需要將一些常用的公共函數編譯并集成到二進制文件(Linux的ELF格式文件),以便其他程序可重復地使用該文件中的函數,此時將這種文件叫做函數庫,使用函數庫不僅能夠節(jié)省很多內存和存儲器的空間資源,而且更重要的是大大降低開發(fā)難度和開銷,提高開發(fā)效率并增強程序的結構性。實際上,在Linux中的每個程序都會鏈接到一個或者多個庫。比如使用C函數的程序會鏈接到C運行時庫,Qt應用程序會鏈接到Qt支持的相關圖形庫等。

函數庫有靜態(tài)庫和動態(tài)庫兩種,靜態(tài)庫是一系列的目標文件(.o文件)的歸檔文件(文件名格式為libname.a),如果在編譯某個程序時鏈接靜態(tài)庫,則鏈接器將會搜索靜態(tài)庫,從中提取出它所需要的目標文件并直接復制到該程序的可執(zhí)行二進制文件(ELF格式文件)之中;動態(tài)庫(文件名格式為libname.so[.主版本號.次版本號.發(fā)行號])在程序編譯時并不會被鏈接到目標代碼中,而是在程序運行時才被載入。

下面舉一個簡單的例子,講解如何怎么創(chuàng)建和使用這兩種函數庫。

首先創(chuàng)建unsgn_pow.c文件,它包含unsgn_pow()函數的定義,具體代碼如下所示。

/*unsgn_pow.c:庫程序*/

unsignedlonglongunsgn_pow(unsignedintx,unsignedinty)

{

unsignedlonglongres=1;

if(y==0)

{

res=1;

}

elseif(y==1)

{

res=x;

}

else

{

res=x*unsgn_pow(x,y-1);

}

returnres;

}

然后創(chuàng)建pow_test.c文件,它會調用unsgn_pow()函數。

/*pow_test.c*/

#include<stdio.h>

#include<stdlib.h>

intmain(intargc,char*argv[])

{

unsignedintx,y;

unsignedlonglongres;

if((argc<3)||(sscanf(argv[1],"%u",&x)!=1)

||(sscanf(argv[2],"%u",&y))!=1)

{

printf("Usage:powbaseexponent\n");

exit(1);

}

res=unsgn_pow(x,y);

printf("%u^%u=%u\n",x,y,res);

exit(0);

}

我們用unsgn_pow.c文件可以制作一個函數庫。下面分別講解怎么生成靜態(tài)庫和動態(tài)庫。

n 靜態(tài)庫的創(chuàng)建和使用。

創(chuàng)建靜態(tài)庫比較簡單,使用歸檔工具ar將一些目標文件集成在一起。

[root@localhostlib]#gcc-cunsgn_pow.c

[root@localhostlib]#arrcsvlibpow.aunsgn_pow.o

a-unsgn_pow.o

下面編譯主程序,它將會鏈接到剛生成的靜態(tài)庫libpow.a。具體運行結果如下所示。

[root@localhostlib]#gcc-opow_testpow_test.c-L.–lpow

[root@localhostlib]#./pow_test210

2^10=1024

其中,選項“-Ldir”的功能與“-Idir”類似,能夠在庫文件的搜索路徑列表中添加dir目錄,而“-lname”選項指示編譯時鏈接到庫文件libname.a或者libname.so。本實例中,程序pow_test.c需要使用當前目錄下的一個靜態(tài)庫libpow.a。

n 動態(tài)庫的創(chuàng)建和使用。

首先使用gcc的-fPIC選項為動態(tài)庫構造一個目標文件

[root@localhostlib]#gcc-fPIC-Wall-cunsgn_pow.c

接下來,使用-shared選項和已創(chuàng)建的位置無關目標代碼,生成一個動態(tài)庫libpow.so。

[root@localhostlib]#gcc-shared-olibpow.sounsgn_pow.o

下面編譯主程序,它將會鏈接到剛生成的動態(tài)庫libpow.so。

[root@localhostlib]#gcc-opow_testpow_test.c-L.–lpow

在運行可執(zhí)行程序之前,需要注冊動態(tài)庫的路徑名。其方法有幾種:修改/etc/ld.so.conf文件,或者修改LD_LIBRARY_PATH環(huán)境變量,或者將庫文件直接復制到/lib或者/usr/lib目錄下(這兩個目錄為系統的默認的庫路徑名)。

[root@localhostlib]#cplibpow.so/lib

[root@localhostlib]#./pow_test210

2^10=1024

動態(tài)庫只有當使用它的程序執(zhí)行時才被鏈接使用,而不是將需要的部分直接編譯入可執(zhí)行文件中,并且一個動態(tài)庫可以被多個程序使用故可稱為共享庫,而靜態(tài)庫將會整合到程序中,因此在程序執(zhí)行時不用加載靜態(tài)庫。從而可知,鏈接到靜態(tài)庫會使用戶的程序臃腫,并且難以升級,但是可能會比較容易部署。而鏈接到動態(tài)庫會使用戶的程序輕便,并且易于升級,但是會難以部署。

(3)告警和出錯選項。

gcc的告警和出錯選項如表3.9所示。

表3.9 gcc警告和出錯選項選項列表

選項

含義

-ansi

支持符合ANSI標準的C程序

-pedantic

允許發(fā)出ANSIC標準所列的全部警告信息

-pedantic-error

允許發(fā)出ANSIC標準所列的全部錯誤信息

-w

關閉所有告警

-Wall

允許發(fā)出gcc提供的所有有用的報警信息

-werror

把所有的告警信息轉化為錯誤信息,并在告警發(fā)生時終止編譯過程

下面結合實例對這幾個告警和出錯選項進行簡單的講解。

有以下程序段:

#include<stdio.h>

voidmain()

{

longlongtmp=1;

printf("Thisisabadcode!\n");

return0;

}

這是一個很糟糕的程序,讀者可以考慮一下有哪些問題。

n “-ansi”

該選項強制gcc生成標準語法所要求的告警信息,盡管這還并不能保證所有沒有警告的程序都是符合ANSIC標準的。運行結果如下所示:

[root@localhostgcc]#gcc–ansiwarning.c–owarning

warning.c:在函數“main”中:

warning.c:7警告:在無返回值的函數中,“return”帶返回值

warning.c:4警告:“main”的返回類型不是“int”

可以看出,該選項并沒有發(fā)現“longlong”這個無效數據類型的錯誤。

n “-pedantic”

打印ANSIC標準所列出的全部警告信息,同樣也保證所有沒有警告的程序都是符合ANSIC標準的。其運行結果如下所示:

[root@localhostgcc]#gcc–pedanticwarning.c–owarning

warning.c:在函數“main”中:

warning.c:5警告:ISOC90不支持“longlong”

warning.c:7警告:在無返回值的函數中,“return”帶返回值

warning.c:4警告:“main”的返回類型不是“int”

可以看出,使用該選項查出了“longlong”這個無效數據類型的錯誤。

n “-Wall”

打印gcc能夠提供的所有有用的報警信息。該選項的運行結果如下所示:

[root@localhostgcc]#gcc–Wallwarning.c–owarning

warning.c:4警告:“main”的返回類型不是“int”

warning.c:在函數“main”中:

warning.c:7警告:在無返回值的函數中,“return”帶返回值

warning.c:5警告:未使用的變量“tmp”

使用“-Wall”選項找出了未使用的變量tmp,但它并沒有找出無效數據類型的錯誤。

另外,gcc還可以利用選項對單獨的常見錯誤分別指定警告。

(4)優(yōu)化選項。

gcc可以對代碼進行優(yōu)化,它通過編譯選項“-On”來控制優(yōu)化代碼的生成,其中n是一個代表優(yōu)化級別的整數。對于不同版本的gcc來講,n的取值范圍及其對應的優(yōu)化效果可能并不完全相同,比較典型的范圍是從0變化到2或3。

不同的優(yōu)化級別對應不同的優(yōu)化處理工作。如使用優(yōu)化選項“-O”主要進行線程跳轉(ThreadJump)和延遲退棧(DeferredStackPops)兩種優(yōu)化。使用優(yōu)化選項“-O2”除了完成所有“-O1”級別的優(yōu)化之外,同時還要進行一些額外的調整工作,如處理器指令調度等。選項“-O3”則還包括循環(huán)展開和其他一些與處理器特性相關的優(yōu)化工作。

雖然優(yōu)化選項可以加速代碼的運行速度,但對于調試而言將是一個很大的挑戰(zhàn)。因為代碼在經過優(yōu)化之后,原先在源程序中聲明和使用的變量很可能不再使用,控制流也可能會突然跳轉到意外的地方,循環(huán)語句也有可能因為循環(huán)展開而變得到處都有,所有這些對調試來講都將是一場噩夢。所以筆者建議在調試的時候最好不使用任何優(yōu)化選項,只有當程序在最終發(fā)行的時候才考慮對其進行優(yōu)化。

(5)體系結構相關選項。

gcc的體系結構相關選項如表3.10所示。

表3.10 gcc體系結構相關選項列表

選項

含義

-mcpu=type

針對不同的CPU使用相應的CPU指令。可選擇的type有i386、i486、pentium及i686等

-mieee-fp

使用IEEE標準進行浮點數的比較

-mno-ieee-fp

不使用IEEE標準進行浮點數的比較

-msoft-float

輸出包含浮點庫調用的目標代碼

-mshort

把int類型作為16位處理,相當于shortint

-mrtd

強行將函數參數個數固定的函數用retNUM返回,節(jié)省調用函數的一條指令

這些體系結構相關選項在嵌入式的設計中會有較多的應用,讀者需根據不同體系結構將對應的選項進行組合處理。在本書后面涉及具體實例時將會有針對性的講解。

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯系該專欄作者,如若文章內容侵犯您的權益,請及時聯系本站刪除。
換一批
延伸閱讀

LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: 驅動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設備,其驅動電源的性能直接關系到整個系統的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅動電源設計中至關重要的兩個環(huán)節(jié),集成化方案的設計成為提升電機驅動性能的關鍵。

關鍵字: 工業(yè)電機 驅動電源

LED 驅動電源作為 LED 照明系統的 “心臟”,其穩(wěn)定性直接決定了整個照明設備的使用壽命。然而,在實際應用中,LED 驅動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設計、生...

關鍵字: 驅動電源 照明系統 散熱

根據LED驅動電源的公式,電感內電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關鍵字: LED 設計 驅動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產業(yè)的重要發(fā)展方向。電動汽車的核心技術之一是電機驅動控制系統,而絕緣柵雙極型晶體管(IGBT)作為電機驅動系統中的關鍵元件,其性能直接影響到電動汽車的動力性能和...

關鍵字: 電動汽車 新能源 驅動電源

在現代城市建設中,街道及停車場照明作為基礎設施的重要組成部分,其質量和效率直接關系到城市的公共安全、居民生活質量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統光源,成為大功率區(qū)域...

關鍵字: 發(fā)光二極管 驅動電源 LED

LED通用照明設計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數校正(PFC)、空間受限和可靠性等。

關鍵字: LED 驅動電源 功率因數校正

在LED照明技術日益普及的今天,LED驅動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設備造成不利影響,甚至引發(fā)系統故障。因此,采取有效的硬件措施來解決L...

關鍵字: LED照明技術 電磁干擾 驅動電源

開關電源具有效率高的特性,而且開關電源的變壓器體積比串聯穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現在的LED驅動電源

關鍵字: LED 驅動電源 開關電源

LED驅動電源是把電源供應轉換為特定的電壓電流以驅動LED發(fā)光的電壓轉換器,通常情況下:LED驅動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關鍵字: LED 隧道燈 驅動電源
關閉