C語言中程序環(huán)境和預(yù)處理的詳細(xì)圖文講解
1. 程序的翻譯環(huán)境和執(zhí)行環(huán)境
在 ANSI C 的任何一種實(shí)現(xiàn)中,存在兩個不同的環(huán)境。
第1種是翻譯環(huán)境,在這個環(huán)境中源代碼被轉(zhuǎn)換為可執(zhí)行的機(jī)器指令。 第2種是執(zhí)行環(huán)境,它用于實(shí)際執(zhí)行代碼。
2. 詳解編譯+鏈接
2.1 翻譯環(huán)境
- 組成一個程序的每個源文件通過編譯過程分別轉(zhuǎn)換成目標(biāo)代碼(object code)。
- 每個目標(biāo)文件由鏈接器(linker)捆綁在一起,形成一個單一而完整的可執(zhí)行程序。
- 鏈接器同時也會引入標(biāo)準(zhǔn)C函數(shù)庫中任何被該程序所用到的函數(shù),而且它可以搜索程序員個人 的程序庫,將其需要的函數(shù)也鏈接到程序中
編譯分為三步驟(如下圖所示)
2.2 編譯本身也分為幾個階段
1. 預(yù)處理: 選項(xiàng) gcc - E test.c - o test.i
預(yù)處理完成之后就停下來,預(yù)處理之后產(chǎn)生的結(jié)果都放在 test.i 文件中。
2. 編譯 :選項(xiàng) gcc - S test.c
編譯完成之后就停下來,結(jié)果保存在 test.s 中。
3. 匯編 : gcc - c test.c
匯編完成之后就停下來,結(jié)果保存在 test.o 中
2.3 運(yùn)行環(huán)境
程序執(zhí)行的過程:
1. 程序必須載入內(nèi)存中。在有操作系統(tǒng)的環(huán)境中:一般這個由操作系統(tǒng)完成。在獨(dú)立的環(huán)境中,程序的載入必須由手工安排,也可能是通過可執(zhí)行代碼置入只讀內(nèi)存來完成。
2. 程序的執(zhí)行便開始。接著便調(diào)用 main 函數(shù)。
3. 開始執(zhí)行程序代碼。這個時候程序?qū)⑹褂靡粋€運(yùn)行時堆棧(stack),存儲函數(shù)的局部變量和返回地址。程序同時也可以使用靜態(tài)(static )內(nèi)存,存儲于靜態(tài)內(nèi)存中的變量在程序的整個執(zhí)行過程一直保留他們的值。
4. 終止程序。正常終止 main 函數(shù);也有可能是意外終止。
3. 預(yù)處理詳解
3.1 預(yù)定義符號
__FILE__ // 進(jìn)行編譯的源文件
__LINE__ // 文件當(dāng)前的行號
__DATE__ // 文件被編譯的日期
__TIME__ // 文件被編譯的時間
__STDC__ // 如果編譯器遵循 ANSI C ,其值為 1 ,否則未定義
舉個栗子:這些預(yù)定義符號都是內(nèi)置的
#include<stdio.h> int main() { printf("file:%s line:%d\n", __FILE__, __LINE__); return 0; }
這兩個預(yù)定義符號會在屏幕打印出源文件路徑,和該代碼在哪一行
3.2 #define
3.2.1 #define 定義標(biāo)識符
提問: 在define定義標(biāo)識符的時候,要不要在最后加上 ; ? 比如:
#define MAX 1000; #define MAX 1000
建議不要加上 ; ,這樣容易導(dǎo)致問題。
比如下面的場景:
if(condition) max = MAX; else max = 0;
這里會出現(xiàn)語法錯誤。
3.2.2 #define 定義宏
#define 機(jī)制包括了一個規(guī)定,允許把參數(shù)替換到文本中,這種實(shí)現(xiàn)通常稱為宏(macro)或定義
宏(define macro)。
下面是宏的申明方式:
#define name( parament - list ) stuff
其中的 parament - list 是一個由逗號隔開的符號表,它們可能出現(xiàn)在 stuff 中。
注意:
參數(shù)列表的左括號必須與 name 緊鄰。
如果兩者之間有任何空白存在,參數(shù)列表就會被解釋為 stuff 的一部分。
如:
#define SQUARE( x ) x * x
這個宏接收一個參數(shù) x . 如果在上述聲明之后,你把SQUARE( 5 );
置于程序中,預(yù)處理器就會用下面這個表達(dá)式替換上面的表達(dá)式。
警告: 這個宏存在一個問題: 觀察下面的代碼段:
int a = 5; printf("%d\n" ,SQUARE( a + 1) );
替換文本時,參數(shù)x被替換成a + 1,所以這條語句實(shí)際上變成了:
printf ("%d\n",a + 1 * a + 1 );
這樣就比較清晰了,由替換產(chǎn)生的表達(dá)式并沒有按照預(yù)想的次序進(jìn)行求值。
在宏定義上加上兩個括號,這個問題便輕松的解決了
3.2.3 #define 替換規(guī)則
在程序中擴(kuò)展 #define 定義符號和宏時,需要涉及幾個步驟。
- 在調(diào)用宏時,首先對參數(shù)進(jìn)行檢查,看看是否包含任何由#define定義的符號。如果是,它們首先被替換。
- 替換文本隨后被插入到程序中原來文本的位置。對于宏,參數(shù)名被他們的值所替換。
- 最后,再次對結(jié)果文件進(jìn)行掃描,看看它是否包含任何由#define定義的符號。如果是,就重復(fù)上述處理過程。
注意:
1. 宏參數(shù)和 #define 定義中可以出現(xiàn)其他 #define 定義的符號。但是對于宏,不能出現(xiàn)遞歸。
2. 當(dāng)預(yù)處理器搜索 #define 定義的符號的時候,字符串常量的內(nèi)容并不被搜索。
3.2.4 #和##
如何把參數(shù)插入到字符串中?
首先我們看看這樣的代碼:
char* p = "hello ""bit\n"; printf("hello"" bit\n"); printf("%s", p);
這里輸出的是不是
hello bit ?
答案是確定的:是。
我們發(fā)現(xiàn)字符串是有自動連接的特點(diǎn)的。
1.使用 # , 把一個宏參數(shù)變成對應(yīng)的字符串
int i = 10; #define PRINT(FORMAT, VALUE)\ printf("the value of " #VALUE "is "FORMAT "\n", VALUE); ... PRINT("%d", i+3);//產(chǎn)生了什么效果
最終的輸出的結(jié)果應(yīng)該是:
the value of i + 3 is 13
## 的作用
##可以把位于它兩邊的符號合成一個符號。 它允許宏定義從分離的文本片段創(chuàng)建標(biāo)識符。
#define ADD_TO_SUM(num, value) \ sum##num += value;...ADD_TO_SUM(5, 10);//作用是:給sum5增加10.
注: 這樣的連接必須產(chǎn)生一個合法的標(biāo)識符。否則其結(jié)果就是未定義的。
3.2.5 帶副作用的宏參數(shù)
當(dāng)宏參數(shù)在宏的定義中出現(xiàn)超過一次的時候,如果參數(shù)帶有副作用,那么你在使用這個宏的時候就可能出現(xiàn)危險,導(dǎo)致不可預(yù)測的后果。副作用就是表達(dá)式求值的時候出現(xiàn)的永久性效果。 例如:
x + 1 ; // 不帶副作用
x ++ ; // 帶有副作用
MAX宏可以證明具有副作用的參數(shù)所引起的問題。
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
...
x = 5 ;
y = 8 ;
z = MAX ( x ++ , y ++ );
printf ( "x=%d y=%d z=%d\n" , x , y , z ); // 輸出的結(jié)果是什么?
這里我們得知道預(yù)處理器處理之后的結(jié)果是什么:
z = ( (x++) > (y++) ? (x++) : (y++));
所以輸出的結(jié)果是:
x=6 y=10 z=9
3.2.6 命名約定
一般來講函數(shù)的宏的使用語法很相似。所以語言本身沒法幫我們區(qū)分二者。 那我們平時的一個習(xí)慣是:
把宏名全部大寫 函數(shù)名不要全部大寫
3.3 #undef
這條指令用于移除一個宏定義。
3.4 命令行定義
許多 C 的編譯器提供了一種能力,允許在命令行中定義符號。用于啟動編譯過程。
例如:當(dāng)我們根據(jù)同一個源文件要編譯出一個程序的不同版本的時候,這個特性有點(diǎn)用處。(假如某個 程序中聲明了一個某個長度的數(shù)組,如果機(jī)器內(nèi)存有限,我們需要一個很小的數(shù)組,但是另外一個機(jī)器內(nèi)存大些,我們需要一個數(shù)組能夠大些。)
3.5 條件編譯
在編譯一個程序的時候我們?nèi)绻獙⒁粭l語句(一組語句)編譯或者放棄是很方便的。因?yàn)槲覀冇袟l件編譯指令。 比如說:
調(diào)試性的代碼,刪除可惜,保留又礙事,所以我們可以選擇性的編譯。
常見的條件編譯指令:
1. #if 常量表達(dá)式 //... #endif //常量表達(dá)式由預(yù)處理器求值。 如: #define __DEBUG__ 1 #if __DEBUG__ //.. #endif 2.多個分支的條件編譯 #if 常量表達(dá)式 //... #elif 常量表達(dá)式 //... #else //... #endif 3.判斷是否被定義 #if defined(symbol) #ifdef symbol #if !defined(symbol) #ifndef symbol 4.嵌套指令 #if defined(OS_UNIX) #ifdef OPTION1 unix_version_option1(); #endif #ifdef OPTION2 unix_version_option2(); #endif #elif defined(OS_MSDOS) #ifdef OPTION2 msdos_version_option2(); #endif #endif
注:這里#undef是取消#define的定義
#ifdef 是判斷有沒有進(jìn)行宏定義,如果沒有定義則就不執(zhí)行#ifdef里面的語句,#ifdef和#endif是配套使用的
總結(jié)
到此這篇關(guān)于C語言中程序環(huán)境和預(yù)處理的文章就介紹到這了,更多相關(guān)C語言程序環(huán)境和預(yù)處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++使用fdk-aac實(shí)現(xiàn)將音頻PCM編碼成aac
mp4的音頻流通常是aac編碼,我們做音視頻采集的時候就需要將,采集的音頻PCM編碼成aac,本文就來為大家介紹一下C++如何使用fdk-aac實(shí)現(xiàn)將音頻PCM編碼成aac吧2023-11-11Qt如何實(shí)現(xiàn)輸入框@聯(lián)系人的@檢測的示例
本文主要介紹了Qt如何實(shí)現(xiàn)輸入框@聯(lián)系人的@檢測的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08