" />

欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解C語(yǔ)言的預(yù)處理效果

 更新時(shí)間:2021年12月29日 15:29:28   作者:Ssorrymaker  
這篇文章主要為大家介紹了C語(yǔ)言的預(yù)處理效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助

前言

編譯一個(gè)C語(yǔ)言程序涉及很多步驟。其中第一個(gè)步驟被稱為預(yù)處理。C語(yǔ)言的預(yù)處理器在源代碼編譯之前對(duì)其進(jìn)行一些文本性質(zhì)的操作。它的主要任務(wù)包括刪除注釋、插入被#include指令包含的文件內(nèi)容、定義和替換由#define指令定義的符號(hào),同時(shí)確定代碼的部分內(nèi)容是否應(yīng)該根據(jù)一些條件編譯指令進(jìn)行編譯。

一、預(yù)定義符號(hào)

下表為C語(yǔ)言預(yù)處理器定義的符號(hào)。他們的值有的是字符串常量,有的是十進(jìn)制數(shù)字常量。

符號(hào) 示例值 含義
__ FILE__ “test.c” 當(dāng)前編譯的源文件名
__ LINE__ 25 本文件當(dāng)前行號(hào)
__ DATE__ “Dec 27 2021” 文件被編譯日期
__ TIME__ “21:30:23” 文件被編譯時(shí)間
__ STDC__ 1 如果編譯器遵循ANSI C,其值就為1,否則未定義

二、#define

我們先來(lái)看一下它的用法

#define name stuff

有了這條指令以后,每當(dāng)有符號(hào)name出現(xiàn)在這條指令之后時(shí),預(yù)處理器就會(huì)把它替換為stuff。

如果定義中的stuff非常長(zhǎng),那就可以將它分成幾行,除了最后一行,每行的末尾都要加一個(gè)反斜杠,如下面例子所示:

#define DEBUG_PRINT printf("File %s line %d:" \
					"x = %d, y = %d, z = %d, \
					__FILE__, __LINE__, x, y, z)

這里利用了一個(gè)特性:相鄰的字符串常量被自動(dòng)連接為一個(gè)字符串。在調(diào)試一個(gè)存在許多涉及一組變量的不同計(jì)算過(guò)程的程序時(shí),這種類(lèi)型的聲明非常有用。我們可以很容易的插入一條測(cè)試語(yǔ)句,打印出它們的當(dāng)前值。

x *= 2;
y += x;
z = x * y;
DEBUG_PRINT;

1.宏

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

#define name(parameter-list) stuff

其中,parameter-list(參數(shù)列表)是一個(gè)由逗號(hào)分隔的符號(hào)列表,它們可能出現(xiàn)在stuff 中。參數(shù)列表的左括號(hào)必須與name緊鄰。否則,參數(shù)列表就會(huì)被解釋為stuf的一部分。

當(dāng)宏被調(diào)用時(shí),名字后面是一個(gè)由逗號(hào)分隔的值的列表,每個(gè)值都與宏定義中的一個(gè)參數(shù)相對(duì)應(yīng)。當(dāng)參數(shù)出現(xiàn)在程序中時(shí),與每個(gè)參數(shù)對(duì)應(yīng)的實(shí)際值都將會(huì)被替換到stuff中。

例如:

#define SQUARE(x) x*x

SQUARE(5)

當(dāng)這兩條語(yǔ)句位于程序中時(shí),預(yù)處理器就會(huì)用上面的表達(dá)式替換下面的表達(dá)式,就會(huì)變成:5 * 5。

但是上面這個(gè)宏存在一個(gè)問(wèn)題,請(qǐng)大家觀察下面的代碼:

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

可能我們直觀的覺(jué)得這段代碼將打印36這個(gè)值。但是事實(shí)上,它會(huì)打印11。Why?來(lái),我們按照宏的規(guī)則做一個(gè)替換,這條語(yǔ)句將變成:

printf("%d\n", a + 1 * a + 1);

發(fā)現(xiàn)問(wèn)題了嗎,這里由替換而產(chǎn)生的表達(dá)式并沒(méi)有按照預(yù)想的次序進(jìn)行求值。所以,我們要對(duì)宏定義的參數(shù)加上括號(hào),包括stuff整體。這樣就能產(chǎn)生我們預(yù)期的結(jié)果了。

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

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

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

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

2.宏與函數(shù)

