激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專(zhuān)注于服務(wù)器技術(shù)及軟件下載分享
分類(lèi)導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - C/C++ - C語(yǔ)言編程之預(yù)處理過(guò)程與define及條件編譯

C語(yǔ)言編程之預(yù)處理過(guò)程與define及條件編譯

2022-01-07 14:34Booksort C/C++

這篇文章主要為大家介紹了C語(yǔ)言編程之預(yù)處理過(guò)程與define及條件編譯,文中通過(guò)圖文及示例代碼方式作了詳細(xì)的解釋?zhuān)行枰呐笥芽梢越梃b參考下

這張圖描述了從源文件到可執(zhí)行文件的整體步驟:

C語(yǔ)言編程之預(yù)處理過(guò)程與define及條件編譯

這張圖展示了大體上步驟。

C語(yǔ)言編程之預(yù)處理過(guò)程與define及條件編譯

從代碼到運(yùn)行環(huán)境,編譯器提供了翻譯環(huán)境。在一個(gè)程序中,會(huì)存在多個(gè)文件 ,而每個(gè)源文件都會(huì)單獨(dú)經(jīng)過(guò)編譯器處理。

C語(yǔ)言編程之預(yù)處理過(guò)程與define及條件編譯

預(yù)編譯:
1,會(huì)將#include等頭文件所包含的內(nèi)容,庫(kù)函數(shù)全部拷貝過(guò)來(lái)
2,代碼中注釋的刪除
3,由#define所定義的符號(hào)全部替換進(jìn)代碼中
對(duì)預(yù)處理指令的操作

編譯:把C代碼翻譯成匯編代碼
1,語(yǔ)法分析(判斷是否存在語(yǔ)言的語(yǔ)法錯(cuò)誤而造成無(wú)法編譯)
2,詞法分析
3,語(yǔ)義分析(分析每句代碼的意思)
4,符號(hào)匯總(會(huì)將整個(gè)程序中的全局符號(hào)進(jìn)行匯總)

匯編
1,形成符號(hào)表

C語(yǔ)言編程之預(yù)處理過(guò)程與define及條件編譯

一個(gè)程序,兩個(gè)文件test.c與add.c,在test.c中,有

extern int add(int x,int y);//聲明對(duì)該函數(shù)引用,在其他文件中找該函數(shù)。

在匯編時(shí),各個(gè)文件都會(huì)形參函數(shù)符號(hào)表,但extern并不會(huì)形成地址標(biāo)記,只是一個(gè)0x00。

鏈接:合并段表
符號(hào)表的合并與重定位檢查各個(gè)函數(shù)及其定義聲明

C語(yǔ)言編程之預(yù)處理過(guò)程與define及條件編譯

 

名示常量#define

define的預(yù)處理指令

#define MACRO substitution
預(yù)處理指令 宏 替換體

宏只是起到替換作用,在替換過(guò)程中不產(chǎn)生任何運(yùn)算
舉個(gè)例子

#define SUM 2+2
int num = SUM * SUM;

不了解的人可能會(huì)認(rèn)為是
num=44;
但,實(shí)際是替換作用
num=2+22+2;
這就是宏只起到替換作用的意思。

再來(lái)介紹一個(gè)原理性概念

記號(hào)
從技術(shù)角度來(lái)看,可以把宏的替換體看作是記號(hào)型字符串。而不是字符型字符串。在C預(yù)處理器記號(hào)是宏定義的替換體的中單獨(dú)的“詞”,用空白把詞分開(kāi)。例如:

#define FOUR 2*2
#define FOURS 2 * 2

這兩個(gè)對(duì)于預(yù)處理器要看預(yù)處理器把這個(gè)替換體看成什么。如果是字符型字符串,這空格也會(huì)是字符串的一部分。但如果是記號(hào)型字符串,空格就會(huì)被認(rèn)為是分隔符,就和2*2是一個(gè)意思。總之要看編譯器的規(guī)則。
總結(jié)一下

如果編譯器理解替換體是字符型字符串,那么空格就會(huì)被認(rèn)為是字符串的一部分
2 * 2就和2*2不是一個(gè)意思。
如果編譯器理解為記號(hào)型字符串,那么空格就會(huì)被認(rèn)為只是分隔符,并不影響。空格不算替換體的一部分
2 * 2和2乘2則是一個(gè)意思。

 

