1.带副作用的宏参数
如果宏的参数中带有改变传过来的参数,形式比如说自增、自减,它可能带有副作用exp:
#define MAX(x,y) ((x>y)?(x):(y)) int main() { int a = 10; int b = 20; printf("%dn", MAX(a++, b++));//21 //printf("%d",(a++>b++)?(a++):(b++)); //这里比较完之后a,b都自增了,输出的较大值b会是21,在输出之后b又自增 printf("%dn", a);//11 //a在上面的式子中只自增了一次变成11 printf("%dn", b);//22 //而b自增了两次,一次在比较之后,一次在输出之后,所以b现在等于22 return 0; }
2.宏和函数的对比
函数的参数需要特定的参数,而宏在传参的时候不需要考虑数据类型,它只是进行替换;但在程序序调试的时候,宏出现的错误不容易被找出;函数在调用和传参还有返回需要大量的汇编代码,花费的时间可能更长,而宏在预处理阶段就发生了替换;但是在每次宏的替换都会将一段代码插入到代码中,而函数仅有一段代码,每次使用的时候仅需调用;函数参数是不能传数据类型的,而宏可以;
#define MALLOC(num,type) (type*)malloc(num*sizeof(type)) #define SIZEOF(type) sizeof(type) void put_data(int* pr) { int i = 0; //动态连续空间放入数据 for (i = 0; i < 10; i++) { pr[i] = i; } //打印 for (i=0; i <10; i++) { printf("%d ", pr[i]); } printf("n"); } int main() { //宏传类型参数,可行 printf("%dn", SIZEOF(int));//4 //动态开辟空间 int* ptr1 = (int*)malloc(10 * sizeof(int)); put_data(ptr1);//0 1 2 3 4 5 6 7 8 9 int* ptr = MALLOC(10, int); put_data(ptr);//0 1 2 3 4 5 6 7 8 9 //释放空间 free(ptr1); ptr1 = NULL; free(ptr); ptr = NULL; return 0; }
#undef用于取消符号定义的
#define MAX(x,y) ((x>y)?(x):(y)) int main() { int a = 20; int b = 30; printf("%d", MAX(a, b)); #undef MAX(x,y) printf("%d", MAX(a, b));//error 未定义标识符 return 0; }
3.命令行定义
gcc test.c -D SZ=10在程序运行之前就定义好,
4.条件编译
在一段程序中,也许可以遇见我们只在特定情况下需要的代码,或者只需要使用一次的代码,我们可以通过条件编译,进行判断选择这段代码需不需要编译
#define PRINT_S int main() { int a = 20; int b = 0; #ifdef PRINT//这里的就是说之前定义过PRINT这个符号,程序就会编译这段代码,反之就会跳过 printf("%d", a);//注意如果一段代码不会进行编译,就是灰色的(vs2017下) #endif//这里的endif是条件编译的结束语句,与ifdef搭配 #ifdef PRINT_S printf("%d", a);//20 #endif //1.#if 常量表达式 #if 1 //为真进行编译 printf("%d", a);//20 #endif //2.多分支判断 #if 20>30 //为假不进行编译 printf("%d", a); #elif 20==30 printf("%d", a); #else printf("%d", a);//20 #endif //3.#if defined(符号) #if defined(PRINT_S)//如果定义过 printf("%d", a);//20 #endif #if !defined(PRINT_S)//如果没有定义过(但定义过) printf("%d", a);//20 #endif //4. return 0; }
5.文件包含
在头文件包含的过程,是将头文件的内容copy一份到现在.c程序中进行编译,在我们实际写工程的过程中,其实会有意无意的将一个头文件包含多次,那么整个项目当中的就会有很多的代码冗余,在我们的库函数有解决办法
//假设这是我们要包含的头文件,我们一般会采取以下方式,防止一个头文件被多次包含 #ifndef __PASS__ #define __PASS__ //这里面是头文件的内容 //....... #endif //这样头文件的内容只能被包含一次(编译) //当然还有其他的解决办法(现在多采用) #pragma once //这里面是头文件的内容 //.......
其他的预处理指令:#error(如果在编译程序的过程中遇到,会编译一个错误提示信息,并停止编译)、#pragma(指定一个跟踪选择)、#line(改变当前命令行数和文件名称)等
6.宏offsetof可以查偏移
#includestruct S { //默认对齐数为8 int a;//4 占用了偏移量为0 1 2 3的地址共四个字节 char b;//1 占用了偏移量为4的地址 共一个字节 double c;//8 现在接下来使用的的空间地址的偏移量为5,但5不是八的倍数,所以要将指针移到偏移量为8的地址继续接下来的的存储, //前面浪费了3个字节的空间,再将c存进去,占8个字节 }s; //这个结构体的大小为16 为最大对齐数的倍数 #define OFFSETOF(struct_typename,member_name) (int)&(((struct_typename*)0)->member_name) //解释:我们将结构体的开始放成员的地址为0(结 //构体类型的地址或者指针)当首地址为0, //那对应偏移量的成员的地址就等于偏移量,exp: //上面b的地址就是偏移量4,最后的类型强转 //的强制类型转化,将地址的转换为整型,地址是十 //六进制数字,而如果指针变量为整型常量,指针的 //存放的地址将会被赋值为这个整型常量(((struct //S*)0)->a)的地址为 0x00000000,在这个地址偏 //移4之后放下b,而此时b的地址为0x00000004,对 //这些地址强转成整型,就会得到0,4,8等成员的 //相较首地址的偏移量 int main() { //printf("%dn", sizeof(s));//16 //使用c库里定义的宏 //printf("a在结构体中的偏移量%dn", offsetof(struct S,a));//0 //printf("b在结构体中的偏移量%dn", offsetof(struct S, b));//4 //printf("c在结构体中的偏移量%dn", offsetof(struct S, c));//8 //使用上面自己定义的宏 printf("a在结构体中的偏移量%dn", OFFSETOF(struct S, a));//0 printf("b在结构体中的偏移量%dn", OFFSETOF(struct S, b));//4 printf("c在结构体中的偏移量%dn", OFFSETOF(struct S, c));//8 return 0; }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)