欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報(bào)邀請(qǐng)碼778899分享:開發(fā)語言 【C語言】預(yù)處理詳解

柚子快報(bào)邀請(qǐng)碼778899分享:開發(fā)語言 【C語言】預(yù)處理詳解

http://yzkb.51969.com/

前言

上篇博客我們總結(jié)了編譯與鏈接,有說過編譯里第一步是預(yù)處理,那本篇博客將對(duì)預(yù)處理進(jìn)行進(jìn)一步的詳細(xì)的總結(jié)

個(gè)人主頁:小張同學(xué)zkf

若有問題 評(píng)論區(qū)見

感興趣就關(guān)注一下吧

目錄

1. 預(yù)定義符號(hào)

2. #define 定義常量

3. #define定義宏

4. 帶有副作用的宏參數(shù)

5. 宏替換的規(guī)則

6. 宏和函數(shù)的對(duì)比

?7. #和##

7.1 #運(yùn)算符

7.2 ## 運(yùn)算符

8. 命名約定

9. #undef

10. 命令行定義

11. 條件編譯

12. 頭文件的包含

12.1 頭文件被包含的方式

12.1.1 本地文件包含

12.1.2 庫(kù)文件包含

12.2 嵌套文件包含

13. 其他預(yù)處理指令

1. 預(yù)定義符號(hào)

C語言設(shè)置了一些預(yù)定義符號(hào),

可以直接使用

,預(yù)定義符號(hào)也是在

預(yù)處理期間

處理的。

__FILE__

//

進(jìn)行編譯的源文件

__LINE__

//文

件當(dāng)前的行號(hào)

__DATE__

//文

件被編譯的日期

__TIME__

//文

件被編譯的時(shí)間

__STDC__

//

如果編譯器遵循

ANSI C

,其值為

1

,否則未定義

我們來看一下,在vs2022中是否遵循ANSI C(標(biāo)準(zhǔn)C)

?由此可見,vs2022不遵循ANSI C

注:預(yù)定義符號(hào)在預(yù)處理間就被替換了

2. #define 定義常量

基本語法:

#

define

name stuff

#

define

MAX 1000

#

define

reg register

//

register

這個(gè)關(guān)鍵字,創(chuàng)建?個(gè)簡(jiǎn)短的名字

#

define

do_forever for(;;)

//用

更形象的符號(hào)來替換?種實(shí)現(xiàn)

#

define

CASE break;case

//

在寫

case

語句的時(shí)候?動(dòng)把

break

寫上。

//

如果定義的

stuff

過?,可以分成?行寫,除了最后??外,每?的后?都加?個(gè)反斜杠

(

續(xù)?

)

。

#

define

DEBUG_PRINT printf(

"file:%s\tline:%d\t \

date:%s\ttime:%s\n"

,\

__FILE__,__LINE__ , \

__DATE__,__TIME__ )

注:上面“\”是續(xù)行符

?我們想一下,在define定義標(biāo)識(shí)符的時(shí)候,要不要在最后加上---“;”

比如:

#

define

MAX 1000;

#

define

MAX 1000

?建議不要加上 ;? ? ?,這樣容易導(dǎo)致問題。

比如下面的場(chǎng)景:

if

(condition)

max = MAX;

else

max =

0

;

如果是加了分號(hào)的情況,等替換后,if和else之間就是2條語句,而沒有大括號(hào)的時(shí)候,if后邊只能有一條語句。這里會(huì)出現(xiàn)語法錯(cuò)誤。

3. #define定義宏

#define機(jī)制包括了一個(gè)規(guī)定,允許把參數(shù)替換到文本中,這種實(shí)現(xiàn)通常稱為宏(macro)或定義宏(define macro)。

下面是宏的申明方式:

#

define

name( parament-list ) stuff

其中的

parament-list

是一個(gè)由逗號(hào)隔開的符號(hào)表,它們可能出現(xiàn)在stuff中。

注意:

參數(shù)列表的左括號(hào)必須與name緊鄰,如果兩者之間有任何空白存在,參數(shù)列表就會(huì)被解釋為stuff的一部分。

?#define SQUARE( x ) x * x

這個(gè)宏接收一個(gè)參數(shù)

x

.如果在上述聲明之后,你把

SQUARE( 5 );

置于程序中,預(yù)處理器就會(huì)用

下面這個(gè)表達(dá)式替換上面的表達(dá)式:5*5

警告:

這個(gè)宏存在一個(gè)問題:

觀察下面的代碼段:

int

a =

5

;

printf

(

"%d\n"

,SQUARE( a +

1

) );

乍一看,你可能覺得這段代碼將打印36,事實(shí)上它將打印11,為什么呢?

?

替換文本時(shí),參數(shù)x被替換成a + 1,所以這條語句實(shí)際上變成了:

printf

(

"%d\n"

,a +

1

* a +

1

);

這樣就比較清晰了,由替換產(chǎn)生的表達(dá)式并沒有按照預(yù)想的次序進(jìn)行求值。

在宏定義上加上兩個(gè)括號(hào),這個(gè)問題便輕松的解決了:

#

define

SQUARE(x) (x) * (x)

這樣預(yù)處理之后就產(chǎn)生了預(yù)期的效果:

printf

(

"%d\n"

,(a +

1

) * (a +

1

) );

?這里還有一個(gè)宏定義:

#

define

DOUBLE(x) (x) + (x)

?定義中我們使用了括號(hào),想避免之前的問題,但是這個(gè)宏可能會(huì)出現(xiàn)新的錯(cuò)誤。

int

a =

5

;

printf

(

"%d\n"

,

10

* DOUBLE(a));

這將打印什么值呢?看上去,好像打印100,但事實(shí)上打印的是55

我們發(fā)現(xiàn)替換之后:

printf

(

"%d\n"

,

10

* (

5

) + (

5

));

乘法運(yùn)算先于宏定義的加法,所以出現(xiàn)了

55

.

這個(gè)問題的解決辦法是在宏定義表達(dá)式兩邊加上一對(duì)括號(hào)就可以了。

#

define

DOUBLE( x) ( ( x ) + ( x ) )

提示:

所以用于對(duì)數(shù)值表達(dá)式進(jìn)行求值的宏定義都應(yīng)該用這種方式加上括號(hào),避免在使用宏時(shí)由于參數(shù)中的操作符或鄰近操作符之間不可預(yù)料的相互作用。

寫宏一定要考慮優(yōu)先級(jí)和結(jié)合性,這個(gè)很重要?。。。。。。。。。。。?/p>

該加括號(hào)就加括號(hào) ?。。。。。?!

4. 帶有副作用的宏參數(shù)

當(dāng)宏參數(shù)在宏的定義中出現(xiàn)超過一次的時(shí)候,如果參數(shù)帶有副作用,那么你在使用這個(gè)宏的時(shí)候就可能出現(xiàn)危險(xiǎn),導(dǎo)致不可預(yù)測(cè)的后果。副作用就是表達(dá)式求值的時(shí)候出現(xiàn)的永久性效果。

1

x+

1

;

//

不帶副作?

2

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++));

?第一個(gè)x++是5,第一個(gè)y++是8,5<8是假,此刻x是6,y是9,執(zhí)行第二個(gè)y++,那返回的值是9,z就為9,第二個(gè)y++之后,y最后為10,

輸出的結(jié)果是:x=6 y=10 z=9

5. 宏替換的規(guī)則

在程序中擴(kuò)展#define定義符號(hào)和宏時(shí),需要涉及幾個(gè)步驟。

1.

在調(diào)用宏時(shí),首先對(duì)參數(shù)進(jìn)行檢查,看看是否包含任何由#define定義的符號(hào)。如果是,它們?先被替換。

2.

替換文本隨后被插入到程序中原來文本的位置。對(duì)于宏,參數(shù)名被他們的值所替換。

3.

最后,再次對(duì)結(jié)果文件進(jìn)行掃描,看看它是否包含任何由#define定義的符號(hào)。如果是,就重復(fù)上述處理過程。

注意:

1.

宏參數(shù)

#define 定義

中可以出現(xiàn)其他#define定義的符號(hào)。但是對(duì)于宏,不能出現(xiàn)遞歸。

2.

當(dāng)預(yù)處理器搜索#define定義的符號(hào)的時(shí)候,字符串常量的內(nèi)容并不被搜索。

6. 宏和函數(shù)的對(duì)比

宏通常被應(yīng)用于執(zhí)行簡(jiǎn)單的運(yùn)算。