重定義常量

假設(shè)把MAX設(shè)為30,在文件中又把它重新定義為10.這個(gè)過(guò)程叫重定義常量但不同的標(biāo)準(zhǔn)有不同的規(guī)則。有一些允許重定義,但是會(huì)報(bào)警。ANSI標(biāo)準(zhǔn)則采用,只有新舊定義完全相同才允許重定義。
完全相同意味著替換體中必須記號(hào)完全相同,順序也必須相同

#define MAX 2 * 3
#define MAX 2 * 3

這才允許

#define MAX 2 * 3
#define MAX 2*3

這不符合那個(gè)標(biāo)準(zhǔn)。(雖然我不知道這個(gè)標(biāo)準(zhǔn)的重定義有什么用,我比較菜)
注:根據(jù)一個(gè)大佬的建議,這類(lèi)代碼非常致命,非常不好,最好不使用。

 

在#define中使用參數(shù)

在#define中也可以創(chuàng)建外形和作用與函數(shù)類(lèi)似的類(lèi)函數(shù)宏
帶有函數(shù)的宏可以達(dá)到部分函數(shù)的作用。

#define	SQUARE(X) X*X
mul=SQUARE(2);

與函數(shù)調(diào)用有些相似。
同時(shí)最好使用足夠多的括號(hào)去確保運(yùn)算和結(jié)合性的正確。

mul=SQUARE(x++)

則會(huì)造成運(yùn)算不符合要求。

用宏參數(shù)創(chuàng)建字符串:#運(yùn)算符

#define PSQRA(X) printf("X is %d\n",((X)*(X));
#define PSQRB(X) printf("#X" is %d\n",((X)*(X));

這兩個(gè)是可以打印出不同的效果
#作為一個(gè)預(yù)處理運(yùn)算符,可以把記號(hào)轉(zhuǎn)換成字符串,如果X是一個(gè)宏形參,那么#X就是“X”的字符串的形參名。

這叫字符串化

int y=50;
PSQRA(y)
X is 2500
PSQRB(Y)
y is 2500
PSQRA(2+4)
X is 36
PSQRB(2+4)
2+4 is 36

這就是區(qū)別。

 

預(yù)處理器粘合劑:##運(yùn)算符

#運(yùn)算符可以作用于宏的替換體
而##運(yùn)算符也可以作用。

#define NUMBER(n) X##n
NUMBER(4)可展開(kāi)為x4

例如

#include <stdio.h>
#define XNAME(N) x##N
#define PRINT(N) pritnf("x"#N"=%d\n",x##N);
int main(void)
{
	int XNAME(1) = 10;//x1=10
	int XNAME(2) = 20;//x2=10
	int x3 = 0;
	PRINT(1);//printf("x1=%d",x1);
	PRINT(2);//printf("x2=%d",x2);
	PRINT(3);//printf("x3=%d",x3);
}

C語(yǔ)言編程之預(yù)處理過(guò)程與define及條件編譯

 

變參宏:… 和_ _ VAG_ARGS_ _

一些函數(shù)可以接受數(shù)量可變的參數(shù)(就是沒(méi)有固定傳遞的參數(shù)的數(shù)量,如printf()和scanf())。而宏也可以擁有這樣的能力。

#define PR(...) printf(_ _VAG_ARGS_ _)
PR("HELLO WORLD");//printf("HELLO WORLD");
PR("x1=%d,x2=%d",10,20);//printf(""x1=%d,x2=%d",10,20);

相當(dāng)于這樣的效果。
省略號(hào)只能代替最后的宏參數(shù)。不能在省略號(hào)加其他參數(shù)。

#define PR(x,...,y) #x #_ _VAG_ARGS_ _ #y

是不被允許的。

 

宏與函數(shù)

有相當(dāng)一部分的宏可以起到和函數(shù)一樣的效果,但到底該怎么選呢?
宏和函數(shù)可以達(dá)到同樣效果。宏比函數(shù)要簡(jiǎn)單一些,同時(shí),在編譯器的消耗時(shí)間也要遠(yuǎn)小于函數(shù)。但是稍有不慎就會(huì)產(chǎn)生一些副作用,導(dǎo)致結(jié)果不可預(yù)測(cè)。
宏與函數(shù)的比較實(shí)際上就是關(guān)于時(shí)間與空間的比較。

