C語言:預處理

C語言:預處理

    • 預定義符號
    • #define
      • 定義常量
      • 定義宏
      • 宏與函數對比
    • #操作符
    • ##操作符
    • 條件編譯
    • 頭文件包含
      • 庫文件包含
      • 本地文件包含
      • 嵌套文件包含


預定義符號

C語?設置了?些預定義符號,可以直接使?,預定義符號也是在預處理期間處理的。

__FILE__ //進?編譯的源?件
__LINE__ //?件當前的?號
__DATE__ //?件被編譯的?期
__TIME__ //?件被編譯的時間
__STDC__ //如果編譯器遵循ANSI C,其值為1,否則未定義

示例:

printf("file:%s line:%d\n", __FILE__, __LINE__);

輸出結果:

file:test.c line:1

以上預定義符號,都是一些常量,可以自己一一嘗試。


#define

在C語言中,#define是一個預處理器指令,用于定義宏。

宏是一個被預處理器替換的標識符或一個標識符和參數的組合。宏定義可以用來簡化代碼、提高代碼的可讀性和可維護性。

使用#define可以定義常量、函數宏等。

定義常量

  • 定義常量:
 #define PI 3.14

這樣就可以在代碼中使用PI來代表3.14

  • 定義關鍵字:
#define reg register

reg 定義為關鍵字 register,可以將reg這個簡寫代替register關鍵字使用。

  • 定義代碼段
#define CASE break;case

正常的switch語句每一個case都要加上break,通過這個寫法,我們可以在寫CASE時自動補齊break


定義宏

#define 機制允許把參數替換到文本中,這種功能叫做 宏(macro) / 定義宏(define macro)

語法:

#define name(parament-list) stuff

其中的 parament-list 是?個由逗號隔開的符號表,它們可能出現在stuff中。

示例:

#define SQUARE(x) x * x

當我們在代碼中輸入以下代碼:

int main()
{int a = 5;int b = SQUARE(a);return 0;
}

在編譯后就會轉化為:

int main()
{int a = 5;int b = a * a;return 0;
}

也就是直接發生了文本替換,這種宏的形式非常像函數,因此也可以稱為宏函數。但是其也有很多需要注意的地方。

比如以下代碼:

int a = 5;
int b = SQUARE(a + 1);

我們希望先執行a + 1,然后再傳入SQUARE中,但是其不會這樣做因為其會將上述代碼直接替換為:

int a = 5;
int b = a + 1 * a + 1;

由于操作符優先級的問題,我們不會得到想要的結果。為了處理這個情況,我們需要把參數用小括號括起來:

#define SQUARE(x) (x) * (x)

代碼就變成:

int a = 5;
int b = (a + 1) * (a + 1);

這樣我們就可以行使預期的功能了。

那么我們再看到一串代碼:

#define DOUBLE(x) (x) + (x)int a = 5;
int b = 10 * DOUBLE(a);

代碼編譯后為:

int a = 5;
int b = 10 * 5 + 5;  

又出現了一樣的問題,我們的 10 * DOUBLE(a)并沒有先執行DOUBLE(a),而展開后,又出現了操作符優先級問題,所以我們的宏還要再優化:

#define DOUBLE(x) ((x) + (x))

在宏的最外側再加一層括號,就可以獨立運行,不受外界操作符影響了。

通過以上推斷,我們可以發現,宏雖然可以很好的替換代碼,但是會受到外界操作符的影響,此時就要注意很多細節。


宏與函數對比

在這里插入圖片描述
函數在調用時,是會開辟內存創建棧幀的,而宏則直接執行,所以速度更快。但是由于宏是在編譯階段就已經處理好了,所以宏不能通過調試觀察現象,還要操作符優先級帶來的種種問題。因此宏不適合處理復雜的函數,但是很適合短小簡單的函數


#操作符

功能:

#可以將宏的參數轉化為字符串

比如以下代碼:

#define PRINT(n) printf("the value of "#n " is %d", n);

我們嘗試調用:

int a = 5;
PRINT(a);

代碼就會被轉化為:

int a = 5;
printf("the value of ""a" " is %d", a);

可以看到,兩個n的替換效果是不同的,對于n其會直接被替換為變量a;而對于#n,其不是簡單的替換,而是把參數名轉化為了字符串”a“


