第16课 - 宏定义与使用分析
- 宏定义常量
#define定义宏常量可以出现在大地吗的任何地方。
#define从本行开始,之后的代码都是用这个宏常量。
例如:
#define ERROR -1
#define PI 3.1415926
#define PATH_1 “D:DelphiCTopic3.ppt”
#define PATH_2 D:DelphiCTopic3.ppt
#define PATH_3 D:DelphiC
- 宏定义表达式
#define表达式给有函数调用的假象,却不是函数。
#define表达式比函数更强大。
#define表达式比函数更容易出错。
#define SUM(a , b) (a)+(b)
#define MIN(a , b) ((a)<(b) ? (a) : (b))
#define DIM(a) (sizeof(a)/sizeof(*a))
例如:
#include<stdio.h>
#define MIN(a , b) ((a)<(b) ? (a) : (b))
int main()
{
int i = 1;
int j = 5;
printf("%d ",MIN(i++,j));
printf("%d ",i);
}
运行结果:
2
3
分析:预编译过程中,变成printf("%d ",(i++)<(j) ? (i++) : j);一共++两次
这里我们看到的是宏的缺点,但是他却有函数没有的功能,例如#define DIM(a) (sizeof(a)/sizeof(*a))就是函数做不到的。我们看下面的例子。
#include <stdio.h>
#include <malloc.h>
#define MALLOC(type, x) (type*)malloc(sizeof(type)*x)
#define FOREVER() while(1)
#define BEGIN {
#define END }
#define FOREACH(i, m) for(i=0; i<m; i++)
int main()
{
int array[] = {1, 2, 3, 4, 5};
int x = 0;
int*p = MALLOC(int, 5);
FOREACH(x, 5)
BEGIN
p[x] = array[x];
END
FOREACH(x, 5)
BEGIN
printf("%d ", p[x]);
END
FOREVER();
free(p);
printf("Last printf... ");
return 0;
}
运行结果:1 2 3 4 5
- 宏表达式与函数的对比
(1) 宏表达式在预编译期被处理,编译器不知道宏表达式的存在。
(2) 宏表达式用“实参”完全代替形象,不进行任何的运算。
(3) 宏表达式没有任何的“调用”开销。
(4) 宏表达式中不能出现递归的定义。
#define FAC(n) ((n>0) ? (FAC(n-1)+1) : 0)这就是错误的。
- 宏定义的作用域限制
#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 ", f1(2, 1));
printf("%d ", f2(5, 3, 2));
return 0;
}
运行结果:1
2
分析:宏定义可以出现在代码的任何地方,而且在任何其他地方都可以调用宏。这里介绍一个符号#undef。当我们使用的#define的内容,我们在后面加上这个符号,就会限制它的使用了。例如:
int f1(int a, int b)
{
#define _MIN_(a,b) ((a)<(b) ? a : b)
return _MIN_(a, b);
#undef
}
这样作用域的概念就出现了。函数里面没法定义另一个函数的,但是却可以定义另一个宏。
- 强大的内置宏
宏 |
含义 |
示例 |
__FILE__ |
被编译的文件名 |
file.c |
__LINE__ |
当前的行号 |
25 |
__DATE__ |
编译时的日期 |
Jan 31 2018 |
__TIME__ |
编译时的时间 |
17:01:01 |
__STDC__ |
编译器是否遵循标准C规范 |
1 |
定义日志,若用函数
#include<stdio.h>
#include<time.h>
void f()
{
time_t t;
struct tm* ti;
time(&t);
ti = localtime(&t);
printf(“%s”,asctime(ti));
printf("Enter f()... ");
printf("Exit f()... ");
}
void Log(char* s)
{
printf("%s:%d %s ", __FILE__, __LINE__, s);
}
int main()
{
Log("Enter main()...");
f();
Log("Exit main()...");
return 0;
}
会出现行号的错误,改为宏
#include<stdio.h>
#include<time.h>
#define LOG(s) do{
time_t t;
struct tm* ti;
time(&t);
ti = localtime(&t);
printf("%s [%s:%d] %s ", asctime(ti),__FILE__, __LINE__, s);
}while(0)
void f()
{
LOG("Enter f()...");
LOG("Exit f()...");
}
int main()
{
LOG("Enter main()...");
f();
LOG("Exit main()...");
return 0;
}
行号显示正确。注意该程序实在linux下运行的。
思考:
#define f (x) ((x)-1)注意在f后面有一个空格那么f是否带了一个参数呢?代表f(x)或者代表((x)-1),还是f代表(x) ((x)-1)呢?为什么?
这是一个错误的用法,#define f(x) ((x)-1)这个是没问题的,但你意外发现f 后面可以加上一个空格,因为宏定义#define的用法是 #define 标识符 字符串,就是用简单的标识符取代复杂的字符串,这个字符串可以是表达式,系统认为标识符是一个函数的借口,返回表达式的值,而返回类型则与主函数中定义的x有关,也可以是一句话(要用双引号括起来,否则系统就会认为是表达式),这样就要以字符串形式输出 关键字+空格+标识符+空格+字符串,这样了在实现取代作用,如果出现多个空格,就会认为是属于字符串中的空格,但如果没有双引号,就会认为是表达式,明显这种表达式是不能运算得,于是当使用f时,就会出现一句编译错误,提示无法评估f函数返回值的类型