深圳網(wǎng)站建設(shè) 推薦xtdseo百度系app有哪些
前言:
? ? ? ? 由于c語(yǔ)言的程序編譯鏈接的這塊知識(shí)點(diǎn)不清楚,回來(lái)復(fù)習(xí)一遍,以便于好理解c++知識(shí),我會(huì)盡快更新下一篇文章。
目錄
1.程序的翻譯環(huán)境和執(zhí)行環(huán)境
2.翻譯環(huán)境(編譯+鏈接)
編譯(編譯器)
預(yù)編譯(預(yù)處理)
1.頭文件的包含
2.注釋的測(cè)試
編譯過(guò)程
匯編過(guò)程
鏈接
1.合并段表
2.符號(hào)表的合并和重定位
計(jì)算機(jī)語(yǔ)言的發(fā)展
運(yùn)行環(huán)境(翻譯之后)
3.預(yù)處理詳解?
3.1 預(yù)定義符號(hào)
3.2 #define
3.2.1 #define 定義標(biāo)識(shí)符
3.2.2 #define 定義宏 ?
3.2.3#define 替換規(guī)則
3.2.4 #和##
3.2.5 帶副作用的宏參數(shù)
1.程序的翻譯環(huán)境和執(zhí)行環(huán)境
總體過(guò)程:
在ANSI C的任何一種實(shí)現(xiàn)中,存在兩個(gè)不同的環(huán)境
第1種是翻譯環(huán)境,在這個(gè)環(huán)境中源代碼被轉(zhuǎn)換為可執(zhí)行的機(jī)器指令。
第2種是執(zhí)行環(huán)境,它用于實(shí)際執(zhí)行代碼。
2.翻譯環(huán)境(編譯+鏈接)
我們常說(shuō)一個(gè)test.c文件要經(jīng)過(guò)以下步驟才能產(chǎn)生可執(zhí)行的程序,那么具體是怎么做到的呢?
在vs編譯器上是要經(jīng)過(guò)以下過(guò)程的:
源文件和目標(biāo)文件的關(guān)系:
????????VS是一個(gè)集成開(kāi)發(fā)環(huán)境,集成很多的功能ctr1+F5,不方便觀察每個(gè)細(xì)節(jié)的功能接下來(lái),我使用gcc這個(gè)編譯器給大家演示
????????由于上圖的操作把這個(gè)編譯運(yùn)行的步驟一次走完了,我們一步步來(lái),咱們用指令讓程序停在預(yù)編譯的階段:
編譯(編譯器)
預(yù)編譯(預(yù)處理)
將控制臺(tái)的內(nèi)容放到test.i之后,預(yù)處理階段所做的事情:
1.頭文件的包含
2.注釋的測(cè)試
編譯過(guò)程
功能:把C語(yǔ)言代碼翻譯成匯編代碼
1.語(yǔ)法分析
2.詞法分析
3.語(yǔ)義分析
4.符號(hào)匯總函數(shù)名,全局變量, 不會(huì)匯總 局部變量(函數(shù)運(yùn)行起來(lái)才行)![]()
鏈接:https://pan.baidu.com/s/1cVcZRTj772hJXm0mO0Q_cw?pwd=1234?
提取碼:1234

匯編過(guò)程
1.把匯編代碼轉(zhuǎn)換成二進(jìn)制的指令
2.形成符號(hào)表?
鏈接
1.合并段表
2符號(hào)表的合并和重定位
1、2的作用其實(shí)是在鏈接期間為這種跨文件的代碼進(jìn)行協(xié)作的時(shí)候起作用的。
在vscode中操作:
過(guò)程:
1.合并段表
2.符號(hào)表的合并和重定位
? ? ? ? 把無(wú)效的地址給替換掉,符號(hào)表和段表的關(guān)系是:符號(hào)表是段表的內(nèi)容
那該如何體現(xiàn)這個(gè)作用呢?
????????假設(shè)我們?cè)趖est.c文件底下extern了一個(gè)函數(shù)Add,但是在add.c底下沒(méi)有實(shí)現(xiàn)這個(gè)Add函數(shù),就會(huì)輸出以下錯(cuò)誤(鏈接型錯(cuò)誤)
? ? ? ? 如果說(shuō)這個(gè)函數(shù)寫錯(cuò)的話,在main函數(shù)內(nèi)調(diào)用大寫的,但是在add.c內(nèi)定義成小寫的,是依然找不到
計(jì)算機(jī)語(yǔ)言的發(fā)展
運(yùn)行環(huán)境(翻譯之后)
1. 程序必須載入 內(nèi)存 中。在有操作系統(tǒng)的環(huán)境中:一般這個(gè)由操作系統(tǒng)完成。在獨(dú)立的環(huán)境中,程序的載入必須由手工安排,也可能是通過(guò)可執(zhí)行代碼置入只讀內(nèi)存來(lái)完成。2. 程序的執(zhí)行便開(kāi)始。接著便調(diào)用 main 函數(shù)(main函數(shù)第一行開(kāi)始)3. 開(kāi)始執(zhí)行程序代碼。這個(gè)時(shí)候程序?qū)⑹褂靡粋€(gè)運(yùn)行時(shí)堆棧( stack )-- 函數(shù)棧幀 ,存儲(chǔ)函數(shù)的局部變量和返回地址。 程序同時(shí)也可以使用靜態(tài)(static)內(nèi)存,存儲(chǔ)于靜態(tài)內(nèi)存中的變量在程序的整個(gè)執(zhí)行過(guò)程一直保留他們的值。4. 終止程序。正常終止 main 函數(shù);也有可能是意外終止。
3.預(yù)處理詳解?
3.1 預(yù)定義符號(hào)
__FILE__ ? ? ? //進(jìn)行編譯的源文件__LINE__ ? ? //文件當(dāng)前的行號(hào)__DATE__ ? ? //文件被編譯的日期__TIME__ ? ? //文件被編譯的時(shí)間
執(zhí)行:
__STDC__ ? ?//如果編譯器遵循ANSI C,其值為1,否則未定義
?而放在gcc編譯器上面是可以執(zhí)行通過(guò)的:
????????編譯器在代碼編譯的時(shí)候,會(huì)對(duì)函數(shù)和變量名重命名的,C++ 中會(huì)更加復(fù)雜
????????在C語(yǔ)言中重命名的規(guī)則基本就是:加_
3.2 #define
3.2.1 #define 定義標(biāo)識(shí)符
語(yǔ)法:#define name stuff
全部例子:?
#define MAX 1000
#define reg register ? ? ? ? ?//為 register這個(gè)關(guān)鍵字,創(chuàng)建一個(gè)簡(jiǎn)短的名字
#define do_forever for(;;) ? ? //用更形象的符號(hào)來(lái)替換一種實(shí)現(xiàn)
#define CASE break;case ? ? ? ?//在寫case語(yǔ)句的時(shí)候自動(dòng)把 break寫上。
// 如果定義的 stuff過(guò)長(zhǎng),可以分成幾行寫,除了最后一行外,每行的后面都加一個(gè)反斜杠(續(xù)行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \date:%s\ttime:%s\n" ,\__FILE__,__LINE__ , ? ? ? \__DATE__,__TIME__ )
#define MAX 1000