比如在兩個(gè)數(shù)中找出較大的一個(gè)時(shí),寫成下面的宏,更有優(yōu)勢(shì)一些。

#

define

MAX(a, b) ((a)>(b)?(a):(b))

?那為什么不用函數(shù)來完成這個(gè)任務(wù)?

原因有二:

1.

于調(diào)用函數(shù)和從函數(shù)返回的代碼可能比實(shí)際執(zhí)行這個(gè)小型計(jì)算工作所需要的時(shí)間更多。? ? ? 所以宏比函數(shù)在程序的規(guī)模和速度方面更勝一籌。

2.

更為重要的是函數(shù)的參數(shù)必須聲明為特定的類型。所以函數(shù)只能在類型合適的表達(dá)式上使? ? ? 用。反之這個(gè)宏可以適用于整形、長(zhǎng)整型、浮點(diǎn)型等,甚至可以把類型當(dāng)做參數(shù)傳進(jìn)去比?

。宏的參數(shù)是類型無關(guān)的,無需進(jìn)行類型比較。

?和函數(shù)相比宏的劣勢(shì):

1.

每次使用宏的時(shí)候,一份宏定義的代碼將插入到程序中。除非宏比較短,否則可能大幅度增加程序的長(zhǎng)度,如果不斷調(diào)用這個(gè)宏,那程序長(zhǎng)度不斷增大,空間也會(huì)增大,反之函數(shù)永遠(yuǎn)調(diào)用的是那一塊空間函數(shù),在這個(gè)方面函數(shù)比較簡(jiǎn)便。

2.

宏是沒法調(diào)試的。

3.

宏由于類型無關(guān),也就不夠嚴(yán)謹(jǐn)。

4.

宏可能會(huì)帶來運(yùn)算符優(yōu)先級(jí)的問題,導(dǎo)致程容易出現(xiàn)錯(cuò)。

宏有時(shí)候可以做函數(shù)做不到的事情。比如:宏的參數(shù)可以出現(xiàn)類型,但是函數(shù)做不到。

#

define

MALLOC(num, type)\

?(type

)malloc(num

sizeof(type))

?...

?

//

使用

?MALLOC(

10

,

int

);

//

類型作為參數(shù)

?

//

預(yù)處理器替換之后:

?(

int

*

)

malloc

(

10

sizeof

(

int

));

宏和函數(shù)的一個(gè)對(duì)比

補(bǔ)充

這里我們補(bǔ)充一個(gè)奇怪的東西,在c++里面有個(gè)內(nèi)聯(lián)函數(shù)(inline)它具有宏的特點(diǎn),也有函數(shù)的特點(diǎn),我們先簡(jiǎn)單了解下,等到c++再詳細(xì)總解

?7. #和##

7.1 #運(yùn)算符

#運(yùn)算符將宏的一個(gè)參數(shù)轉(zhuǎn)換為字符串字面量。它僅允許出現(xiàn)在帶參數(shù)的宏的替換列表中。

#運(yùn)算符所執(zhí)行的操作可以理解為”字符串化“。

當(dāng)我們有一個(gè)變量

int a = 10;

的時(shí)候,我們想打印出:

the value of a is 10

.

就可以寫成:

#

define

PRINT(n) printf(

"the value of "

#n

" is %d"

, n);

當(dāng)我們按照下面的方式調(diào)用的時(shí)候:

PRINT(a);//當(dāng)我們把a(bǔ)替換到宏的體內(nèi)時(shí),就出現(xiàn)了#a,而#a就是轉(zhuǎn)換為"a",時(shí)一個(gè)字符串

?代碼就會(huì)被預(yù)處理為:

printf

(

"the value of ""a" " is %d"

, a);

運(yùn)行代碼就能在屏幕上打?。?/p>

the value of a is

10

7.2 ## 運(yùn)算符

##

可以把位于它兩邊的符號(hào)合成一個(gè)符號(hào),它允許宏定義從分離的文本片段創(chuàng)建標(biāo)識(shí)符。

##

被稱

為記號(hào)粘合

這樣的連接必須產(chǎn)生一個(gè)合法的標(biāo)識(shí)符。否則其結(jié)果就是未定義的。

這里我們想想,寫一個(gè)函數(shù)求2個(gè)數(shù)的較大值的時(shí)候,不同的數(shù)據(jù)類型就得寫不同的函數(shù)。