宏非常頻繁的用于執(zhí)行簡(jiǎn)單的計(jì)算,比如在兩個(gè)表達(dá)式中尋找其中較大的一個(gè)(或較小):

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

這個(gè)功能好像我們也能用函數(shù)來(lái)實(shí)現(xiàn),那為什么不使用函數(shù)呢?有兩個(gè)原因。首先,用于調(diào)用和從函數(shù)返回的代碼很可能比實(shí)際執(zhí)行這個(gè)小型工作的代碼更大,所以使用宏要比使用函數(shù)在程序中的規(guī)模和速度都更勝一籌。

其次,更為重要的是,函數(shù)的參數(shù)必須聲明為一種特定的類(lèi)型,所以它只能在類(lèi)型合適的表達(dá)式中使用。但是宏是與類(lèi)型無(wú)關(guān)的。

有優(yōu)點(diǎn)就會(huì)有缺點(diǎn),和使用函數(shù)相比,使用宏的不利之處在于每次使用宏時(shí),一份宏定義代碼的副本都將會(huì)插入到程序中。除非宏非常短,否則使用宏可能會(huì)大幅度增加程序的長(zhǎng)度。

還有一些任務(wù)無(wú)法用函數(shù)來(lái)實(shí)現(xiàn),比如下面這個(gè)代碼:

#define MALLOC(n, type) ((type*) malloc ((n) * sizeof(type)))

type是一個(gè)數(shù)據(jù)類(lèi)型,而函數(shù)是無(wú)法將類(lèi)型作為參數(shù)傳遞的。

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

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

x + 1;

這個(gè)表達(dá)式無(wú)論執(zhí)行幾百次都是一樣的,所以它沒(méi)有副作用。

x++;

但是這個(gè)表達(dá)式就不同了,每次執(zhí)行都會(huì)改變x的值,每一次執(zhí)行都是一個(gè)不同的結(jié)果。所以,這個(gè)表達(dá)式是具有副作用的。我們看下面的例子,你覺(jué)得它會(huì)打印出什么:

#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é)果是: x = 6, y = 10, z = 9。產(chǎn)生這個(gè)結(jié)果的原因是那個(gè)較小的值只增加了一次,而那個(gè)較大的值卻增加了兩次——第一次是在比較的時(shí)候,第二次是在執(zhí)行?后面的表達(dá)式時(shí)。這就是一個(gè)具有副作用的宏參數(shù),我們?cè)谑褂玫臅r(shí)候一定要注意。

4. 宏和函數(shù)的不同

通過(guò)一個(gè)表格來(lái)分析:

屬性 #define宏 函數(shù)
代碼長(zhǎng)度 每次使用時(shí),宏代碼都被插入到程序中。除了非常小的宏,程序的長(zhǎng)度將大幅增長(zhǎng) 函數(shù)代碼只出現(xiàn)在一個(gè)地方,每次使用函數(shù)時(shí),都調(diào)用那個(gè)地方的同一份代碼
執(zhí)行速度 更快 存在函數(shù)調(diào)用和返回的額外開(kāi)銷(xiāo)
操作符優(yōu)先級(jí) 宏參數(shù)的求值是在所有周?chē)磉_(dá)式的上下文環(huán)境里,除非它們加上括號(hào),否則臨近操作符的優(yōu)先級(jí)可能會(huì)發(fā)生改變 函數(shù)參數(shù)指在函數(shù)調(diào)用時(shí)求值一次,其結(jié)果傳遞給函數(shù),求值結(jié)果更容易預(yù)測(cè)
參數(shù)求值 參數(shù)每次用于宏定義時(shí),它們都將重新求值。由于多次求值,具有副作用的參數(shù)可能會(huì)產(chǎn)生不可預(yù)料的后果 參數(shù)在函數(shù)被調(diào)用前只求值一次,在函數(shù)中多次使用參數(shù)并不會(huì)導(dǎo)致多個(gè)求職過(guò)程。參數(shù)的副作用并不會(huì)造成任何特殊的問(wèn)題
參數(shù)類(lèi)型 宏與類(lèi)型無(wú)關(guān)。只要對(duì)參數(shù)的操作是合法的,它可以使用任何類(lèi)型的參數(shù) 函數(shù)的參數(shù)是與類(lèi)型有關(guān)的,如果參數(shù)的類(lèi)型不同,就需要使用不同的函數(shù),即使它們執(zhí)行的任務(wù)是相同的

5.#undef

#undef這個(gè)預(yù)處理指令用于移除一個(gè)宏定義:

