您的位置:首页 > 编程语言 > C语言/C++

【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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: