tft每日頭條

 > 生活

 > 宏程序的說明

宏程序的說明

生活 更新时间:2025-01-31 18:39:05

C語言中用到宏定義的地方很多,如在頭文件中為了防止頭文件被重複包含,則用到:

#ifndef cTest_Header_h #define cTest_Header_h //頭文件内容 #endif

在我們常用的 stdio.h 頭文件中也可以見到很多宏定義,如:

#define BUFSIZ 1024 //緩沖區大小 #define EOF (-1) //表文件末尾 #ifndef SEEK_SET #define SEEK_SET 0 //表示文件指針從文件的開頭開始 #endif #ifndef SEEK_CUR #define SEEK_CUR 1 //表示文件指針從現在的位置開始 #endif #ifndef SEEK_END #define SEEK_END 2 //表示文件指針從文件的末尾開始 #endif

從開始寫C語言到生成執行程序的流程大緻如下(姑且忽略預處理之前的編譯器的翻譯處理流程等),在進行編譯的第一次掃描(詞法掃描和語法分析)之前,會有由預處理程序負責完成的預處理工作。

宏程序的說明(詳解宏定義define)1

預處理工作是系統引用預處理程序對源程序中的預處理部分做處理,而預處理部分是指以“#”開頭的、放在函數之外的、一般放在源文件的前面的預處理命令,如:包括命令 #include,宏命令 #define 等,合理地利用預處理功能可以使得程序更加方便地閱讀、修改、移植、調試等,也有利于模塊化程序設計。本文主要介紹宏定義的以下幾個部分:

宏程序的說明(詳解宏定義define)2

1、概念及無參宏

一種最簡單的宏的形式如下:

一種最簡單的宏的形式如下: #define 宏名 替換文本

每個#define行(即邏輯行)由三部分組成:第一部分是指令 #define 自身,“#”表示這是一條預處理命令,“define”為宏命令。第二部分為宏(macro),一般為縮略語,其名稱(宏名)一般大寫,而且不能有空格,遵循C變量命令規則。“替換文本”可以是任意常數、表達式、字符串等。在預處理工作過程中,代碼中所有出現的“宏名”,都會被“替換文本”替換。這個替換的過程被稱為“宏代換”或“宏展開”(macro expansion)。“宏代換”是由預處理程序自動完成的。在C語言中,“宏”分為兩種:無參數 和 有參數。

無參宏是指宏名之後不帶參數,上面最簡單的宏就是無參宏。

#define M 5 // 宏定義 #define PI 3.14 //宏定義 int a[M]; // 會被替換為: int a[5]; int b = M; // 會被替換為: int b = 5; printf("PI = %.2f\n", PI); // 輸出結果為: PI = 3.14

注意宏不是語句,結尾不需要加“;”,否則會被替換進程序中,如:

#define N 10; // 宏定義 int c[N]; // 會被替換為: int c[10;]; //error:… main.c:133:11: Expected ']'

以上幾個宏都是用來代表值,所以被成為類對象宏(object-like macro,還有類函數宏,下面會介紹)。

如果要寫宏不止一行,則在結尾加反斜線符号使得多行能連接上,如:

#define HELLO "hello \ the world"

注意第二行要對齊,否則,如:

#define HELLO "hello the wo\ rld" printf("HELLO is %s\n", HELLO); //輸出結果為: HELLO is hello the wo rld

也就是行與行之間的空格也會被作為替換文本的一部分

而且由這個例子也可以看出:宏名如果出現在源程序中的“”内,則不會被當做宏來進行宏代換。

宏可以嵌套,但不參與運算:

#define M 5 // 宏定義 #define MM M * M // 宏的嵌套 printf("MM = %d\n", MM); // MM 被替換為: MM = M * M, 然後又變成 MM = 5 * 5

宏代換的過程在上句已經結束,實際的 5 * 5 相乘過程則在編譯階段完成,而不是在預處理器工作階段完成,所以宏不進行運算,它隻是按照指令進行文字的替換操作。再強調下,宏進行簡單的文本替換,無論替換文本中是常數、表達式或者字符串等,預處理程序都不做任何檢查,如果出現錯誤,隻能是被宏代換之後的程序在編譯階段發現。

宏定義必須寫在函數之外,其作用域是 #define 開始,到源程序結束。如果要提前結束它的作用域則用 #undef 命令,如:

#define M 5 // 宏定義 printf("M = %d\n", M); // 輸出結果為: M = 5 #define M 100 // 取消宏定義 printf("M = %d\n", M); // error:… main.c:138:24: Use of undeclared identifier 'M'

也可以用宏定義表示數據類型,可以使代碼簡便:

#define STU struct Student // 宏定義STU struct Student{ // 定義結構體Student char *name; int sNo; }; STU stu = {"Jack", 20}; // 被替換為:struct Student stu = {"Jack", 20}; printf("name: %s, sNo: %d\n", stu.name, stu.sNo);

如果重複定義宏,則不同的編譯器采用不同的重定義策略。有的編譯器認為這是錯誤的,有的則隻是提示警告。Xcode中采用第二種方式。如:

#define M 5 //宏定義 #define M 100 //重定義,warning:… main.c:26:9: 'M' macro redefined

這些簡單的宏主要被用來定義那些顯式常量(Manifest Constants)(Stephen Prata,2004),而且會使得程序更加容易修改,特别是某一常量的值在程序中多次被用到的時候,隻需要改動一個宏定義,則程序中所有出現該變量的值都可以被改變。而且宏定義還有更多其他優點,如使得程序更容易理解,可以控制條件編譯等。

#define 與 #typedef 的區别:

兩者都可以用來表示數據類型,如:

#define INT1 int typedef int INT2;

兩者是等效的,調用也一樣:

INT1 a1 = 3; INT2 a2 = 5;

但當如下使用時,問題就來了:

#define INT1 int * typedef int * INT2; INT1 a1, b1; INT2 a2, b2; b1 = &m; //... main.c:185:8: Incompatible pointer to integer conversion assigning to 'int' from 'int *'; remove & b2 = &n; // OK

因為 INT1 a1, b1; 被宏代換後為: int * a1, b1;即定義的是一個指向int型變量的指針 a1 和一個int型的變量b1.而INT2 a2, b2;表示定義的是兩個變量a2和b2,這兩個變量的類型都是INT2的,也就是int *的,所以兩個都是指向int型變量的指針。

所以兩者區别在于,宏定義隻是簡單的字符串代換,在預處理階段完成。而typede不是簡單的字符串代換,而是可以用來做類型說明符的重命名的,類型的别名可以具有類型定義說明的功能,在編譯階段完成的。

2、有參宏

C語言中宏是可以有參數的,這樣的宏就成了外形與函數相似的類函數宏(function-like macro),如:

宏程序的說明(詳解宏定義define)3

宏調用:

宏名(實參表);

printf(“MEAN = %d\n”, MEAN(7, 9)); // 輸出結果: MEAN = 8

和函數類似,在宏定義中的參數成為形式參數,在宏調用中的參數成為實際參數。

而且和無參宏不同的一點是,有參宏在調用中,不僅要進行宏展開,而且還要用實參去替換形參。如:

#define M 5 //無參宏 #define COUNT(M) M * M //有參宏 printf("COUNT = %d\n", COUNT(10)); // 替換為: COUNT(10) = 10 * 10 // 輸出結果: COUNT = 100

這看上去用法與函數調用類似,但實際上是有很大差别的。如:

#define COUNT(M) M * M //定義有參宏 int x = 6; printf("COUNT = %d\n", COUNT(x 1));// 輸出結果: COUNT = 13 printf("COUNT = %d\n", COUNT( x)); // 輸出結果: COUNT = 56 //warning:... main.c:161:34: Multiple unsequenced modifications to 'x'

這兩個結果和調用函數的方法的結果差别很大,因為如果是像函數那樣的話,COUNT(x 1)應該相當于COUNT(7),結果應該是 7 * 7 = 49,但輸出結果卻是21。原因在于,預處理器不進行技術,隻是進行字符串替換,而且也不會自動加上括号(),所以COUNT(x 1)被替換為 COUNT(x 1 * x 1),代入 x = 6,即為 6 1 * 6 1 = 13。而解決辦法則是:盡量用括号把整個替換文本及其中的每個參數括起來:

#define COUNT(M) ((M) * (M))

但即使用括号,也不能解決上面例子的最後一個情況,COUNT( x) 被替換為 x * x,即為 7 * 8 = 56,而不是想要 7 * 7 = 49,解決辦法最簡單的是:不要在有參宏用使用到“ ”、“–”等。