宏在預(yù)編譯的時(shí)候會(huì)生成內(nèi)聯(lián)代碼,也就是會(huì)在程序中替換生成語(yǔ)句。如果調(diào)用20次,則會(huì)在程序中插入20行代碼。

但如果調(diào)用函數(shù)20次,函數(shù)也只有一份副本,節(jié)省了相當(dāng)一部分空間。但執(zhí)行函數(shù)時(shí),要調(diào)用,再執(zhí)行,再返回,遠(yuǎn)比宏插入內(nèi)聯(lián)語(yǔ)句消耗的時(shí)間要多。

宏較函數(shù)也存在缺陷

  • 當(dāng)宏較大時(shí)會(huì)增加代碼長(zhǎng)度。
  • 宏是無(wú)法調(diào)試(在預(yù)編譯的時(shí)候就已完成替換),可能會(huì)出現(xiàn)問(wèn)題。
  • 宏由于不要求類(lèi)型,會(huì)造成不嚴(yán)謹(jǐn)(這也是對(duì)于函數(shù)的一個(gè)好處,函數(shù)傳參會(huì)要求參數(shù)類(lèi)型,而宏只會(huì)將參數(shù)當(dāng)作字符串處理,只要是int或float類(lèi)型都可以)
  • 宏可能會(huì)帶來(lái)運(yùn)算符優(yōu)先級(jí)的問(wèn)題,使運(yùn)算不可預(yù)測(cè)。

自己按照情況去使用。如果使用宏容易出現(xiàn)副作用,那還是調(diào)用函數(shù)吧。

但要記住以下幾點(diǎn)

1,記住宏名中不允許有空格,但在替換字符串中可以有空格。ANSI C允許在參數(shù)列表中使用空格。

2,用括號(hào)把宏的參數(shù)和替換體括起來(lái),正確展開(kāi),防止出現(xiàn)副作用。

3,一般用大寫(xiě)字母表示宏常量,一般不全大寫(xiě)表示宏函數(shù)

4,如果用宏來(lái)加快函數(shù)的運(yùn)行速度,要先確定宏和函數(shù)之間是否有差距。且,如果只使用一次那對(duì)速度加快影響不大。最好在多重嵌套中使用。

 

預(yù)處理指令

當(dāng)編譯器碰上#include 指令時(shí),會(huì)查看后面文件名并把內(nèi)容添加到當(dāng)前文件中。

#include <stdio.h>//查找系統(tǒng)目錄文件
#include "mtfile.h"//查找當(dāng)前工作目錄
#include "/usr/biff/file.h"//查找/usr/biff/目錄

不同的系統(tǒng)有不同的規(guī)則,但<>與“”的規(guī)則是不變的。

通過(guò)頭文件的引用,我們才能使用各種函數(shù)。頭文件中有各種函數(shù)的聲明。再通過(guò)庫(kù)文件去調(diào)用函數(shù)的原型。

 

#undef指令

可以通過(guò)該指令去向之前#define定義的宏

#include <stdio.h>
#define MAX 10
#undef MAX
int main(void)
{
	printf("%d", MAX);
}

C語(yǔ)言編程之預(yù)處理過(guò)程與define及條件編譯

直接報(bào)錯(cuò)
#undef可以取消,某個(gè)宏的定義,可以用用來(lái)防止某個(gè)宏被重復(fù)定義,造成錯(cuò)誤。

 

從C預(yù)處理器的角度看已定義

預(yù)處理器在處理標(biāo)識(shí)符時(shí)遵循相同規(guī)則。當(dāng)預(yù)處理器發(fā)現(xiàn)一個(gè)標(biāo)識(shí)符時(shí),會(huì)將其當(dāng)作已定義或未定義。而這里的已定義是由預(yù)處理器決定。如果該標(biāo)識(shí)符是由define定義的且沒(méi)有undef取消,那就是已定義。如果是定義的某個(gè)全局變量,那就是未定義(對(duì)預(yù)處理器而言)。

