程序環境和預處理(2)

文章目錄

        • 3.2.7 命名約定
      • 3.3 #undef
      • 3.4 命令行定義
      • 3.5 條件編譯
      • 3.6 文件包含
        • 3.6.1 頭文件被包含的方式
        • 3.6.2 嵌套文件包含
    • 4. 其他預處理指令

3.2.7 命名約定

一般來講函數的使用語法很相似,所以語言本身沒法幫我們區分二者,那我們平時的一個習慣是:

把宏名全部大寫
函數名不要全部大寫

#define MAX(x, y) ((x)>(y)?(x):(y))int Max(int x, int y)
{return x > y ? x : y;
}//有一個特例
//offsetof - 宏 - 全小寫int main()
{return 0;
}

3.3 #undef

這條指令用于移除一個宏定義。

#undef NAME
//如果現存的一個名字需要被重新定義,那么它的舊名字首先要被移除。

#include <stdio.h>#define MAX(x, y) ((x)>(y)?(x):(y))int Max(int x, int y)
{return x > y ? x : y;
}int main()
{int c = MAX(3, 5);printf("%d\n", c);
#undef MAXc = MAX(5, -5);//編譯器會報錯:“MAX”未定義printf("%d\n", c);return 0;
}

3.4 命令行定義

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

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

注:VS環境無法演示,gcc來演示

#include <stdio.h>int main()
{int arr [SZ];int i = 0;for(i = 0; i < SZ; i++){arr[i] = i + 1;}for(i = 0; i < SZ; i++){printf("%d " , arr[i]);}return 0;
}

命令行定義

3.5 條件編譯

在編譯一個程序的時候我們如果要將一條語句(一組語句)編譯或者放棄是很方便的,因為我們有條件編譯指令。

比如我們要在不同的操作系統上執行代碼,那么就要執行不同的代碼,我們就可以使用條件編譯,滿足相應的條件,就編譯相應的代碼,另外一份代碼就不會被編譯了。

常見的條件編譯指令:

#if 常量表達式
//…
#endif
//常量表達式由預處理器求值。

#include <stdio.h>#define M 0int main()
{
#if 1 == M  //#if 后面的語句為真,就參與編譯;否則就不參與編譯printf("hehe\n");
#endifreturn 0;
}

條件編譯例子的預處理
注: #if 條件編譯不滿足是不編譯;而 if 條件語句不滿足是不執行,但是它的代碼運行時是存在的。

  1. 多個分支的條件編譯

#if 常量表達式
//…
#elif 常量表達式
//…
#else
//…
#endif

#include <stdio.h>#define M 0int main()
{
#if 1 == Mprintf("hehe\n");
#elif 2 == Mprintf("haha\n");
#elseprintf("heihei\n");
#endifreturn 0;
}

條件編譯例子的預處理(2)

  1. 判斷是否被定義

#if defined(symbol)
#ifdef symbol

#if !defined(symbol)
#ifndef symbol

#include <stdio.h>#define WIN 0int main()
{
#if defined(WIN)printf("windows");
#endifreturn 0;
}

條件編譯例子的預處理(3)
也可以這樣寫:

#include <stdio.h>#define WIN 0int main()
{
#ifdef WINprintf("windows");
#endifreturn 0;
}

#include <stdio.h>#define WIN 0int main()
{
#if !defined(WIN)printf("windows\n");
#endifreturn 0;
}

條件編譯例子的預處理(4)
也可以這樣寫:

#include <stdio.h>#define WIN 0int main()
{
#ifndef WINprintf("windows\n");
#endifreturn 0;
}
  1. 嵌套指令
#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#endif

我們在 stdio.h 這個頭文件中就能看到條件編譯的應用:
stdio.h中條件編譯的應用


有些人會將條件編譯當注釋來用:

#if 0
int main()
{return 0;
}
#endif#include <stdio.h>int main()
{printf("hehe\n");return 0;
}

3.6 文件包含

我們已經知道, #include 指令可以使另外一個文件被編譯,就像它實際出現于 #include 指令的地方一樣。

這種替換的方式很簡單:

預處理器先刪除這條指令,并用包含文件的內容替換。

這樣一個源文件被包含10次,那就實際被編譯10次。

3.6.1 頭文件被包含的方式
  • 本地文件包含

#include “filename”

查找策略:先在源文件所在目錄下查找,如果該頭文件未找到,編譯器就像查找庫函數頭文件一樣在標準位置查找頭文件,如果找不到就提示編譯錯誤。

Linux環境的標準頭文件的路徑:

/usr/include

VS環境的標準頭文件的路徑:

C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include
//這是VS2013的默認路徑

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


  • 庫文件包含

#include <filename.h>

查找頭文件直接去標準路徑下去查找,如果找不到就提示編譯錯誤。

這樣是不是可以說,對于庫文件也可以使用 “” 的形式包含?

答案是肯定的,可以

但是這樣做查找的效率就低些,當然這樣也不容易區分是庫文件還是本地文件了。

3.6.2 嵌套文件包含

如果出現這樣的場景:
嵌套文件包含
comm.h和comm.c是公共模塊。

test1.h和test1.c使用了公共模塊。

test2.h和test2.c使用了公共模塊。

test.h和test.c使用了test1模塊和test2模塊。

這樣最終程序中就會出現兩份comm.h的內容。這樣就造成了文件內容的重復。

例子如下:
重復包含頭文件例子

如何解決這個問題?

答案:條件編譯。

每個頭文件的開頭寫:

#ifndef __TEST_H__
#define __TEST_H__
//頭文件的內容
#endif   //__TEST_H__

或者:

#pragma once

就可以避免頭文件的重復引入。

例子:

//test.h#ifndef __TEST_H__
#define __TEST_H__int Add(int x, int y);#endif

或者這樣寫:

//test.h#pragma onceint Add(int x, int y);

4. 其他預處理指令

#error
#pragma
#line

不做介紹,自己去了解。
#pragma pack()在結構體部分介紹。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/718348.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/718348.shtml
英文地址,請注明出處:http://en.pswp.cn/news/718348.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

linux條件判斷之if-then

if..then是最常見的條件判斷語句&#xff0c;簡而言之&#xff0c;就是當符合某個條件判斷的時候&#xff0c;就予以進行某項工作。 1.if-then格式 if-then格式1&#xff1a; if [ 條件判斷表達式 ];then 當條件判斷表達式成立時&#xff0c;需執行的命令 fi if-then格式2…

Redis安全加固策略:綁定Redis監聽的IP地址 修改默認端口 禁用或者重命名高危命令

Redis安全加固策略&#xff1a;綁定Redis監聽的IP地址 & 修改默認端口 & 禁用或者重命名高危命令 1.1 綁定Redis監聽的IP地址1.2 修改默認端口1.3 禁用或者重命名高危命令1.4 附&#xff1a;redis配置文件詳解&#xff08;來源于網絡&#xff09; &#x1f496;The Beg…

驅動開發面試復習

創建字符設備 1 創建設備號 alloc_chrdev_region 2.創建cdev cdev_init 3.添加一個 cdev,完成字符設備注冊到內核 cdev_add 4.創建類 class_create 5.創建設備 device_create 1.內核空間與用戶空間數據 copy_from_user 和copy_to_user 倆個函數來完成。 copy_from_user 函數…

618快遞準點到達,別忘了感謝它!

進入6月以來&#xff0c;全國快遞日均業務量飛速上漲。 雖然618大促是電商的主場&#xff0c;但作為不可或缺的物流環節&#xff0c;為了這場年中大考&#xff0c;快遞企業在此期間也使盡渾身解數&#xff0c;競相比拼配送速度。那么&#xff0c;為了更快的時效&#xff0c;快遞…

uniapp 的video播放如何實現小窗功能

在頁面中使用<video>組件來展示視頻&#xff0c;并設置好相應的屬性和事件監聽&#xff1a; <video src"video.mp4" play"onVideoPlay" pause"onVideoPause"></video>在頁面的data中定義一個變量來表示是否開啟小窗模式&#…

【Wio Terminal】使用WiFi(3)- Wi-F的高級使用

使用WiFi&#xff08;3&#xff09; Wi-F的高級使用HTTPClient 的使用HTTP GETHTTPs GETHTTP POSTWebServerHTTP Authentication Web ServerDNSServermDNSmDNS-SDWiFiManager Wi-F的高級使用 本節介紹了一些WiFi的高級庫用法&#xff0c;如HTTPClient、DNSServer和WebServer庫…

美國亞利桑那州立大學宣布與OpenAI建立合作伙伴關系!

美國亞利桑那州立大學 (Arizona State University) 在官網宣布—— 將與OpenAI建立合作伙伴關系&#xff01; 該校也成為了第一個與OpenAI合作的高等教育機構。 來源&#xff1a;亞利桑那州立大學官網 亞利桑那州立大學校長表示&#xff1a; “我們認識到人工智能系統將持續…

高并發IO底層原理淺析(四)

Java NIO中的Selector&#xff08;選擇器&#xff09;是一個用于檢測多個非阻塞通道&#xff08;Channel&#xff09;是否準備就緒進行讀寫操作的關鍵組件&#xff0c;它實現了I/O多路復用技術。在單個線程中&#xff0c;Selector可以監聽和管理多個Channel上的事件&#xff0c…

Redis安全加固策略:配置文件權限設置 配置本地日志存儲目錄 連接超時時間限制

