C語言高級編程


一、gcc和gdb

GNU工具

編譯工具:把一個源程序編譯為一個可執行程序

調試工具:能對執行程序進行源碼或匯編級調試

軟件工程工具:用於協助多人開發或大型軟件項目的管理,如make、CVS、Subvision

其他工具:用於把多個目標文件鏈接成可執行文件的鏈接器,或者用作格式轉換的工具。

1、GCC編譯器

全稱為GNU CC ,GNU項目中符合ANSI C標准的編譯系統

編譯如C、C++、Object C、Java、Fortran、Pascal、Modula-3和Ada等多種語言

GCC是可以在多種硬體平台上編譯出可執行程序的超級編譯器,其執行效率與一般的編譯器相比平均效率要高20%~30%

一個交叉平台編譯器,適合在嵌入式領域的開發編譯

a、gcc所支持后綴名解釋

image

b、編譯器的主要組件

image

c、GCC的基本用法和選項

Gcc最基本的用法是∶gcc [options] [filenames]

-c,只編譯,不連接成為可執行文件,編譯器只是由輸入的.c等源代碼文件生成.o為后綴的目標文件,通常用於編譯不包含主程序的子程序文件。

-o output_filename,確定輸出文件的名稱為output_filename,同時這個名稱不能和源文件同名。如果不給出這個選項,gcc就給出預設的可執行文件a.out。

-g,產生符號調試工具(GNU的gdb)所必要的符號資訊,要想對源代碼進行調試,我們就必須加入這個選項。

-O,對程序進行優化編譯、連接,采用這個選項,整個源代碼會在編譯、連接過程中進行優化處理,這樣產生的可執行文件的執行效率可以提高,但是,編譯、連接的速度就相應地要慢一些。

-O2,比-O更好的優化編譯、連接,當然整個編譯、連接過程會更慢。

-I dirname,將dirname所指出的目錄加入到程序頭文件目錄列表中,是在預編譯過程中使用的參數。

-L dirname,將dirname所指出的目錄加入到程序函數檔案庫文件的目錄列表中,是在鏈接過程中使用的參數。

d、GCC編譯過程

image

e、“hello”的演變歷程

image

2、GDB調試工具

a、Gdb調試流程

image

image

二、條件編譯

編譯器根據條件的真假決定是否編譯相關的代碼。

常見的條件編譯有兩種方法:

a、根據宏是否定義,其語法如下:

#ifdef <macro>

……

#else

……

#endif

實例:

#define _DEBUG_

#ifdef _DEBUG_

printf(“The macro _DEBUG_ is defined\n”);

#else

printf(“The macro _DEBUG_ is not defined\n”);

#endif

b、根據宏的值,其語法如下:

#if <macro>

……

#else

……

#endif

實例:

#define _DEBUG_ 1

#if _DEBUG_

printf(“The macro _DEBUG_ is defined\n”);

#else

printf(“The macro _DEBUG_ is not defined\n”);

#endif

三、結構體與共用體

1、結構體

a、簡述

在實際的處理對象中,有許多信息是由多個不同類型的數據組合在一起進行描述,而且這些不同類型的數據是互相聯系組成了一個有機的整體。此時,就要用到一種新的構造類型數據——結構體(structure),簡稱結構。結構體的使用為處理復雜的數據結構(如動態數據結構等)提供了有效的手段,而且,它們為函數間傳遞不同類型的數據提供了方便。

b、概念

結構體是用戶自定義的新數據類型,在結構體中可以包含若干個不同數據類型和不同意義的數據項(當然也可以相同),從而使這些數據項組合起來反映某一個信息。例如,可以定義一個職工worker結構體,在這個結構體中包括職工編號、姓名、性別、年齡、工資、家庭住址、聯系電話。這樣就可以用一個結構體數據類型的變量來存放某個職工的所有相關信息。並且,用戶自定義的數據類型worker也可以與int、double等基本數據類型一樣,用來作為定義其他變量的數據類型。

c、定義

定義一個結構體類型的一般形式為:

struct 結構體名

{

數據類型   成員名1;

數據類型   成員名2;

數據類型   成員名n;

};

d、結構體類型變量的定義方法

