一.预处理
二.宏定义用法
①宏常量
#include"stdio.h" #define PI 3.14 #define STR "圆周率约等于" int main() {
printf("%s %f",STR,PI); //预处理时会被替换为 printf("%s %f","圆周率约等于",3.14); return 0; }
②宏语句
#include"stdio.h" #define Print printf("hello world!") int main() {
Print; //预处理时会被替换为 printf("hello world!"); return 0; }
③宏函数
#include"stdio.h" #define Print(str) printf("%s",str) int main() {
Print("这是一个只有一条语句的宏函数!"); //预处理时会被替换为 printf("%s","这是一个只有一条语句的宏函数!") return 0; }

④其它
1.#undef 是用来撤销宏定义的,用法如下:
#define PI 3. ... // code #undef PI //下面开始 PI 就失效了
2.使用ifndef防止头文件被重复包含和编译
这是宏定义的一种,它可以根据是否已经定义了一个变量来进行分支选择,一般用于调试等等.实际上确切的说这应该是预处理功能中三种(宏定义,文件包含和条件编译)中的一种—-条件编译。 C语言在对程序进行编译时,会先根据预处理命令进行“预处理”。C语言编译系统包括预处理,编译和链接等部分。
#ifndef x //先测试x是否被宏定义过 #define x //如果没有宏定义下面就宏定义x并编译下面的语句 ... ... ... #endif //如果已经定义了则编译#endif后面的语句
所以还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你都要加上这个。一般格式是这样的:
#ifndef <标识> #define <标识> ...... #endif
<标识>
在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h
标识>
#ifndef _STDIO_H_ #define STDIO_H ... #endif #ifndef xxx //如果没有定义xxx #define xxx //定义xxx #endif //结束如果
这个用法主要是在头文件中,主要是为了防止类重复的include,所以在类的头文件之前加上前面两个,用类名替代xxx,在最后加上最后一句
三.宏定义相关作用符
①换行符 “\n”
#include"stdio.h" #define Print printf("这是第1条语句\n"); printf("这是第2条语句\n"); printf("这是第3条语句\n") #define Show(str1,str2,str3) {
printf(“%s\n”,str1); printf(“%s\n”,str2); printf(“%s\n”,str3); } int main() {
Print; //无参数宏函数 Show(“first”,“second”,“third”); //带参数宏函数 return 0; }
②字符串化符 “#”
#include"stdio.h" #define Print(str) {
printf(#str"的值是%d",str); } int main() {
int x=3,y=4; Print(x+y); //此处等价于printf("x+y""的值是%d",x+y); //#str等价于"x+y",所以#str不需要再用双引号引起来 return 0; }
③片段连接符””
#include"stdio.h" #define Add(n,value) {
numn+=value; } int main() {
int num1=1; int num2=10; Add(2,10); //等价于num2+=10; 这里把num和2连接成了num2 printf(" num1=%d\n num2=%d",num1,num2); return 0; }
四.宏函数的巧用
①类型传递
#define Malloc(type,size) (type*)malloc(sizeof(type)*size)
这个时候,我们只有把类型,容量作为参数传递进行,就可以开辟各种类型的内存了
int *p=Malloc(int,100); //开辟int类型的内存 char *q=Malloc(char,100); //开辟字符类型的内存
②传递数组
#include"stdio.h" #define InsertSort(list) {
int s=sizeof(list)/4; int i,j; for(i=2;i<=s;i++) {
list[0]=list[i]; for(j=i-1;list[j]>list[0];j--) list[j+1]=list[j]; list[j+1]=list[0]; } } int main() {
int num[]={
0,2,5,7,3,1,8,0,8,22,57,56,74,18,99,34,31,55,41,12,9,4}; InsertSort(num); for(int i=1;i<sizeof(num)/4;i++) printf("%d ",num[i]); return 0; }
五.注意事项
① 运算符优先级问题
#define MULTIPLY(x, y) x * y
这是一个很简单的乘法函数,当计算MULTIPLY(10, 10),结果是100,这个大家都知道,但是当你计算MULTIPLY(5+5, 10)时,你以为结果还是100吗?当然不是,MULTIPLY(5+5, 10)=5+5*10=55,所以结果是55,所以我们写宏函数时要特别注意运算符的优先级,这里稳妥一点的写法应该这样写
#define MULTIPLY(x, y) ((x)*(y))
②宏参数重复调用
#define MAX(a,b) ((a)>(b)?(a):(b)) int a=0; int b =1; int c =MAX(++a,++b);
这里很多人都以为是c=MAX(1,2)=2;而实际上上面代码等价于
int c =((++a)>(++b)?(++a):(++b));
可以看到实际上a b都各自加了两次,所以c=1>2?2:3=3,所以结果是3
③分号吞噬问题
#include"stdio.h" #define FUN(n) {
while(n>0) {
if(n==3) break; } } int main() {
int num=10; if(num>0) FUN(num); else num=-num; return 0; }
#define FUN(n) do {
while(n>0) {
if(n==3) break; } }while(0)
④递归调用问题
#define NUM (4 + NUM)
按前面的理解,(4 + NUM)会展开成(4 + (4 + NUM)),然后一直展开下去,直至内存耗尽。但是,预处理器采取的策略是只展开一次。也就是说,NUM只会展开成(4 + NUM),而展开之后NUM的含义就要根据上下文来确定了。
⑤宏参数预处理
宏参数中若包含另外的宏,那么宏参数在被代入到宏体之前会做一次完全的展开,除非宏体中含有#或。
有如下宏定义:
#define A(y) X_y #define B(y) A(y) #define SIZE 1024 #define S SIZE
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/225036.html原文链接:https://javaforall.net