比如:

int

int_max

(

int

x,

int

y)

{

return

x>y?x:y;

}

float

float_max

(

float

x,

float

y)

{

return

x>yx:y;

}

?但是這樣寫起來太繁瑣了,現(xiàn)在我們這樣寫代碼試試:

//

宏定義

#

define

GENERIC_MAX(type) \

type type##_max(type x, type y)\

{ \

return (x>y?x:y); \

}

?使用宏,定義不同函數(shù)

GENERIC_MAX(

int

)?

//

替換到宏體內(nèi)后

int##_max

?成了新的符號(hào)

int_max

做函數(shù)名

?

GENERIC_MAX(

float

)

//

替換到宏體內(nèi)后

float##_max

?成了新的符號(hào)

float_max

做函數(shù)名

?

int

main

()

?

{

?

//

調(diào)?函數(shù)

?

int

m = int_max(

2

,

3

);

?

printf

(

"%d\n"

, m);

?

float

fm = float_max(

3.5f

,

4.5f

);

?

printf

(

"%f\n"

, fm);

?

return

0

;

?

}

?輸出:

3

4.500000

?整體代碼如上圖,這個(gè)代碼非常巧妙地用宏來函數(shù)定義,只需將類型傳進(jìn)去,這個(gè)##就是用來將左右兩個(gè)標(biāo)識(shí)符合并成一個(gè)標(biāo)識(shí)符,type改變,那對(duì)應(yīng)的type_max也發(fā)生改變,這樣就能有不同的函數(shù)名字。

假如沒有##,會(huì)發(fā)生什么

如上圖可知,沒有##,這個(gè)type_max本身就是一體的,函數(shù)名不會(huì)隨著你穿進(jìn)去類型的變化而變化,type_max就是簡(jiǎn)簡(jiǎn)單單的一個(gè)標(biāo)識(shí)符。?

?在實(shí)際開發(fā)過程中##使用的很少,很難取出非常貼切的例子。

8. 命名約定

一般來講函數(shù)的宏的使用語法很相似。所以語言本身沒法幫我們區(qū)分二者。

那我們平時(shí)的一個(gè)習(xí)慣是:

把宏名全部大寫

函數(shù)名不要全部大寫

當(dāng)然也有小寫的宏,例如:?

9. #undef

這條指令用于移除一個(gè)宏定義。

#

undef

NAME

?

//

如果現(xiàn)存的?個(gè)名字需要被重新定義,那么它的舊名字?先要被移除。

比如:

這個(gè)MAX不是被定義了嘛,因?yàn)?undef出現(xiàn)所以取消了MAX定義,此刻MAX未定義

10. 命令行定義

許多C 的編譯器提供了一種能力,允許在命令行中定義符號(hào)。用于啟動(dòng)編譯過程。

例如:當(dāng)我們根據(jù)同一個(gè)源文件要編譯出一個(gè)程序的不同版本的時(shí)候,這個(gè)特性有點(diǎn)用處。(假定某個(gè)程序中聲明了一個(gè)某個(gè)長(zhǎng)度的數(shù)組,如果機(jī)器內(nèi)存有限,我們需要一個(gè)很小的數(shù)組,但是另外一個(gè)機(jī)器內(nèi)存大些,我們需要一個(gè)數(shù)組能夠大些。)

#

include

int

main

()

{

int

array

[ARRAY_SIZE];

int

i =

0

;

for

(i =

0

; i< ARRAY_SIZE; i ++)

{

array

[i] = i;

}

for

(i =

0

; i< ARRAY_SIZE; i ++)

{

printf

(

"%d "

,

array

[i]);

}

printf

(

"\n"

);

return

0

;

}

假如我們不用#define定義,我們可以直接在命令行中進(jìn)行如下操作

?編譯指令:

//linux

環(huán)境演?

gcc -D ARRAY_SIZE=

10

programe.c

?用-D命令直接把ARRAY_SIZE賦值成10(這是在gcc編譯器下)

11. 條件編譯

在編譯一個(gè)程序的時(shí)候我們?nèi)绻獙⒁粭l語句(一組語句)編譯或者放棄是很方便的。因?yàn)槲覀冇袟l件編譯指令。

比如說:

調(diào)試性的代碼,刪除可惜,保留又礙事,所以我們可以選擇性的編譯。