##操作符

##操縱符可以將兩個符號合并為一個符號

示例:

#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ \
return (x>y?x:y); \
}

該宏用于創建不同類型的比大小函數,由于不同函數需要不同的函數名,于是利用##來連接函數名,也就是type##_max部分。type是一個宏參數,當傳入floattype##_max整體就被連接為float_max,當傳入int,整體就被連接為int_max。也就是##起到一個連接作用。


條件編譯

條件編譯是C語言中的一種編譯指令,用于在編譯過程中根據指定的條件選擇性地包含或排除某些代碼。它主要是為了滿足不同平臺、不同編譯選項或不同場景下的需求。

條件編譯使用預處理指令實現,預處理指令以#開頭。下面是一些常用的條件編譯指令及其用法:

  1. #if / #elif / #else / #endif
    #if用于基于預處理器常量的值進行條件判斷。
    #elif用于在多個條件之間進行選擇。
    #else用于在沒有匹配的#if#elif時執行。
    #endif用于結束條件編譯塊。
    示例:
   #define NUM 5#if NUM > 10printf("NUM is greater than 10\n");#elif NUM > 0printf("NUM is greater than 0\n");#elseprintf("NUM is less than or equal to 0\n");#endif

這個代碼和C語言的if代碼很像,不過多講解了。

  1. #ifdef / #ifndef
    #ifdef用于檢查一個標識符是否已經定義,如果已定義則編譯后面的代碼,否則跳過。
    #ifndef#ifdef相反,用于檢查一個標識符是否未定義。
    示例:
#ifdef DEBUGprintf("Debug mode enabled\n");
#endif

以上代碼中,只要我們定義了DEBUG這個變量,就會輸出"Debug mode enabled\n"語句。

  1. #define
    #define用于定義宏。宏是一種將一組指令作為一個整體進行替換的方式。
    示例:

    #define MAX(a, b) ((a) > (b) ? (a) : (b))int x = 10;
    int y = 20;
    int max = MAX(x, y);
    
  2. #include
    #include用于將指定的頭文件包含到當前文件中。
    示例:

    #include <stdio.h>int main() {printf("Hello, World!\n");return 0;
    }
    
  3. #pragma
    #pragma用于向編譯器發出特定的指令,如優化選項、警告控制等。它的語法和功能因編譯器而異。
    示例:

    #pragma warning(disable: 4996)
    

這些是C語言中常用的條件編譯指令和代碼用法。通過合理使用條件編譯,我們可以根據不同的需求自由地控制代碼的編譯過程。


頭文件包含

頭文件包含分兩種形式:本地頭文件庫文件

庫文件包含

語法:

#include <filename.h>

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

我們平常使用的庫文件都通過尖括號<>來包含,其會直接到存放庫文件的路徑中查找。

本地文件包含

#include "filename"

先在源?件所在?錄下查找,如果該頭?件未找到,編譯器就像查找庫函數頭?件?樣在標準位置查找頭?件,如果找不到就提?編譯錯誤

如果是用戶自己編寫的頭文件,我們要用雙引號""包含,如果通過這種方式包含頭文件,那么會先在當前源文件的目錄下查找,如果沒有找到,再去庫文件中查找

也就是說:庫文件也可以通過雙引號包含,但是會多出額外的查找步驟,所以庫文件還是用尖括號包含更好,而自己編寫的頭文件必須雙引號包含。

嵌套文件包含

假設我們現在有以下文件結構:
頭文件test.h

void test();
struct Stu
{int id;char name[20];
};

源文件test.c

#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
int main()
{return 0;
}

我們在test.c中多次包含了頭文件,這就會導致test.h反復被展開,產生大量重復代碼。這就是嵌套文件包含的問題,那么我們要如何處理這個問題,讓其只能被包含一次呢?

條件編譯方法
在頭文件test.h中加入以下代碼:

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

第一次包含頭文件:
先執行#ifndef __TEST_H__,我們此時沒有定義__TEST_H__這個變量,if成立,此時頭文件會被展開,同時執行#define __TEST_H__,此時__TEST_H__就已經被定義了