#define定義的宏的作用域從文件開(kāi)頭開(kāi)始,延申至文件結(jié)尾或者遇到#undef取消定義,如果跨文件使用,那使用的位置要在#include引用的文件后。

 

條件編譯

就跟條件判斷語(yǔ)句有著類(lèi)似的意思。

#ifdef , #else , #endif

舉個(gè)例子

#include <stdio.h>
#define MAX 10
#undef MAX
#ifdef MAX
	#include <string.h>
	#define MIN 10
#endif // 
int main(void)
{
	printf("%d", MIN);
}

C語(yǔ)言編程之預(yù)處理過(guò)程與define及條件編譯

結(jié)果就是這個(gè)。
但屏蔽#undef

#include <stdio.h>
#define MAX 10
//#undef MAX
#ifdef MAX
	#include <string.h>
	#define MIN 10
#endif // 
int main(void)
{
	printf("%d", MIN);
}

C語(yǔ)言編程之預(yù)處理過(guò)程與define及條件編譯

可以運(yùn)行。
條件編譯指令與條件判斷語(yǔ)句類(lèi)似。
只不過(guò),條件判斷語(yǔ)句是判讀是否執(zhí)行,
二條件編譯指令是判斷是否進(jìn)行預(yù)編譯。
#endif用來(lái)結(jié)束該指令的范圍
再引入一個(gè)指令

#ifndef DEBUG

如果DEBUG未定義就執(zhí)行編譯,如果已定義就不執(zhí)行。

還有這幾個(gè)指令,非常接近條件判斷語(yǔ)句

#if ,#elif ,#else

#if和#elif與if和else if類(lèi)似。但它們的后面接整形常量表達(dá)式。
0為假,非0為真
都是判斷是否進(jìn)行預(yù)編譯

可以通過(guò)條件編譯指令去防止某些文件被多次調(diào)用導(dǎo)致問(wèn)題出現(xiàn)

#ifndef _FILE_H
#define _FILE_H
文件內(nèi)容
。
。
#endif

或者直接使用

#pragma once
//可以保證文件只是使用一次

 

offsetof函數(shù)

size_t offsetof( structName, memberName );

用于測(cè)算結(jié)構(gòu)體成員相對(duì)于起始位置的偏移量

實(shí)現(xiàn)

#define OFFSETOF(structName,memberName) (int)&(((struct structName*)0)->memberName)

從0地址處,開(kāi)始向成員訪問(wèn)再取地址在強(qiáng)轉(zhuǎn)成整形。

以上就是C語(yǔ)言編程之預(yù)處理過(guò)程與define及條件編譯的詳細(xì)內(nèi)容,更多關(guān)于C語(yǔ)言預(yù)處理的資料請(qǐng)關(guān)注服務(wù)器之家其它相關(guān)文章!

原文鏈接:https://blog.csdn.net/weixin_52199109/article/details/115048443

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 成品片a免费直接观看 | 亚洲一区二区三区日本久久九 | 特级黄一级播放 | 国产精品久久国产精麻豆96堂 | 国产精品一区二区三区在线播放 | 黄色片免费在线 | v天堂在线视频 | 欧美一级成人 | 国产午夜电影在线观看 | 中国毛片在线观看 | 精品久久久久久久久亚洲 | 国产一区二区三区欧美 | 欧美a级毛片 | 久久精品99国产国产精 | 国产成人av免费观看 | 久久9色 | 国产视频在线免费观看 | 久久国产精品小视频 | 国产一级在线看 | 538任你躁在线精品视频网站 | 羞羞视频免费观看网站 | 欧美日韩亚洲国产精品 | 日韩美香港a一级毛片免费 欧美一级淫片007 | 欧美成人小视频 | 欧美三级日本三级少妇99 | 中文字幕一二三区芒果 | 中文字幕精品在线播放 | 黄色的视频免费观看 | 国产午夜精品一区二区三区不卡 | 黄色网址电影 | 秋霞a级毛片在线看 | 999精品国产 | 国产黄网 | 精品一区二区电影 | 黄色成年在线观看 | 色欲香天天天综合网站 | 中文字幕网在线 | 久久国产28 | 久久综合久久综合久久综合 | 国产电影av在线 | 精国品产一区二区三区有限公司 |