#undef name

如果現(xiàn)存的名字需要被重新定義,那么首先必須用#undef移除它的舊定義。

三、條件編譯

在編譯一個(gè)程序時(shí),如果可以翻譯或忽略選定的某條語(yǔ)句或某組語(yǔ)句,會(huì)給我們帶來(lái)極大的便利。而條件編譯(conditional compilation)可以實(shí)現(xiàn)這個(gè)目的。使用條件編譯,可以選擇代碼的一部分是被正常編譯還是完全忽略。用于支持條件編譯的基本結(jié)構(gòu)是#if指令和#endif指令。一下是其使用方式:

#if constant-expression
	statements
#endif
#if constant-expression
	statements
#elif constant-expression
	other statements
#else
	other statements
#endif

同時(shí),條件編譯的另一個(gè)用途是在編譯時(shí)選擇不同的代碼部分。所以#if指令還具有可選的#elif和#else子句。如下:

#if constant-expression
	statements
#elif constant-expression
	other statements
#else
	other statements
#endif

#elif子句出現(xiàn)的次數(shù)可以不限。但是每個(gè)常量表達(dá)式(constant-expression)只有當(dāng)前面所有常量表達(dá)式的值都為假時(shí)才會(huì)被編譯。#else子句中的語(yǔ)句只有當(dāng)前面所有常量表達(dá)式的值都為假時(shí)才會(huì)被編譯。

四、文件包含

#include指令使另一個(gè)文件的內(nèi)容被編譯,就好像它實(shí)際出現(xiàn)在#include指令出現(xiàn)的位置一樣。這種替換執(zhí)行的方式很簡(jiǎn)單:預(yù)處理器刪除這條指令,并用包含文件的內(nèi)容取而代之。如果一個(gè)頭文件被包含到十個(gè)源文件中,那它實(shí)際上被編譯了十次。

1.函數(shù)庫(kù)文件包含

C語(yǔ)言編譯器支持兩種不同類(lèi)型的#include文件包含:函數(shù)庫(kù)文件和本地文件。事實(shí)上,他們之間的區(qū)別很小。

函數(shù)庫(kù)文件的包含使用以下語(yǔ)法:

#include<filename>

對(duì)于filename(文件名),并沒(méi)有任何的限制,不過(guò)根據(jù)規(guī)定,標(biāo)準(zhǔn)庫(kù)文件以一個(gè).h后綴結(jié)尾。

編譯器通過(guò)觀察由編譯器定義的“一系列標(biāo)準(zhǔn)位置”查找函數(shù)庫(kù)頭文件。你所使用的編譯器文檔會(huì)說(shuō)明這些標(biāo)準(zhǔn)的位置是什么,以及怎樣修改它們或者在列表中添加其他位置。

2.本地文件包含

以下是#include指令的另外一種形式:

#include"filename"

標(biāo)準(zhǔn)允許編譯器自行決定是否把本地形式的#include和函數(shù)庫(kù)形式的#include區(qū)別對(duì)待。可以先對(duì)本地頭文件使用一種特殊的處理方式,如果失敗,編譯器再按照函數(shù)庫(kù)頭文件的處理方式對(duì)待它們進(jìn)行處理。

處理本地頭文件的一種常見(jiàn)策略就是在源文件所在的當(dāng)前目錄進(jìn)行查找,如果該頭文件并未找到,編譯器就像查找函數(shù)庫(kù)頭文件一樣在標(biāo)準(zhǔn)位置查找本地頭文件。

總結(jié)

不要在一個(gè)宏定義的末尾加上分號(hào),使其成為一條完整語(yǔ)句。在宏定義中使用參數(shù),不要忘了在其周?chē)由侠ㄌ?hào)。同時(shí)不要忘了在宏定義兩邊加上括號(hào).

本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!