第二次包含頭文件:
第二次站時,由于上一次展開已經定義了__TEST_H__這個變量,導致#ifndef __TEST_H__判斷為假,此時整個頭文件都不會再被編譯,直接舍棄

后續再展開頭文件,都會因為 __TEST_H__被定義而不會編譯,解決了嵌套編譯的問題。

一般而言,我們這個用于判斷頭文件有沒有被展開過的變量,是頭文件名通過一定規則轉化來的:

  1. 在頭文件前后加上兩個下劃線__頭文件.h__
  2. 把頭文件中的點.也改為下劃線__頭文件_h__

因此test.h的常量就是:__TEST_H__

pragma
通過條件編譯其實是比較傳統的寫法,我們還有一種更加簡潔方便的寫法:

#pragma once

只要在任何頭文件前面加上這句話,就只會被編譯一次了。


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

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

相關文章

多智能體強化學習簡介

基礎概念 什么是多智能體系統 多智能體系統&#xff08;Multi-Agent System&#xff0c;MAS&#xff09;是由多個自主智能體組成的系統。這些智能體可以協同工作&#xff0c;也可以獨立行動&#xff0c;以實現各自的目標。在多智能體系統中&#xff0c;每個智能體都有自己的決…

在你的 Vue + Electron 項目里,引入 ESLint

因為我的項目是基于 Electron 平臺的 Web 應用&#xff0c;使用 Vue 3 實現&#xff0c;而且用了 TypeScript&#xff0c;所以&#xff0c;在引入 ESLint 的時候&#xff0c;要考慮好幾種規范的問題。 文章目錄 零、簡介1. 規則2. 配置文件3. 共享配置4. 插件5. 解析器6. 自定義…

Vue開發實例(九)動態路由實現左側菜單導航

之前在【Vue開發實例&#xff08;六&#xff09;實現左側菜單導航】文中實現了菜單的導航&#xff0c;本篇是在那個基礎上改造的。 動態路由實現左側菜單導航 一、動態菜單創建二、根據菜單數據來創建路由三、添加路由已加載標記&#xff0c;省的每次點擊菜單都要加載 一、動態…

2021 年 3 月青少年軟編等考 C 語言一級真題解析

目錄 T1. 字符菱形思路分析 T2. 與圓相關的計算思路分析 T3. 蘋果和蟲子 2思路分析 T4. 奇數求和思路分析 T5. 藥房管理思路分析 T1. 字符菱形 給定一個字符&#xff0c;用它構造一個對角線長 5 5 5 個字符&#xff0c;傾斜放置的菱形。 時間限制&#xff1a;1 s 內存限制&a…

3、云原生安全之falco的部署

文章目錄 1、helm安裝2、拉去鏡像失敗與解決3、安裝faclo4、安裝nfs服務器,配置k8s的持久卷4.1、創建nfs服務器,4.2、部署master節點(nsf服務的客戶端)4.3、pv與pvc4.4、假設pv和pvc的配置文件出錯了5、安裝falcosidekick可視化(建議跳過,直接使用6)6、安裝faclo與falco…

【設計模式 01】單例模式

單例模式&#xff0c;是一種創建型設計模式&#xff0c;他的核心思想是保證一個類只有一個實例&#xff08;即&#xff0c;在整個應用程序中&#xff0c;只存在該類的一個實例對象&#xff0c;而不是創建多個相同類型的對象&#xff09;&#xff0c;并提供一個全局訪問點來訪問…

java012 - Java集合基礎

1、集合基礎 1.1 集合概述 引用數據類型包括&#xff1a;類、接口、數組[] 1.2 ArrayList構造和添加方法 代碼&#xff1a; 空集合對象&#xff1a;[] add() add(int index,E element): 1.3 ArrayList集合常用方法

計算機體系結構安全:對體系結構如何支持安全機制進行調研

一、體系結構支持信任建立和主動防御的技術&#xff1a; 可信3.0 二、體系結構怎么更好的支持信任建立和主動防御 2.1 支持信任建立 一、以手機芯片舉例&#xff0c;用智能手機的芯片作為信任根&#xff0c;確保應用程序和敏感數據受到保護。 二、啟動時驗證操作系統和應用…

Stable Diffusion 模型分享:Henmix_Real(人像、真實、寫真、亞洲面孔)