#

include

#

define

__DEBUG__

int

main

()

{

int

i =

0

;

int

arr[

10

] = {

0

};

for

(i=

0

; i<

10

; i++)

{

arr[i] = i;

#

ifdef

__DEBUG__

printf

(

"%d\n"

, arr[i]);

//

為了觀察數(shù)組是否賦值成功。

#

endif

//__DEBUG__

}

return

0

;

}

?常見的條件編譯指令:

1.

#

if

常量表達(dá)式

//...

#

endif

//

常量表達(dá)式由預(yù)處理器求值。

如:

#

define

__DEBUG__ 1

#

if

__DEBUG__

//..

#

endif

2.

多個(gè)分支的條件編譯

#

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

注意:只要是#if指令,那么一定要在用完之后加上#endif !!!

12. 頭文件的包含

12.1 頭文件被包含的方式

12.1.1 本地文件包含

#

include

"filename"

查找策略:先在源文件所在目錄下查找,如果該頭文件未找到,編譯器就像查找?guī)旌瘮?shù)頭文件一樣在標(biāo)準(zhǔn)位置查找頭文件。

如果找不到就提示編譯錯(cuò)誤

Linux環(huán)境的標(biāo)準(zhǔn)頭文件的路徑:

/usr/include

?VS環(huán)境的標(biāo)準(zhǔn)頭文件的路徑:

C:\Program

Files

(x86)\Microsoft Visual Studio 12.0\VC\include

?

//

這是

VS2013

的默認(rèn)路徑

?注意按照自己的安裝路徑去找。

12.1.2 庫(kù)文件包含

#

include

查找頭文件直接去標(biāo)準(zhǔn)路徑下去查找,如果找不到就提示編譯錯(cuò)誤。

這樣是不是可以說,對(duì)于庫(kù)文件也可以使用?

“”

的形式包含?

答案是肯定的,可以,但是這樣做查找的效率就低些,當(dāng)然這樣也不容易區(qū)分是庫(kù)文件還是本地文件了。

12.2 嵌套文件包含

我們已經(jīng)知道,

#include

指令可以使另外一個(gè)文件被編譯。就像它實(shí)際出現(xiàn)于

#include

指令的

地方一樣。

這種替換的方式很簡(jiǎn)單:預(yù)處理器先刪除這條指令,并用包含文件的內(nèi)容替換。

一個(gè)頭文件被包含10次,那就實(shí)際被編譯10次,如果重復(fù)包含,對(duì)編譯的壓力就比較大。

test.c

#

include

"test.h"

#

include

"test.h"

#

include

"test.h"

#

include

"test.h"

#

include

"test.h"

int

main

()

{

return

0

;

}

test.h

void

test

();

struct

Stu

{

int

id;

char

name[

20

];

};

如果直接這樣寫,test.c文件中將test.h包含5次,那么test.h文件的內(nèi)容將會(huì)被拷貝5份在test.c中。

如果test.h 文件比較大,這樣預(yù)處理后代碼量會(huì)劇增。如果工程比較大,有公共使用的頭文件,被大家都能使用,又不做任何的處理,那么后果真的不堪設(shè)想。

如何解決頭文件被重復(fù)引入的問題?答案:條件編譯。

每個(gè)頭文件的開頭寫:

#

ifndef

__TEST_H__

#

define

__TEST_H__

//

頭?件的內(nèi)容

#

endif

//__TEST_H__

或者

?#pragma once

?就可以避免頭文件的重復(fù)引入。

13. 其他預(yù)處理指令

#

error

#

pragma

#

line

#

pragma

pack()

……

?結(jié)束語

本篇文章小編已經(jīng)盡力在總結(jié)重點(diǎn),但肯定有些地方挖的不夠深,如果想更加詳細(xì)的了解這方面的點(diǎn)點(diǎn)滴滴,我們可以參考《C語言深度解剖》

OK感謝觀看?。?!

柚子快報(bào)邀請(qǐng)碼778899分享:開發(fā)語言 【C語言】預(yù)處理詳解

http://yzkb.51969.com/

好文推薦

評(píng)論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。

轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://m.gantiao.com.cn/post/19269769.html

發(fā)布評(píng)論

您暫未設(shè)置收款碼

請(qǐng)?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問

文章目錄