?#define reg register//為 register這個(gè)關(guān)鍵字,創(chuàng)建一個(gè)簡(jiǎn)短的名字 ?
#define do_forever for(;;)//用更形象的符號(hào)來(lái)替換一種實(shí)現(xiàn)?
#define CASE break;case //在寫case語(yǔ)句的時(shí)候自動(dòng)把 break寫上。

長(zhǎng)行如何拆分,\后面只能是換行,不能是其他?
?如果定義的 stuff過(guò)長(zhǎng),可以分成幾行寫,除了最后一行外,每行的后面都加一個(gè)反斜杠(續(xù)行符)。

在define定義標(biāo)識(shí)符的時(shí)候,要不要在最后加上 ; ?
#define MAX 1000;#define MAX 1000

if ( condition )????????max = MAX ;else????????max = 0 ;
這里會(huì)出現(xiàn)語(yǔ)法錯(cuò)誤.
3.2.2 #define 定義宏 ?
#define name( parament-list ) stuff其中的 parament - list 是一個(gè)由逗號(hào)隔開(kāi)的符號(hào)表,它們可能出現(xiàn)在 stuff 中
#define SQUARE( x ) x * x
SQUARE ( 5 );
5 * 5

int a = 5 ;printf ( "%d\n" , SQUARE ( a + 1 ) );

替換文本時(shí),參數(shù)x被替換成a + 1,所以這條語(yǔ)句實(shí)際上變成了:printf ("%d\n",a + 1 * a + 1 );
#define SQUARE(x) (x) * (x)

printf ( "%d\n" ,( a + 1 ) * ( a + 1 ) );
#define DOUBLE(x) (x) + (x)
定義中我們使用了括號(hào),想避免之前的問(wèn)題,但是這個(gè)宏可能會(huì)出現(xiàn)新的錯(cuò)誤。
int a = 5 ;printf ( "%d\n" , 10 * DOUBLE ( a ));
printf ("%d\n",10 * (5) + (5));
#define DOUBLE( x) ? ( ( x ) + ( x ) )
所以用于對(duì)數(shù)值表達(dá)式進(jìn)行求值的宏定義都應(yīng)該用這種方式加上括號(hào),避免在使用宏時(shí)由于參數(shù)中的操作符或鄰近操作符之間不可預(yù)料的相互作用。
所以這就涉及到#define的替換規(guī)則了
3.2.3#define 替換規(guī)則
在程序中擴(kuò)展 #define 定義符號(hào)和宏時(shí),需要涉及幾個(gè)步驟。????????1. 在調(diào)用宏時(shí),首先對(duì)參數(shù)進(jìn)行檢查,看看是否包含任何由 #define 定義的符號(hào)。如果是,它們首先被替換。????????2. 替換文本隨后被插入到程序中原來(lái)文本的位置。對(duì)于宏,參數(shù)名被他們的值所替換。????????3. 最后,再次對(duì)結(jié)果文件進(jìn)行掃描,看看它是否包含任何由 #define 定義的符號(hào)。如果是,就重復(fù)上述處理過(guò)程。
3.2.4 #和##
如何把參數(shù)(變量名)插入到字符串中?
首先我們看看這樣的代碼:
char* p = "hello ""bit\n";
printf("hello"" bit\n");
printf("%s", p);
?這里輸出的是不是hello bit ?
? ? ? ? 請(qǐng)看下圖,有沒(méi)有那么一條語(yǔ)句,將這三個(gè)變量傳參傳過(guò)去,然后實(shí)現(xiàn)它們各自的輸出呢??
現(xiàn)在定義一個(gè)宏
#define print_format(num, format) \?printf("the value of #num is "format)
并且讓它輸出:
這時(shí)候發(fā)現(xiàn)需要正確輸入它們的數(shù)值:
## 可以把位于它兩邊的符號(hào)合成一個(gè)符號(hào)。它允許宏定義從分離的文本片段創(chuàng)建標(biāo)識(shí)符。

3.2.5 帶副作用的宏參數(shù)
x + 1 ; //不帶副作用x ++ ; //帶有副作用
在vscode2019內(nèi)調(diào)試一下:
? ? ? ? 想知道宏和函數(shù)的區(qū)別嗎,欲知后事如何,請(qǐng)聽(tīng)下回分解。