本文收錄于《AI繪畫從入門到精通》專欄,專欄總目錄:點這里。 文章目錄 模型介紹生成案例案例一案例二案例三案例四案例五案例六案例七案例八下載地址模型介紹 作者述:這個模型試圖改

深入理解算法的空間復雜度

算法一&#xff1a;逐步遞增型 void Loveyou(int n)//n為問題規模 {int i1;while(i<n){i;printf("I love you %d\n",i);}printf("I love you more than %d\n",n);//5 } int main() {Loveyou(3000);return 0; } 無論問題規模怎么變&#xff0c;算法運行…

Springboot教程(五)——單元測試

idea中一般使用JUnit進行單元測試 基本使用 我們可以在idea的test文件夾下的XXXXApplicationTests內進行單元測試&#xff1a; 可以在Test標注的方法上寫測試代碼&#xff1a; SpringBootTest class C0101ApplicationTests {Testfun contextLoads() {println("Hello …

基礎二分學習筆記

模板 : 個人傾向第一種 ; 整數二分 : 最大化查找 : 可行區域在左側 : 查找最后一個<q的數的下標 : int find(int q){// 查找最后一個 < q 的下標 int l 0 , r n 1 ;while(l 1 < r){int mid l r >> 1 ;if(a[mid]<q) l mid ;else r mid ;}return…

django settings.py STATICFILES_FINDERS 設置

STATICFILES_FINDERS 定義查找器后端以確保Django能夠正確地定位和提供靜態文件是很重要的. Django中的STATICFILES FINDERS設置是一個inder后端列表&#xff0c;它知道如何在不同的位置定位靜態文件。 它被Django的靜態文件處理系統用來在開發和部署過程中查找和收集靜態文件…

js json轉換成字符串

js中JSON數據轉換成字符串&#xff0c;可以使用JSON.stringify()方法。 var obj {name: "張三", age: 18, gender: "男"}; var jsonString JSON.stringify(obj); console.log(jsonString); // 輸出 {"name":"張三","age"…

土壤類型數據

國家地球系統科學數據中心

AGM CPLD (AGRV2K )的時鐘(外部時鐘和片上內部振蕩器)

AGM CPLD &#xff08;AGRV2K &#xff09;的時鐘(外部時鐘和片上內部振蕩器) 外部晶振 與 內部振蕩器&#xff1a; mcu 和 cpld 聯合編程時&#xff0c; 整顆芯片需要一顆外部晶振。 &#xff08;芯片有內部振蕩器&#xff0c; 但誤差較大&#xff0c; 校準后 5%以內誤差&…

216. 組合總和 III(力扣LeetCode)

文章目錄 216. 組合總和 III回溯算法 216. 組合總和 III 找出所有相加之和為 n 的 k 個數的組合&#xff0c;且滿足下列條件&#xff1a; 只使用數字1到9每個數字 最多使用一次 返回 所有可能的有效組合的列表 。該列表不能包含相同的組合兩次&#xff0c;組合可以以任何順序…

Electron通過預加載腳本從渲染器訪問Node.js

問題&#xff1a;如何實現輸出Electron的版本號和它的依賴項到你的web頁面上&#xff1f; 答案&#xff1a;在主進程通過Node的全局 process 對象訪問這個信息是微不足道的。 然而&#xff0c;你不能直接在主進程中編輯DOM&#xff0c;因為它無法訪問渲染器 文檔 上下文。 它們…

【軟考】數據庫的三級模式

目錄 一、概念1.1 說明1.2 數據庫系統體系結構圖 二、外模式三、概念模式四、內模式 一、概念 1.1 說明 1.數據的存儲結構各不相同&#xff0c;但體系結構基本上具有相同的特征&#xff0c;采用三級模式和兩級鏡像 2.數據庫系統設計員可以在視圖層、邏輯層和物理層對數據進行抽…

matplotlib散點圖

matplotlib散點圖 假設通過爬蟲你獲取到了北京2016年3, 10月份每天白天的最高氣溫(分別位于列表a, b), 那么此時如何尋找出氣溫和隨時間(天)變化的某種規律? from matplotlib import pyplot as pltx_3 range(1, 32) x_10 range(51, 82)y_3 [11,17,16,11,12,11,12,6,6,7,8…