上面說到宏名中不能有空格,宏名與形參表之間也不能有空格,而形參表中形參之間可以出現空格:

#define SUM (a,b) a b //定義有參宏 printf("SUM = %d\n", SUM(1,2)); //調用有參宏。Build Failed! 因為 SUM 被替換為:(a,b) a b

如果用函數求一個整數的平方,則是:

int count(int x){ return x * x; }

所以在宏定義中:#define COUNT(M) M * M 中的形參不分配内存單元,所以不作類型定義。而函數 int count(int x)中形參是局部變量,會在棧區分配内存單元,所以要作類型定義,而且實參與形參之間是“值傳遞”。而宏隻是符号代換,不存在值傳遞。

宏定義也可以用來定義表達式或者多個語句。如:

#define JI(a,b) a = i 3; b = j 5; //宏定義多個語句 int i = 5, j = 10; int m = 0, n = 0; JI(m, n); // 宏代換後為: m = i 3, n = j 5; printf("m = %d, n = %d\n", m, n); // 輸出結果為: m = 8, n = 15

3、# 運算符

比如如果我們宏定義了:

#define SUM (a,b) ((a) (b))

我們想要輸出“1 2 3 4 = 10”,用以下方式顯得比較麻煩,有重複代碼,而且中間還有括号:

printf("(%d %d) (%d %d) = %d\n", 1, 2, 3, 4, SUM(1 2, 3 4));

那麼這時可以考慮用 # 運算符來在字符串中包含宏參數,# 運算符的用處就是把語言符号轉化為字符串。例如,如果 a 是一個宏的形參,則替換文本中的 #a 則被系統轉化為 “a”。而這個轉化的過程成為 “字符串化(stringizing)”。用這個方法實現上面的要求:

#define SUM(a,b) printf(#a " "#b" = %d\n",((a) (b))) //宏定義,運用 # 運算符 SUM(1 2, 3 4); //宏調用 //輸出結果:1 2 3 4 = 10

調用宏時,用 1 2 代替 a,用 3 4 代替b,則替換文本為:printf(“1 2” ” ” “3 4” ” = %d\n”,((1 2) (3 4))),接着字符串連接功能将四個相鄰的字符串轉換為一個字符串:

"1 2 3 4 = %d\n"

4、## 運算符

和 # 運算符一樣,## 運算符也可以用在替換文本中,而它的作用是起到粘合的作用,即将兩個語言符号組合成一個語言符号,所以又稱為“預處理器的粘合劑(Preprocessor Glue)”。用法:

#define NAME(n) num ## n //宏定義,使用 ## 運算符 int num0 = 10; printf("num0 = %d\n", NAME(0)); //宏調用

NAME(0)被替換為 num ## 0,被粘合為: num0。

5、可變宏:… 和 __VA_ARGS__

我們經常要輸出結果時要多次使用 prinf(“…”, …); 如果用上面例子#define SUM(a,b) printf(#a ” “#b” = %d\n”,((a) (b))),則格式比較固定,不能用于輸出其他格式。

這時我們可以考慮用可變宏(Variadic Macros)。用法是:

#define PR(...) printf(__VA_ARGS__) //宏定義 PR("hello\n"); //宏調用 //輸出結果:hello

在宏定義中,形參列表的最後一個參數為省略号“…”,而“__VA_ARGS__”就可以被用在替換文本中,來表示省略号“…”代表了什麼。而上面例子宏代換之後為: printf(“hello\n”);

還有個例子如:

#define PR2(X, ...) printf("Message"#X":"__VA_ARGS__) //宏定義 double msg = 10; PR2(1, "msg = %.2f\n", msg); //宏調用 //輸出結果:Message1:msg = 10.00

在宏調用中,X的值為10,所以 #X 被替換為”1”。宏代換後為:

printf("Message""1"":""msg = %.2f\n", msg);

接着這4個字符串連接成一個:

printf("Message1:msg = %.2f\n", msg);

要注意的是:省略号“…”隻能用來替換宏的形參列表中最後一個!

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关生活资讯推荐

热门生活资讯推荐

网友关注

Copyright 2023-2025 - www.tftnews.com All Rights Reserved