最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C語言零基礎(chǔ)徹底掌握預(yù)處理下篇

 更新時(shí)間:2022年08月09日 11:10:46   作者:程序猿教你打籃球  
在C語言的程序中包括各種以符號(hào)#開頭的編譯指令,這些指令稱為預(yù)處理命令。預(yù)處理命令屬于C語言編譯器,而不是C語言的組成部分,通過預(yù)處理命令可擴(kuò)展C語言程序設(shè)計(jì)的環(huán)境

1、條件編譯

1.1 條件編譯如何使用

C語言提供的條件編譯的功能可以讓我們按照不同的條件去編譯不同的程序部分,從而產(chǎn)生不同目標(biāo)代碼文件。

第一種形式:

#ifdef 標(biāo)識(shí)符

程序段1

#else

程序段2

#endif

它的功能是,如果標(biāo)識(shí)符已經(jīng)被 #define 定義了,則只會(huì)對(duì)程序段1進(jìn)行編譯,不會(huì)對(duì)程序段2進(jìn)行編譯,如果沒有被定義則反之,如果我們不需要程序段2,也可以省去 #else 和他對(duì)應(yīng)的程序段。

第二種形式:

#ifndef 標(biāo)識(shí)符

程序段1

#else

程序段2

#endif

第二種形式與第一種形式的區(qū)別是將 ifdef 改為 ifndef,它的功能是,如果標(biāo)識(shí)符沒有被 #dfine 定義,則對(duì)程序段1進(jìn)行編譯,不會(huì)對(duì)程序段2進(jìn)行編譯,如果被定義了則反之,如果我們不需要程序段2,也可以省去 #else 和他對(duì)應(yīng)的程序段。

第三種形式:

#if 常量表達(dá)式

程序段1

#else

程序段2

#endif

第三種形式的功能是:如果常量表達(dá)式的值為真(非0),則對(duì)程序段1進(jìn)行編譯,否則對(duì)程序段2進(jìn)行編譯,因此可以使程序在不同條件下,完成不同的功能。

至于里面還可以添加 #elif 命令,意義與 else if 相同,形成一個(gè) if else 階梯狀語句,可進(jìn)行多種編譯選擇。

注意:如果定義空宏則會(huì)報(bào)錯(cuò),因?yàn)?#if 后面必須要更常量表達(dá)式!

1.2 用 #if 模擬 #ifdef

此代碼的意思是,如果 PRINT 宏被定義了,則執(zhí)行第一個(gè)打印函數(shù),否則執(zhí)行第二個(gè)打印函數(shù),同時(shí)我們也可以模擬 #ifndef,只需前面加個(gè)邏輯非就可以 ' ! ',例如:#if (!defined(PRINT))

就這樣完了嗎?其實(shí)并沒有,在更復(fù)雜的項(xiàng)目中,往往會(huì)出現(xiàn)兩個(gè)或多個(gè)宏需要同時(shí)定義才能滿足需求,我舉一個(gè)很簡單的例子,如果我定義了 C 宏和 CPP 宏,我才可以編譯所對(duì)應(yīng)的代碼:

如上代碼就需要兩個(gè)宏都被定義才能編譯下面的程序段,相信學(xué)習(xí)過邏輯與的小伙伴應(yīng)該很容易理解吧,那么我們?nèi)绻枰獌蓚€(gè)都未定義才能編譯下面的程序段呢?如何寫?

兩個(gè)都未定義才編譯: #if (!defined(C) && !defined(CPP))前面分別加邏輯非就可以 ' ! '

或者:#if (!(defined(C) || defined(CPP)))本代碼中邏輯或只要有一個(gè)被定義,就為真,然后執(zhí)行邏輯非,這樣也能保證兩個(gè)都未定義才進(jìn)行編譯!

至于最后用不用大括號(hào)給括起來,我的建議是括起來,這樣我們閱讀代碼會(huì)更直觀!

既然出現(xiàn)了邏輯與,是不是也可以出現(xiàn)邏輯或呢?當(dāng)然上面已經(jīng)有例子了,但是這里我就不一一演示了,感興趣的可以下來自己去嘗試一下。

條件編譯支持嵌套:

這里其實(shí)和我們平常用的 if 嵌套式是似的,也很容易理解,這里我們就不細(xì)說,有一點(diǎn)要注意的就是,條件編譯每個(gè) #if 都需要有對(duì)應(yīng)的 #endif 來結(jié)束

1.3 為何要有條件編譯

我們先對(duì)我們上面2小節(jié)的內(nèi)容做一個(gè)總結(jié):條件編譯本質(zhì)上是讓編譯器對(duì)代碼進(jìn)行裁剪!