相關(guān)文章

  • C語(yǔ)言 鏈?zhǔn)蕉鏄?shù)結(jié)構(gòu)詳解原理

    C語(yǔ)言 鏈?zhǔn)蕉鏄?shù)結(jié)構(gòu)詳解原理

    二叉樹(shù)的鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)是指,用鏈表來(lái)表示一棵二叉樹(shù),即用鏈來(lái)指示元素的邏輯關(guān)系。通常的方法是鏈表中每個(gè)結(jié)點(diǎn)由三個(gè)域組成,數(shù)據(jù)域和左右指針域,左右指針?lè)謩e用來(lái)給出該結(jié)點(diǎn)左孩子和右孩子所在的鏈結(jié)點(diǎn)的存儲(chǔ)地址
    2021-11-11
  • C 語(yǔ)言進(jìn)制之間的轉(zhuǎn)換

    C 語(yǔ)言進(jìn)制之間的轉(zhuǎn)換

    本篇文章主要介紹了C語(yǔ)言進(jìn)制之間的轉(zhuǎn)換,舉例說(shuō)明并附圖片,幫助大家理解,希望對(duì)大家有所幫助
    2016-07-07
  • 基于C++實(shí)現(xiàn)日期計(jì)算器的詳細(xì)教程

    基于C++實(shí)現(xiàn)日期計(jì)算器的詳細(xì)教程

    在現(xiàn)代社會(huì)中,計(jì)算器已經(jīng)進(jìn)入了每一個(gè)家庭,人們?cè)谏詈蛯W(xué)習(xí)中經(jīng)常需要使用到計(jì)算器,下面這篇文章主要給大家介紹了關(guān)于基于C++實(shí)現(xiàn)日期計(jì)算器的相關(guān)資料,需要的朋友可以參考下
    2022-06-06
  • 如何使用C++獲取指定的重載函數(shù)地址

    如何使用C++獲取指定的重載函數(shù)地址

    重載函數(shù)是完全不同的幾個(gè)函數(shù),有不同的函數(shù)地址,下面這篇文章主要給大家介紹了關(guān)于如何使用C++獲取指定的重載函數(shù)地址的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • Qt使用QJson模塊實(shí)現(xiàn)解析Json文件

    Qt使用QJson模塊實(shí)現(xiàn)解析Json文件

    在項(xiàng)目開(kāi)發(fā)過(guò)程中,經(jīng)常會(huì)遇到讀寫(xiě)Json文件的需求,掌握J(rèn)son文件的操作是基礎(chǔ)中的基礎(chǔ),下面我們就來(lái)看看如何使用QT內(nèi)置的QJson模塊解析Json文件吧
    2023-10-10
  • C語(yǔ)言零基礎(chǔ)精通變量與常量

    C語(yǔ)言零基礎(chǔ)精通變量與常量

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言的變量和常量,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-04-04
  • C語(yǔ)言實(shí)現(xiàn)可增容動(dòng)態(tài)通訊錄詳細(xì)過(guò)程

    C語(yǔ)言實(shí)現(xiàn)可增容動(dòng)態(tài)通訊錄詳細(xì)過(guò)程

    這篇文章主要為大家介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易通訊錄的完整流程,此通訊錄還可以增容,并且每個(gè)環(huán)節(jié)都有完整代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-05-05
  • 從使用角度解讀c++20 協(xié)程示例

    從使用角度解讀c++20 協(xié)程示例

    類(lèi)比線程,線程是個(gè)函數(shù),把這個(gè)函數(shù)交給 創(chuàng)建線程的api,然后這個(gè)函數(shù)就變成線程了,這個(gè)函數(shù)本身沒(méi)有任何特殊的地方,就是普通函數(shù),這篇文章主要介紹了從使用角度解讀c++20 協(xié)程示例,需要的朋友可以參考下
    2023-01-01
  • 利用C語(yǔ)言實(shí)現(xiàn)猜數(shù)字小游戲

    利用C語(yǔ)言實(shí)現(xiàn)猜數(shù)字小游戲

    這篇文章主要為大家詳細(xì)介紹了利用C語(yǔ)言實(shí)現(xiàn)猜數(shù)字小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • C語(yǔ)言調(diào)用SQLite數(shù)據(jù)庫(kù)實(shí)現(xiàn)數(shù)據(jù)增刪改查

    C語(yǔ)言調(diào)用SQLite數(shù)據(jù)庫(kù)實(shí)現(xiàn)數(shù)據(jù)增刪改查

    SQLite是一種輕量級(jí)的關(guān)系型數(shù)據(jù)庫(kù)管理系統(tǒng),是一個(gè)開(kāi)源的、零配置的、服務(wù)器端的、自包含的、零管理的、事務(wù)性的SQL數(shù)據(jù)庫(kù)引擎,本文主要介紹了如何調(diào)用SQLite數(shù)據(jù)庫(kù)實(shí)現(xiàn)數(shù)據(jù)增刪改查,需要的可以參考一下
    2023-08-08

最新評(píng)論