網站首頁 編程語言 正文
前言
C語言中源代碼到可執行文件的第一階段,也就是預處理階段,會檢查源文件中的預處理指令語句和宏定義,并對源代碼進行相應的替換,預處理過程還會刪除程序中的注釋和多余的空白符號。
預處理指令是以#開頭的代碼行,#必須是該行除了空白符外的第一個字符,#后是指令關鍵字,在#和指令關鍵字之間允許存在若干個空白字符,define是宏定義命令。在C語言程序中允許用一個標識符來表示一個字符串,稱為“宏”,“宏”又分為有參和無參,有參又稱為“宏函數”,被定義為“宏”的標識符稱為“宏名”。
#define 定義宏(無參)
語法規定:
#define name stuff
name:標識符名\宏名
stuff:可以是關鍵字、常量、關鍵字、標識符、標點符號、運算符,表達式
在預處理階段,編譯器會在程序中使用#define定義的標識符替換成stuff,可以通過預處理生成的.i文件查看效果。
//stuff是數值常量
#define NUM 10
//stuff是關鍵字
#define reg register
//stuff是標點符號
#define GREATER_THAN >
//stuff的更多表達方式
#define do_forever for(;;)
//若定義的stuff過長,可以分成幾行寫,除了最后一行外,每行的后面都加一個\(續行符)
#define DBBUG_PRINT printf("file:%s\tline:%d\t \
date:%s\ttime:%s\n",\
__FILE__,__LINE__,\
__DATE__,__TIME__)
總結:#define定義的name(宏名),在預編譯階段會將所有的宏名替換成stuff,stuff內容被替換到源代碼中。稱為“宏代換”或“宏展開”。
注意:define定義的標識符的時候,后面加上“;”會將“;”認為是stuff中的內容。
#define 定義宏函數
宏函數的申明方式:#define name(parament-list) stuff
parament-list:參數列表
?注意:參數列表的左括號必須與name緊鄰,如果兩者之間有空白存在,參數列表就會被解釋為stuff的部分。
宏函數存在的問題1
#include <stdio.h>
#define SQUARE(x) x*x//定義一個宏函數求平方
int main()
{
int x = SQUARE(3+1);//替換后x的計算結果是多少?答案是:7
}
?為什么呢?
在給宏函數傳參時,如果傳遞的是一個表達式,不會先計算表達式的結果再進行傳參,而是直接將表達式整體作為參數傳遞。
那么如何防止發生這樣的情況呢?+()
?宏函數存在的問題2
#include <stdio.h>
#define SUM(x,y) (x)+(y)
int main()
{
int a = 10;
int b = 5;
int c = SUM(a,b)*2;//替換后c的結果為20,why
return 0;
}
我們看看替換后的結果
?這又該如何解決呢?
?總結:在對數值表達式進行求值的宏定義應該用這兩種方式加上括號,避免在使用宏參數的操作符或鄰近操作符之間不可預料的相互作用。?
?#define替換規則:
1.在使用宏函數時,首先對參數進行檢查,看看參數中是否包含任何#define定義的標識符,如果有,他們首先被替換。
2.替換的內容被插入到源文件原來的位置。對于宏函數,參數名被他們的值替換
宏的更多規則特性
1.宏名一般用大寫
2.使用宏可提高程序的通用性和易讀性,便于修改。
3.宏定義末尾不加分號
4.宏定義寫在函數的大括號外面,作用域為其后的程序,通常放在開頭
5.宏函數不可遞歸
6.宏定義不分配內存,變量定義分配內存
7.字符串" "中永遠不包含宏
8.宏定義不存在類型問題,他的參數也沒有類型
宏的缺點
1.宏不能調試
2.宏由于與類型無關,不夠嚴謹
3.宏可能帶來運算符優先級的問題,導致容易出錯
常見預處理指令
#define:宏定義
#undef:撤銷已經定義過的宏名
#include:將另一個源文件嵌入到#include源文件中
#if~#endif:如果#if后面的常量表達式為真,則編譯#if~#endif之間的代碼,如果為假,跳過這些代碼不編譯。
#if~#elif~#else~#endif:和if~else if~else類似,可以建立更分支。
#ifdef symbol~endif:判斷是否被定義,定義了編譯他們之間內容
#ifndef symbol~endif:判斷是否被定義,沒定義編譯他們之間的內容
#line:改變當前行數和文件名稱,是在編譯程序中預先定義的標識符命令的基本形式:#line number["filename"]
#error:編譯程序時,只要遇到#error就會生成一個編譯錯誤的提示信息,并停止編譯。
#pragma:?可以設定編譯程序完成一些特點的動作(可以通過編譯程序的菜單中設置),可以向編譯程序傳送各種指令。
原文鏈接:https://blog.csdn.net/qq_52763385/article/details/124136790
相關推薦
- 2023-01-05 Python中glob類的使用方法_python
- 2022-05-31 Python中的變量及簡單數據類型應用_python
- 2022-01-25 win10 更換JDK后 查詢JDK路徑還是原路徑怎么辦?
- 2023-12-18 Path環境變量點編輯無法展開
- 2022-09-18 Go語言實現文件上傳_Golang
- 2022-08-22 C++實現字符串切割的兩種方法_C 語言
- 2022-07-16 List轉String的簡單方法
- 2022-01-18 移動端自適應布局(viewport+rem)
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支