【C语言复习(八)】宏定义的使用与分析
2014-06-12 16:52
337 查看
1、宏常量
#define定义的宏常量可以出现在代码的任何地方,但为了美观易读,通常都写在开头#define从定义行开始,后面均可使用此宏定义
2、宏表达式
#define表达式给人有函数的假象,但绝对不是函数#define表达式有时可以比函数更强大
#define表达式比函数更容易出错,应谨慎使用
案例分析:
试着分析如下程序:#include <stdio.h> #define SUM(a,b) (a) + (b) #define MIN(a,b) ((a<b) ? (a) : (b)) #define DIM(array) (sizeof(array) / sizeof(*array)) int main() { printf("%d\n",SUM(1,2)); printf("%d\n",MIN(1,2)); return 0; }
运行程序,会发现并无问题。然后,稍作更改:printf("%d\n",SUM(1,2)*SUM(1,2));
我们预期的结果是9,但实际运行结果是5;因为根据宏表达式的替换规则可以得出最后替换的结果是:(1)+(2)*(1)+(2),也可直接让编译器预编译后,查看后缀为.i中间文件;要想得到预期的结果9,将宏修改一下即可:#define SUM(a,b) ((a) + (b))
再看如下程序:
#include <stdio.h> #define SUM(a,b) (a) + (b) #define MIN(a,b) ((a<b) ? (a) : (b)) #define DIM(array) (sizeof(array) / sizeof(*array)) int main() { int i=1; int j=5; printf("%d\n",MIN(i++,j)); return 0; }
这时候的输出该如何计算呢??同样根据宏表达式的替换规则,替换后的结果应该是:((i++<j) ? (i++) : (j)),此时分析一下,不难得出结果应该为2;只要将上述的输出语句改成:printf("%d\n",MIN(++i,j));那么输出结果又会不一样!
函数==宏表达式??
对于上述问题,我们试着用函数来实现,会发现情况没有使用宏表达式这么复杂:int Min(int a,int b) { return ((a<b) ? (a) : (b)); }
因此,在使用宏表达式时,建议不要在宏参数里面使用自增、减表达式,单纯的使用变量等等。
宏表达式的妙用
宏表达式的使用也不是完全没有好处的,比如第三个宏DIM,这个用函数可实现不了!#include <stdio.h> #define SUM(a,b) (a) + (b) #define MIN(a,b) ((a<b) ? (a) : (b)) #define DIM(array) (sizeof(array) / sizeof(*array)) int main() { int num[]={1,10,50,68,72,56,110}; printf("%d\n",DIM(num)); return 0; }
该宏实现返回一个数组元素个数的功能,可以试着定义一个函数来实现此功能:
int Dim(int array[]) { return sizeof(array)/sizeof(*array); }
运行整个程序后,会发现值为1,显然不正确。
其实我们也是可以通过代码来实现此宏的功能,如:int i=sizeof(num)/sizeof(*num);
这句代码就是预编译后宏表达式被替换的结果。那为什么上面的函数无法实现此功能呢??原因要结合函数调用时实参传递到形参的方式了,这里实参传递到形参后,形参实际上就只是一个指向实参数组的指针,所以函数中sizeof(array)的结果就是这个指针所占内存的大小,而不是整个数组所占内存的大小,我们的本意是要给第一个sizeof()传递一个类型,因此在这里宏表达式可比函数强大!且此宏可以用于任意类型的数组,通用性非常强!
再举一个例子:
利用宏表达式,动态分配内存;#include <stdio.h> #include <malloc.h> #define MALLOC(type,size) (type*)malloc(sizeof(type)*size) typedef struct _person { char* _name; int _age; } Person; int main() { Person* p=MALLOC(Person,1); p->_age=10; p->_name="张山疯!"; printf("Hello my name is %s,今年%d岁了!!",p->_name,p->_age); return 0; }
此宏定义的灵活性非常强,比函数好用多了!
3、宏表达式与函数的对比
宏表达式在预编译期被处理,编译器不知道宏表达式的存在;宏表达式用“实参”完全替代形参,不进行任何运算,区别于函数的实参和形参;
宏表达式没有任何的调用开销!在预编译器就被“内嵌”到程序代码中了,有点类似于C++中的内联函数吧,不像调用函数一样,执行流程要跳转到被调用的函数中去。
宏表达式中不能出现递归定义,这点区别于函数,因为宏只是做字符串替换,且只替换一次,如果出现递归定义,就会无法被完全替换,导致后续编译时原宏名被当作函数;
如下:
#define Func(n) ((n>0) ? (Func(n-1)+1) : 0) int j=Func(50);上述代码被预编译后,会被处理成:
int j=((50>0) ? (Func(50-1)+1) : 0)
此时预编译结束,编译阶段开始后,就会把Func(50-1)当作一个函数,因此就会报错,说Func()函数未定义。
宏定义的作用域限制
首先看如下的代码:#include <stdio.h> int f1(int a, int b) { #define _MIN_(a,b) ((a)<(b) ? a : b) return _MIN_(a, b); } int f2(int a, int b, int c) { return _MIN_(_MIN_(a,b), c); } int main() { printf("%d\n", f1(2, 1)); printf("%d\n", f2(5, 3, 2)); return 0; }
试着编译一下,会发现全然没有报错!按照一般的情况来说,我们是在f1函数中定义的宏_MIN_,可是在f2函数中居然也能使用!这里变量的作用域规则不再适用于宏定义,只要定义以后,后面的代码里都能使用!当然如果想限定它的使用范围,可以添加#undef _MIN_(a,b)到f1函数的return语句后,再次编译就会报错了!
4、一些内置宏
其实编译器也给我们提供了挺多的内置宏,这些宏可以直接拿来使用,当然有些需要包含定义的头文件,挺多的内置宏,所以只举例说明几个:5、示例:内置宏的使用举例--日志宏
代码如下:#include <stdio.h> #include <time.h> #define _log_(str) do{ \ time_t t; \ struct tm* ti; \ time(&t); \ ti=localtime(&t); \ printf("%s[%s : %d行]\t%s\n",asctime(ti),__FILE__,__LINE__,str); \ }while(0) void func() { _log_("进入func函数...."); printf("执行func函数!"); _log_("退出func函数...."); } int main() { func(); return 0; }
为什么上面的宏里面要使用一个do--while循环呢??如果不用此循环,会提示变量重复定义,可以去掉do--while循环,预编译一下就能看到原因了
以上代码的VS2015版本:
#include <stdio.h> #include <time.h> #define _log_(str) do{ \ time_t t; \ struct tm ti; \ char buffer[32] = {0}; \ time(&t); \ localtime_s(&ti,&t); \ asctime_s(buffer,sizeof(buffer),&ti);\ printf("%s[%s : %d行]\t%s\n",buffer,__FILE__,__LINE__,str); \ }while(0) void func() { _log_("进入func函数...."); printf("执行func函数!"); _log_("退出func函数...."); } int main() { func(); return 0; }
相关文章推荐
- 【C语言复习(九)】条件编译的使用与分析
- C语言宏定义使用分析
- C语言学习笔记12——宏定义与使用分析
- C语言的常用库函数使用方法分析及用途(15)
- C语言的常用库函数使用方法分析及用途(4)
- (转)C语言宏定义使用技巧
- C语言宏定义使用技巧
- C语言宏定义使用技巧(转)
- C语言的常用库函数使用方法分析及用途(13)
- C语言的常用库函数使用方法分析及用途(2)
- C语言的常用库函数使用方法分析及用途(5)
- C语言的常用库函数使用方法分析及用途(10)
- C语言宏定义的使用
- C语言的常用库函数使用方法分析及用途(9)
- C语言的常用库函数使用方法分析及用途(11)
- C语言的常用库函数使用方法分析及用途(8)
- C语言宏定义使用技巧(转)
- C语言宏定义使用技巧
- C语言宏定义使用技巧
- C语言的常用库函数使用方法分析及用途(6)