一、宏定义定义常量
#include
void test_define1()
{
printf("file name is %s\n", __FILE__);
printf("line is %d\n", __LINE__);
printf("date is %s\n", __DATE__);
printf("time is %s\n", __TIME__);
printf("stdc is %d\n", __STDC__);
}
int main()
{
test_define1();
return 0;
}
void test_define2()
{
#define PI 3.14
int R = 2;
float S = 0.0;
S = R*R*PI;
printf("S = %f\n", S);
}
int main()
{
//test_define1();
test_define2();
return 0;
}
我们先将test.c预编译一下:
gcc -o test1.c -E test.c
然后打开预编译产生的文件test1.c:
void test_define2()
{
int R = 2;
float S = 0.0;
S = R*R*3.14;
printf("S = %f\n", S);
}
int main()
{
test_define2();
return 0;
}
可以看到预编译将#define PI 3.14这个定义直接替换到使用PI的地方,也就是说预处理会将使用宏定义变量的地方直接进行替换处理。
宏定义原则:
#define定义宏常量可以出现在代码的任何地方
#define从本行开始,,之后的代码都可以使用这个宏常量
注意宏不是语句,结尾不需要加“;”,否则会被替换进程序中,如:
#define N 10; // 宏定义
int c[N]; // 会被替换为: int c[10;];
//error:… main.c:133:11: Expected ']
如果重复定义宏,则不同的编译器采用不同的重定义策略。有的编译器认为这是错误的,有的则只是提示警告。Xcode中采用第二种方式。如:
#define M 5 //宏定义
#define M 100 //重定义,warning:… main.c:26:9: 'M' macro redefined
#define INT1 int
typedef int INT2;
两者是等效的,调用也一样:
INT1 a1 = 3;
INT2 a2 = 5;
但是用于指针时,问题就来了:
#define INT1 int *
typedef int * INT2;
INT1 a1, b1;
INT2 a2, b2;
b1 = &m; //... main.c:185:8: Incompatible pointer to integer conversion assigning to 'int' from 'int *'; remove &
b2 = &n; // OK
因为 INT1 a1, b1; 被宏代换后为: int * a1, b1;即定义的是一个指向int型变量的指针 a1 和一个int型的变量b1.而INT2 a2, b2;表示定义的是两个变量a2和b2,这两个变量的类型都是INT2的,也就是int *的,所以两个都是指向int型变量的指针。
所以两者区别在于,宏定义只是简单的字符串代换,在预处理阶段完成。而typede不是简单的字符串代换,而是可以用来做类型说明符的重命名的,类型的别名可以具有类型定义说明的功能,在编译阶段完成的。
总之一句话:编译器对#define预处理就是直接进行文本替换
#define HELLO "hello the world"
我们可以用连接符进行换行处理:
#define HELLO "hello \
the world"
又比如我们用#define来定义一个函数:
#define ADD(a, b) \
((a) + (b))
void test_define3()
{
int a = 10;
int b = 20;
int sum = 0;
sum = ADD(a, b);
printf("sum is %d\n", sum);
}
int main()
{
test_define3();
return 0;
}
二、宏定义定义表达式
我们先来看宏带参数的定义:
和函数类似,在宏定义中的参数成为形式参数,在宏调用中的参数成为实际参数。
而且和无参宏不同的一点是,有参宏在调用中,不仅要进行宏展开,而且还要用实参去替换形参。
#define MAX(x,y) ((x)>(y)?(x):(y))
对应这个定义的函数:
int Max(int x, int y)
{
return x > y ? x : y;
}
相对于函数来说:
(1).用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。
(2).更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。但是宏可以适用于整形、长整型、浮点型等可以用于来比较的类型。宏是类型无关的。
而且,宏有时候可以做到函数做不到的事情。例如,宏的参数可以出现类型,但是函数却不可以。
但是,宏也有劣势的地方,例如:
(1)每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
(2)宏是没法调试的。
(3)宏由于类型无关,也就不够严谨。
(4)宏可能会带来运算符优先级的问题,导致程容易出现错。
我们来看看一段代码:
#define STD(n) std##n
int test_define4()
{
int STD(1);
int STD(2);
STD(1) = 1;
STD(2) = 2;
printf("%d\n", STD(1));
printf("%d\n", STD(2));
return 0;
}
int main()
{
test_define4();
return 0;
}
这里定义了student:#define STD(n) std##n,然后声明了两个student,值分别为1,2,我们来看看预编译产生的结果:
int test_define4()
{
int std1;
int std2;
std1 = 1;
std2 = 2;
printf("%d\n", std1);
printf("%d\n", std2);
return 0;
}
int main()
{
test_define4();
return 0;
}
可以看到预编译直接将std和参数n进行拼接处理
三、宏定义带来的副作用
#define INT1 int *
typedef int * INT2;
INT1 a1, b1;
INT2 a2, b2;
b1 = &m; //... main.c:185:8: Incompatible pointer to integer conversion assigning to 'int' from 'int *'; remove &
b2 = &n; // OK
因为 INT1 a1, b1; 被宏代换后为: int * a1, b1;即定义的是一个指向int型变量的指针 a1 和一个int型的变量b1.而INT2 a2, b2;表示定义的是两个变量a2和b2,这两个变量的类型都是INT2的,也就是int *的,所以两个都是指向int型变量的指针。
#define COUNT(M) M * M //定义有参宏
int x = 6;
printf("COUNT = %d\n", COUNT(x + 1));// 输出结果: COUNT = 13
printf("COUNT = %d\n", COUNT(++x)); // 输出结果: COUNT = 56
我们来看看预编译产生的:
void test_define5()
{
int x = 6;
printf("COUNT = %d\n", x + 1 * x + 1);
printf("COUNT = %d\n", ++x * ++x);
}
看到预编译的结果我们可以明显看到为什么结果是13和56了,这个并不是我们想要的结果。
四、总结: