網站首頁 編程語言 正文
一、C語言中的宏定義
- #define是預處理器處理的單元實體之一
- #define 定義的宏可以出現在程序的任意位置
- #define 定義之后的代碼都可以使用這個宏
- #define 定義的宏常量可以直接使用
- #define 定義的宏常量本質為字面量
下面的宏常量定義正確嗎?
編寫代碼來測試:
#define ERROR -1
#define PATH1 "D:\test\test.c"
#define PATH2 D:\test\test.c
#define PATH3 D:\test\
test.c
int main()
{
int err = ERROR;
char* p1 = PATH1;
char* p2 = PATH2;
char* p3 = PATH3;
}
先使用gcc -E Test.c -o Test.i 進行預編譯,預編譯沒有報錯,結果如下:
# 1 "Test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "Test.c"
int main()
{
int err = -1;
char* p1 = "D:\test\test.c";
char* p2 = D:\test\test.c;
char* p3 = D:\testtest.c;
}
直接進行編譯,發現 char* p2 = PATH2; char* p3 = PATH3; 報錯
這說明宏定義是正確的,但是編譯是過不了的,只是
#define PATH2 D:\test\test.c
#define PATH3 D:\test\
不符合語法規范。
二、宏定義表達式
- #define 表達式的使用類似函數調用
- #define 表達式可以比函數更強大
- #define 表達式比函數更容易出錯
強大之處其中之一就是可以求數組的大小,這是不能編寫函數辦到的。
下面看一段宏表達式的代碼:
#include <stdio.h>
#define _SUM_(a, b) (a) + (b)
#define _MIN_(a, b) ((a) < (b) ? (a) : (b))
#define _DIM_(a) sizeof(a)/sizeof(*a)
int main()
{
int a = 1;
int b = 2;
int c[4] = {0};
int s1 = _SUM_(a, b);
int s2 = _SUM_(a, b) * _SUM_(a, b);
int m = _MIN_(a++, b);
int d = _DIM_(c);
printf("s1 = %d\n", s1);
printf("s2 = %d\n", s2);
printf("m = %d\n", m);
printf("d = %d\n", d);
return 0;
}
下面為輸出結果,但是 s2 我們預期的結果應該是 9,m 的值我們預期的結果應該是 1,這是怎么回事呢?
下面進行預編譯看看代碼到底是怎么運行的,輸入 gcc -E Test.c -o Test.i
int main()
{
int a = 1;
int b = 2;
int c[4] = {0};
int s1 = (a) + (b);
int s2 = (a) + (b) * (a) + (b);
int m = ((a++) < (b) ? (a++) : (b));
int d = sizeof(c)/sizeof(*c);
printf("s1 = %d\n", s1);
printf("s2 = %d\n", s2);
printf("m = %d\n", m);
printf("d = %d\n", d);
return 0;
}
通過上面宏定義的替換,我們很容易知道為什么結果跟我們想的不一樣。
三、宏表達式與函數的對比
- 宏表達式被預處理器處理,編譯器不知道宏表達式的存在
- 宏表達式用“實參”完全替代形參,不進行任何運算
- 宏表達式沒有任何的“調用”開銷
- 宏表達式中不能出現遞歸定義
所以,下面遞歸定義就是錯誤的:
四、有趣的問題
宏定義的常量或表達式是否有作用域限制?(沒有)
下面看一個宏作用域分析的代碼:
#include <stdio.h>
void def()
{
#define PI 3.1415926
#define AREA(r) r * r * PI
}
double area(double r)
{
return AREA(r);
}
int main()
{
double r = area(5);
printf("PI = %f\n", PI);
printf("d = 5; a = %f\n", r);
return 0;
}
下面為輸出結果:
作用域的概念是針對 C 語言中的變量和函數,不針對宏。宏表達式被預處理器處理,編譯器不知道宏表達式的存在。
五、強大的內置宏
宏 | 含義 | 示例 |
_FILE_ | 被編譯的文件名 | file1.c |
_LINE_ | 當前行號 | 25 |
_DATE_ | 編譯時的日期 | Jan 31 2021 |
_TIME_ | 編譯時的時間 | 17:01:01 |
_STDC_ | 編譯器是否遵循標準C規范 | 1 |
下面看一個宏使用的綜合示例:
#include <stdio.h>
#include <malloc.h>
#define MALLOC(type, x) (type*)malloc(sizeof(type)*x)
#define FREE(p) (free(p), p=NULL)
#define LOG(s) printf("[%s] {%s:%d} %s \n", __DATE__, __FILE__, __LINE__, s)
#define FOREACH(i, m) for(i=0; i<m; i++)
#define BEGIN {
#define END }
int main()
{
int x = 0;
int* p = MALLOC(int, 5);
LOG("Begin to run main code...");
FOREACH(x, 5)
BEGIN
p[x] = x;
END
FOREACH(x, 5)
BEGIN
printf("%d\n", p[x]);
END
FREE(p);
LOG("End");
return 0;
}
下面為輸出結果:
可以看到宏定義是很強大的,可以打印出日期,文件名,行號,不使用宏定義很難實現。
六、小結
- 預處理器直接對宏進行文本替換
- 宏使用時的參數不會進行求值和運算
- 預處理器不會對宏定義進行語法檢查
- 宏定義時出現的語法錯誤只能被編譯器檢測
- 宏定義的效率高于函數調用
- 宏的使用會帶來一定的副作用
原文鏈接:https://blog.csdn.net/weixin_43129713/article/details/123458247
相關推薦
- 2022-09-08 Go語言中的Iota關鍵字_Golang
- 2022-08-04 Python?venv虛擬環境跨設備遷移的實現_python
- 2023-05-22 Flask中基于Token的身份認證的實現_python
- 2022-06-28 React18之狀態批處理的使用_React
- 2023-02-18 go?gin?正確讀取http?response?body內容并多次使用詳解_Golang
- 2022-07-25 C++超詳細講解內存空間分配與this指針_C 語言
- 2022-10-05 C#?獲取文件夾里所有文件名的詳細代碼_C#教程
- 2023-06-20 Python?Unittest?ddt數據驅動的實現_python
- 最近更新
-
- 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同步修改后的遠程分支