先定義結構體類型再定義變量名,這是C語言中定義結構體類型變量最常見的方式,這種形式的定義的一般形式為:

struct 結構體名

{

成員列表;

};

struct 結構體名 變量名;

在定義類型的同時定義變量,這種形式的定義的一般形式為:

struct 結構體名

{

成員列表;

}變量名;

直接定義結構類型變量,其一般形式為:

struct //沒有結構體名

{

成員列表;

}變量名;

e、結構體的大小

一個結構體變量占用內存的實際大小,也可以利用sizeof求出。它的運算表達式為:sizeof(運算量)//求出給定的運算量占用內存空間的字節數;其中運算量可以是變量、數組或結構體變量,可以是數據類型的名稱。

f、注意事項

(1)不能將一個結構體類型變量作為一個整體加以引用,而只能對結構體類型變量中的各個成員分別引用。

(2)如果成員本身又屬一個結構體類型,則要用若干個成員運算符,一級一級地找到最低的一級成員。只能對最低級的成員進行賦值或存取以及運算。例如,可以這樣訪問各成員:

worker1.age

worker1.name

worker1.birthday.year

worker1.birthday.month

worker1.birthday.day

注意:不能用worker1.birthday來訪問worker1變量中的成員birthday,因為birthday本身是一個結構體變量。

(3)對成員變量可以像普通變量一樣進行各種運算(根據其類型決定可以進行的運算)。例如:

worker2.age=worker1.age;

sum=worker1.age+worker2.age;

worker1.age++;

(4)在數組中,數組是不能彼此賦值的,而結構體類型變量可以相互賦值。

在C程序中,同一結構體類型的結構體變量之間允許相互賦值,而不同結構體類型的結構體變量之間不允許相互賦值,即使兩者包含有同樣的成員。

2、結構體數組

具有相同結構體類型的結構體變量也可以組成數組,稱它們為結構體數組。結構體數組的每一個數組元素都是結構體類型的數據,它們都分別包括各個成員(分量)項。

3、結構體指針

可以設定一個指針變量用來指向一個結構體變量。此時該指針變量的值是結構體變量的起始地址,該指針稱為結構體指針。

結構體指針與前面介紹的各種指針變量在特性和方法上是相同的。與前述相同,在程序中結構體指針也是通過訪問目標運算“*”訪問它的對象。結構體指針在程序中的一般定義形式為:

struct 結構體名 *結構指針名;其中的結構體名必須是已經定義過的結構體類型。

當表示指針變量p所指向的結構體變量中的成員時,“(*結構體指針名).成員名”這種表示形式總是需要使用圓括號,顯得很不簡煉。因此,對於結構體指針指向的結構體成員項,給出了另外一種簡潔的表示方法,如下表示:結構體指針名->成員名;它與前一種表示方法在意義上是完全等價的。例如,結構體指針p指向的結構體變量中的成員name可以表示如下:(*p).name 或 p->name

4、共用體

a、共用體的概念

在C語言中,不同數據類型的數據可以使用共同的存儲區域,這種數據構造類型稱為共用體,簡稱共用,又稱聯合體。共用體在定義、說明和使用形式上與結構體相似。兩者本質上的不同僅在於使用內存的方式上。 定義一個共用體類型的一般形式為:

union 共用體名

{

成員表列;

};

例如:

union gy

{

int i;

char c;

float f;

};

這里定義了一個共用體類型union gy,它由三個成員組成,這三個成員在內存中使用共同的存儲空間。由於共用體中各成員的數據長度往往不同,所以共用體變量在存儲時總是按其成員中數據長度最大的成員占用內存空間。在這一點上共用體與結構體不同,結構體類型變量在存儲時總是按各成員的數據長度之和占用內存空間。

5、typedef

在C語言中,允許使用關鍵字typedef定義新的數據類型

其語法如下:

typedef <已有數據類型> <新數據類型>;

如:

typedef int INTEGER;

這里新定義了數據類型INTEGER, 其等價於int

INTEGER i; <==> int i;

在C語言中經常在定義結構體類型時使用typedef,例如

typedef struct _node_

{

int data;

struct _node_ *next;

} listnode, *linklist;