本質(zhì)認(rèn)識(shí):條件編譯,其實(shí)就是編譯器根據(jù)實(shí)際情況,對(duì)代碼進(jìn)行裁剪,而這里 “實(shí)際情況” ,取決于代碼平臺(tái),代碼本身的業(yè)務(wù)邏輯。

  • 可以只保留當(dāng)前最需要的代碼邏輯,其他去掉,可以減少生成代碼的大小
  • 可以寫出跨平臺(tái)的代碼,讓一個(gè)具體業(yè)務(wù),在不同平臺(tái)編譯的時(shí)候,可以有同樣的表現(xiàn)

條件編譯都用在哪些地方呢?

張三有個(gè)公司,公司有個(gè)項(xiàng)目,項(xiàng)目對(duì)應(yīng)的軟件又有專業(yè)版,免費(fèi)版,精簡版等等...

難道每個(gè)版本都對(duì)應(yīng)著不同的代碼嗎?不是的,這樣維護(hù)起來太麻煩了,其實(shí)所謂不同的版本,本質(zhì)就是功能上的有和無,所以在技術(shù)層面上,為了更好的維護(hù),當(dāng)然可以使用條件編譯,需要哪個(gè)版本,就是用條件編譯裁剪就行。

著名的 Linux 內(nèi)核,功能上,其實(shí)也是用條件編譯進(jìn)行功能裁剪的,用來滿足不同平臺(tái)的軟件。

2、文件包含

2.1 #include 究竟干了什么

我相信 #include 對(duì)于每個(gè)編程小伙伴來說都不陌生,很多人寫 C 語言第一件事就是寫上 #include <stdio.h> 可能老師會(huì)告訴你們這是包含標(biāo)準(zhǔn)輸入輸出頭文件,至于如何包含的,可能不會(huì)跟你講。那今天我們就來通過預(yù)處理來看一看到底是如何包含的:

我們來寫上一小段代碼:

前面說過,預(yù)處理會(huì)將頭文件展開,去注釋,宏替換,條件編譯等等

在 Linux 環(huán)境下我們可以執(zhí)行命令:gcc -E test.c -o test.i保留預(yù)處理之后的文件并命名為 test.i

為了更好的對(duì)比,我們執(zhí)行 vim 命令模式下的 vs 指令:vs/sur/include/tdio.h 也就是打開標(biāo)準(zhǔn)輸入輸出的頭文件:

看到預(yù)處理的結(jié)果之后,發(fā)現(xiàn)文件大小比我們實(shí)際代碼要大得多!

結(jié)論:#include 本質(zhì)是把頭文件相關(guān)內(nèi)容,拷貝到源文件中。

2.2 防止頭文件重復(fù)包含的條件編譯是如何做到的

既然我們會(huì)包含頭文件,那有沒有可能存在頭文件重復(fù)被包含的可能性呢?導(dǎo)致我們頭文件被重復(fù)拷貝?

這里可能會(huì)有很多老師也教過,同學(xué)們啊,我們寫頭文件的時(shí)候一定要寫如下代碼啊,這是防止頭文件重復(fù)包含的啊:

#ifndef _TEST_H_
#define _TEST_H_
#include <stdio.h>
#define MAX 999
int g_val = 10;
extern void Print();
...
#endif

如上代碼很多小伙伴都知道在#ifndef _TEST_H_ 和 #endif 之間寫的頭文件包含,宏定義,全局變量,函數(shù)聲明,都不會(huì)被重復(fù)拷貝,為什么呢?他是如何做到的?我們實(shí)驗(yàn)證明 (如下兩張圖最右邊是預(yù)處理之后的結(jié)果) :

如下代碼是沒有帶上條件編譯防止頭文件重復(fù)包含,但在源文件已經(jīng)重復(fù)包含的例子:

我們加上#ifndef _TEST_H_ 和 #endif在來看重復(fù)包含的效果:

已經(jīng)沒有重復(fù)拷貝的情況了,看來確實(shí)有防止頭文件重復(fù)包含的效果!

那么這條語句是如何做到的呢?

我們前面學(xué)過 #ifndef 如果沒有定義這個(gè)宏,則執(zhí)行后續(xù)語句,當(dāng)?shù)谝淮挝覀冾^文件展開的時(shí)候,確實(shí)沒有定義_TEST_H_ 這個(gè)宏,所以會(huì)執(zhí)行后續(xù)的語句,但是在第一次展開的時(shí)候我們立馬定義了_TEST_H_ 宏,所以我們重復(fù)包含頭文件第二次展開的時(shí)候,這個(gè)宏已經(jīng)被定義了,所以也就不會(huì)去執(zhí)行#ifndef后續(xù)語句了!

結(jié)論:所有頭文件都得帶上條件編譯,防止頭文件重復(fù)包含!當(dāng)然也可以直接 #pragma once

重復(fù)包含的一定會(huì)報(bào)錯(cuò)嗎?顯然是不會(huì)的,但是會(huì)引起多次拷貝,會(huì)影響編譯效率。

3、選學(xué)內(nèi)容

3.1 #error 預(yù)處理

#error 預(yù)處理指令的作用是:編譯程序時(shí),只要遇到 #error 就會(huì)生成一個(gè)編譯錯(cuò)誤提示消息,并停止編譯:

3.2 #line 預(yù)處理

#line 的作用時(shí)改變當(dāng)前行數(shù)和文件名稱,他們是在編譯程序中預(yù)先定義的標(biāo)識(shí)符。這里我就不給你們看運(yùn)行結(jié)果了,感興趣的可以復(fù)制代碼下去自行了解下哦:

int main()
{
	printf("%s, %d\n", __FILE__, __LINE__); //C預(yù)定義符號(hào),代表當(dāng)前文件名和代碼行號(hào)
#line 60 "hehe.h" //定制化完成
	printf("%s, %d\n", __FILE__, __LINE__);
	return 0;
}

本質(zhì)其實(shí)是可以定制化你的文件名稱和代碼行號(hào),很少使用!

3.3 #pragma 預(yù)處理

3.3.1 #pragma message

message 參數(shù)他能在編譯信息輸出窗口中輸出相應(yīng)的信息,這對(duì)于源代碼信息的控制是非常重要的。

#define TEST
int main()
{
#ifdef TEST
#pragma message("TEST macor activated!")
#endif
    return 0;
}

當(dāng)我們定義了 TEST 這個(gè)宏后,應(yīng)用程序在編譯時(shí)就會(huì)在編譯輸出窗口里顯示TEST macor activated! 因此我們就不會(huì)因?yàn)椴挥浀米约憾x的一些宏而著急了!

3.3.2 #pragma once

這個(gè)還是比較常用的,只要在頭文件的最開始加入這條指令就能夠保證頭文件被編譯一次,但是考慮到兼容性的問題,并沒有太多的使用。

3.3.3 #pragma warning

#pragma warning(disable : 4507 34; once : 4385; error : 164)
//等價(jià)于:
#pragma warning(disable : 4507 34) //不顯示 4507 和 34 號(hào)警告信息
#pragma warning(once : 4385)       //4385 號(hào)警告信息僅報(bào)告一次
#pragma warning(error : 164)       //把 164 號(hào)警告信息作為一個(gè)錯(cuò)誤

當(dāng)使用 windows vs 環(huán)境的小伙伴們,在使用庫函數(shù)的時(shí)候比如 scanf 會(huì)說這個(gè)函數(shù)不安全,推薦你使用 scanf_s,那我們要保證代碼可以移植性如何辦呢?通過查看報(bào)錯(cuò)發(fā)現(xiàn)是 4996 報(bào)錯(cuò),那我們則可以:

#pragma warning(disable : 4996) //這樣就解決問題了!

3.3.4 #pragma pack

設(shè)置結(jié)構(gòu)體內(nèi)存對(duì)齊,我們還沒更新到結(jié)構(gòu)體,加上用的并不算多,所以感興趣的可以先去自行研究哦。

3.4 # 和 ##

假設(shè)說我們今天定義了一個(gè)打印宏:

#define PRINT(x) printf("hello x is %d.\n", ((x)*(x)))

調(diào)用宏 PRINT(8); 則會(huì)輸出:hello x is 64.

如果你希望字符串中包含宏參數(shù),那我們就可以使用 "#",它可以把語言符號(hào)轉(zhuǎn)換成字符串:

#define PRINT(x) printf("hello "#x" is %d.\n", ((x)*(x)))

這樣調(diào)用PRINT(8); 則會(huì)輸出:hello 8is 64.

## 使用起來也很簡單,就是將兩個(gè)相連的符號(hào),連接成為一個(gè)符號(hào):

#define XNAME(n) x##n

如果這樣使用宏: XNAME(8)則會(huì)被展開成為:x8

在 "#" 或 "##" 預(yù)處理操作符相關(guān)的計(jì)算次序,如果未被指定則會(huì)產(chǎn)生問題,為了避免該問題,在單一的宏定義中只能使用其中一種操作符。除非是必須使用,否則盡量不適用這兩個(gè)預(yù)處理操作符!

到此這篇關(guān)于C語言零基礎(chǔ)徹底掌握預(yù)處理下篇的文章就介紹到這了,更多相關(guān)C語言預(yù)處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 匯編語言rep movsd 的使用詳解

    匯編語言rep movsd 的使用詳解

    rep movsd 每次ecx!=0便執(zhí)行movsd ,然后ecx=ecx-1 movsd移動(dòng)ds:[si] 到es:[di],在32位匯編下可以用esi代替si,edi代替di
    2013-09-09
  • C語言數(shù)據(jù)結(jié)構(gòu)之模式匹配字符串定位問題

    C語言數(shù)據(jù)結(jié)構(gòu)之模式匹配字符串定位問題

    這篇文章主要介紹了C語言數(shù)據(jù)結(jié)構(gòu)之模式匹配字符串定位問題的相關(guān)資料,希望通過本文能幫助到大家,讓大家理解這部分內(nèi)容,需要的朋友可以參考下
    2017-10-10
  • 基于C語言實(shí)現(xiàn)簡單的走迷宮游戲

    基于C語言實(shí)現(xiàn)簡單的走迷宮游戲

    這篇文章主要介紹了基于C語言實(shí)現(xiàn)簡單的走迷宮游戲,用到雙向隊(duì)列,方便在運(yùn)行完畢后輸出經(jīng)過的點(diǎn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-04-04
  • 基于getline()函數(shù)的深入理解

    基于getline()函數(shù)的深入理解

    本篇文章是對(duì)getline()函數(shù)的使用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • 詳解C++中普通舊數(shù)據(jù)(POD)的使用

    詳解C++中普通舊數(shù)據(jù)(POD)的使用

    普通舊數(shù)據(jù)就是內(nèi)存中的連續(xù)字節(jié)序列,是能夠被“僅當(dāng)作數(shù)據(jù)”處理的對(duì)象。這篇文章主要帶大家了解一下C++中普通舊數(shù)據(jù)的定義與使用,感興趣的可以了解下
    2023-03-03
  • Mygui中文換行問題解決方案

    Mygui中文換行問題解決方案

    相信大家解決了中文輸入后一定會(huì)遇到如何解決中文輸入的問題,中文輸入換行問題是很多gui框架都存在的一個(gè)問題,需要的朋友可以了解下
    2012-11-11
  • C語言由淺入深理解指針

    C語言由淺入深理解指針

    C語言這門課程在計(jì)算機(jī)的基礎(chǔ)教學(xué)中一直占有比較重要的地位,然而要想突破C語言的學(xué)習(xí),對(duì)指針的掌握是非常重要的,本文將具體針對(duì)指針的基礎(chǔ)做詳盡的介紹
    2022-05-05
  • C/C++內(nèi)存泄漏原因分析與應(yīng)對(duì)方法

    C/C++內(nèi)存泄漏原因分析與應(yīng)對(duì)方法

    內(nèi)存泄漏會(huì)導(dǎo)致當(dāng)前應(yīng)用程序消耗更多的內(nèi)存,使得其他應(yīng)用程序可用的內(nèi)存更少了,那么為什么會(huì)內(nèi)存泄漏,我們應(yīng)該怎樣應(yīng)對(duì)內(nèi)存泄漏,所以接下來就給大家詳細(xì)介紹一下C++內(nèi)存泄漏原因分析與應(yīng)對(duì)方法,需要的朋友可以參考下
    2023-07-07
  • 簡單談?wù)凜語言中的= 和==、!=

    簡單談?wù)凜語言中的= 和==、!=

    這篇文章主要給大家介紹了關(guān)于C語言中= 和==、!=的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • C++ Boost Exception超詳細(xì)講解

    C++ Boost Exception超詳細(xì)講解

    Boost是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱。Boost庫是一個(gè)可移植、提供源代碼的C++庫,作為標(biāo)準(zhǔn)庫的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開發(fā)引擎之一,是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱
    2022-11-11

最新評(píng)論

东乡族自治县| 北宁市| 普兰店市| 甘肃省| 玉门市| 进贤县| 恭城| 保靖县| 浙江省| 龙泉市| 绥棱县| 庄河市| 顺平县| 合江县| 大安市| 彰化市| 吴川市| 大埔县| 久治县| 北京市| 阳城县| 元江| 甘洛县| 沂源县| 达日县| 灵宝市| 永济市| 什邡市| 迁西县| 竹溪县| 太仓市| 偏关县| 福贡县| 宣化县| 剑川县| 西安市| 丽水市| 张家川| 诏安县| 肥乡县| 高淳县|