Redis安全加固策略&#xff1a;配置文件權限設置 & 配置本地日志存儲目錄 & 連接超時時間限制 1.1 配置文件權限設置1.2 配置本地日志存儲目錄1.3 連接超時時間限制 &#x1f496;The Begin&#x1f496;點點關注&#xff0c;收藏不迷路&#x1f496; 1.1 配置文件權限…

如何理解template<size_t N>?

template<size_t N> 是C中的模板參數&#xff0c;用于定一個非類型參數的值。它可以在編譯時確定&#xff0c;并且可以在編譯時進行計算和使用。 例如&#xff0c;我們可以定義一個模板函數&#xff0c;接受一個大小為N的數組作為參數&#xff1a; template<size_t …

Qt/事件分發器/事件過濾器

事件分發器 //事件分發器bool event(QEvent* e); //事件分發器&#xff1a;&#xff1a;用途 分發事件 bool MyLabel::event(QEvent* e) {if(e->type() QEvent::MouseButtonPress){//如果是鼠標摁下 攔截事件 不向下分發QMouseEvent* ev static_cast<QMouseEvent*>…

Qt-Qss 樣式表屬性大全

目錄 前言 源碼文件信息 QSS屬性大全 1.控件常用的屬性 2.控件常用屬性值 3.控件常用狀態 4.其他 前言 相信很大一部分開發者真的被Qt樣式表的屬性搞挺頭痛的&#xff01; 一方面Qss的示例Demo太過簡單&#xff0c; 另一方面&#xff0c;Qss的所有屬性在Qt的官方文…

軟考高級:逆向工程相關概念和例題

作者&#xff1a;明明如月學長&#xff0c; CSDN 博客專家&#xff0c;大廠高級 Java 工程師&#xff0c;《性能優化方法論》作者、《解鎖大廠思維&#xff1a;剖析《阿里巴巴Java開發手冊》》、《再學經典&#xff1a;《Effective Java》獨家解析》專欄作者。 熱門文章推薦&am…

Deeplearning4j【基礎 01】初識Java深度學習框架DL4J

初識Java深度學習框架DL4J 1.起因2.簡介3.組件3.1 Deeplearning4j/ScalNet3.1.1 Deeplearning4jf&#xff08;Java&#xff09;3.1.2 ScalNet&#xff08;Scala&#xff09; 3.2 ND4J/LibND4J3.3 SameDiff3.4 DataVec3.5 Arbiter3.6 RL4J 4.總結 內容來自網絡&#xff0c;基于官…

Redis--線程模型詳解

Redis線程模型 Redis內部使用的文件事件處理器&#xff08;基于Reactor模式開發的&#xff09;file event handler是單線程的&#xff0c;所以Redis線程模型才叫單線程模型&#xff0c;它采用IO多路復用機制同時監聽多個socket&#xff0c;當被監聽的socket準備好執行accep、r…

072:vue+cesium 實現下雪效果

第072個 點擊查看專欄目錄 本示例的目的是介紹如何在vue+cesium中實現下雪效果,這里使用著色器來實現實例特效。 直接復制下面的 vue+cesium源代碼,操作2分鐘即可運行實現效果. 文章目錄 示例效果配置方式示例源代碼(共120行)著色代碼實現心得:專欄目標示例效果

Python世界之附加

一、數據類型轉換 1.隱式類型轉換 在隱式類型轉換中&#xff0c;Python 會自動將一種數據類型轉換為另一種數據類型。 對兩種不同類型的數據進行運算&#xff0c;較低數據類型就會轉換為較高數據類型以避免數據丟失。 2.顯式類型轉換 在顯式類型轉換中&#xff0c;用戶將對…

Linux設置靜態IP地址

在Linux系統中設置靜態IP地址的步驟如下&#xff1a; 1.使用管理員權限登錄系統。 2.編輯網卡配置文件&#xff1a; 網卡配置文件通常位于 /etc/sysconfig/network-scripts/ 目錄下&#xff0c;可以使用命令 cd /etc/sysconfig/network-scripts 切換到該目錄。 …

Wifi技術知識

參考文章 https://www.163.com/dy/article/FDFT60T70550I80C.html https://www.zhihu.com/tardis/bd/art/485711752?source_id1001 wifi代數 以前大家看電腦手機路由器參數&#xff0c;WiFi都是802.11a/b/n/g/ac/ax&#xff0c;這學名讀起來有些麻煩&#xff0c;現在好了&…

第12屆智能計算與無線光通信國際會議(ICWOC 2024)即將召開!

2024年第12屆智能計算與無線光通信國際會議&#xff08;ICWOC 2024&#xff09;將于2024年6月21-23日在中國重慶召開。隨著深度學習等人工智能技術的不斷進步&#xff0c;以自動化、自治為特征的智能應用預計將激增。本屆會議主題為“光通信智能鏈接”&#xff0c;旨在為相關技…