這里定義了兩個新的數據類型listnode和linklist。其中listnode等價於數據類型struct _node_ 而 linklist等價於struct _node_ *。

四、內存管理

C/C++定義了4個內存區間:

代碼區/全局變量與靜態變量區/局部變量區即棧區/動態存儲區即堆區。

1、靜態存儲分配

通常定義變量,編譯器在編譯時都可以根據該變量的類型知道所需內存空間的大小,從而系統在適當的時候為他們分配確定的存儲空間。

2、在棧上創建

在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。

3、動態存儲分配

有些操作對象只有在程序運行時才能確定,這樣編譯器在編譯時就無法為他們預定存儲空間,只能在程序運行時,系統根據運行時的要求進行內存分配,這種方法稱為動態存儲分配。

所有動態存儲分配都在堆區中進行。

從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc申請任意多少的內存,程序員自己負責在何時用free釋放內存。動態內存的生存期由我們決定,使用非常靈活,但問題也最多。

注意:當程序運行到需要一個動態分配的變量或對象時,必須向系統申請取得堆中的一塊所需大小的存貯空間,用於存貯該變量或對象。當不再使用該變量或對象時,也就是它的生命結束時,要顯式釋放它所占用的存貯空間,這樣系統就能對該堆空間進行再次分配,做到重復使用有限的資源;堆區是不會自動在分配時做初始化的(包括清零),所以必須用初始化式(initializer)來顯式初始化。

4、malloc/free

void * malloc(size_t num)

void free(void *p)

malloc函數本身並不識別要申請的內存是什么類型,它只關心內存的總字節數。

malloc申請到的是一塊連續的內存,有時可能會比所申請的空間大。其有時會申請不到內存,返回NULL。

malloc返回值的類型是void *,所以在調用malloc時要顯式地進行類型轉換,將void * 轉換成所需要的指針類型。

如果free的參數是NULL的話,沒有任何效果。

釋放一塊內存中的一部分是不被允許的。

a、刪除一個指針p

free(p);,實際意思是刪除了p所指的目標(變量或對象等),釋放了它所占的堆空間,而不是刪除p本身,釋放堆空間后,p就成了空懸指針;

b、動態分配失敗

返回一個空指針(NULL),表示發生了異常,堆資源不足,分配失敗;

c、malloc與free是配對使用的

free只能釋放堆空間。如果malloc返回的指針值丟失,則所分配的堆空間無法回收,稱內存泄漏,同一空間重復釋放也是危險的,因為該空間可能已另分配,所以必須妥善保存malloc返回的指針,以保證不發生內存泄漏,也必須保證不會重復釋放堆內存空間;

d、動態分配的變量或對象的生命期

無名對象的生命期並不依賴於建立它的作用域,比如在函數中建立的動態對象在函數返回后仍可使用。我們也稱堆空間為自由空間(free store)就是這個原因。但必須記住釋放該對象所占堆空間,並只能釋放一次,在函數內建立,而在函數外釋放是一件很容易失控的事,往往會出錯。

e、野指針

不是NULL指針,是指向“垃圾”內存的指針。“野指針”是很危險的。 “野指針”的成因主要有兩種:指針變量沒有被初始化;指針p被free之后,沒有置為NULL,讓人誤以為p是個合法的指針。指針操作超越了變量的作用范圍。這種情況讓人防不勝防。

五、Makefile

1、Make簡介

工程管理器,顧名思義,是指管理較多的文件 ;Make工程管理器也就是個“自動編譯管理器”,這里的“自動”是指它能夠根據文件時間戳自動發現更新過的文件而減少編譯的工作量,同時,它通過讀入Makefile文件的內容來執行大量的編譯工作;Make將只編譯改動的代碼文件,而不用完全編譯。

2、Makefile基本結構

Makefile是Make讀入的唯一配置文件,包括:由make工具創建的目標體(target),通常是目標文件或可執行文件;要創建的目標體所依賴的文件(dependency_file);創建每個目標體時需要運行的命令(command)。注意:命令行前面必須是一個”TAB鍵”,否則編譯錯誤為:*** missing separator. Stop。

image

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM