作者:陈曦
日期:2012-7-28 18:19:55
环境:[Mac 10.7.1 Lion Intel i3 支持64位指令 gcc4.2.1 xcode4.2]
转载请注明出处
Q1: 宏这个东西真是很奇怪,为什么我想将一句#include代码用宏来替换,却不行?
#define INCLUDE_STDIO #include
INCLUDE_STDIO
int main()
{
return 0;
}
保存为preprocess_header.c
A: 如果是预处理出了问题,我们可以使用-E查看预处理后的结果,来分析到底哪里出了问题。
Q2: 由上面看,好像没有什么问题。
A: 看起来是没有什么问题,问题在于上面的结果是预处理后的结果。编译器编译预处理后的源代码还出现了#include, 编译器是不识别的,所以会报错。换句话说,预处理做预处理的事情,编译器做编译源代码的事情(不过编译器常常被看做包括预处理的功能).预处理器可以处理#include, 而编译器根本无法识别#include.
Q3: 预处理当发现INCLUDE_STDIO符号是#include时为什么没有继续预处理此头文件的内容?
A: 这就在于c语言标准规定#define定义的符号是进行重新预处理的,但是仅限于#define的符号,遇到#include是不做处理的。
Q4: 有时,需要测试数据,写了好多相同名称开头的变量,最后进行赋值,有什么好方法?
A: 这当然需要用到##符号了,它可以正确生成你需要的变量。如下代码:
#include
#include
#include
#include
#define PRINT_D(longValue) printf(#longValue" is %ld\n", ((long)longValue));
#define PRINT_STR(str) printf(#str" is %s\n", (str));
#define ADD_TO_NUM(sum_name, sum_number, value) \
(sum_name ## sum_number) += (value);
int main(int argc, char **argv)
{
int sum1 = 10, sum2 = 20;
int i = 10;
ADD_TO_NUM(sum, 1, i);
ADD_TO_NUM(sum, 2, i);
PRINT_D(sum1)
PRINT_D(sum2)
return 0;
}
编译运行:
sum1 is 20
sum2 is 30
同理,如果需要对c语言的结构,比如堆栈、队列等写不同类型数据的操作函数,可以将类型用作参数做出类似的宏定义。
Q5: 对于do, while循环,我喜欢反着用,用until的模式,有什么方法吗?
A: 如下代码:
#define repeat do
#define until(x) while(!(x))
测试代码:
#include
#include
#include
#include
#define PRINT_D(longValue) printf(#longValue" is %ld\n", ((long)longValue));
#define PRINT_STR(str) printf(#str" is %s\n", (str));
#define repeat do
#define until(x) while(!(x))
int main(int argc, char **argv)
{
int i = 1;
repeat
{
PRINT_D(i)
++i;
}until(i > 10);
return 0;
}
运行结果:
i is 1
i is 2
i is 3
i is 4
i is 5
i is 6
i is 7
i is 8
i is 9
i is 10
Q6: 很多时候,写代码的时候,发现一个关键字或者代码组合老是写,用宏做替换是否是个好的选择?
A: 只要风险可控就是好选择。比如,register关键字感觉好长,可以用REG代替;无限循环for(;;)也很长,可以用FOREVER代替;switch语句内部的break和case语句连写也可以用宏CASE代替。
#define REG register
#define FOREVER for(;;)
#define CASE break; case
测试代码:
#include
#include
#include
#include
#define PRINT_D(longValue) printf(#longValue" is %ld\n", ((long)longValue));
#define PRINT_STR(str) printf(#str" is %s\n", (str));
#define REG register
#define FOREVER for(;;)
#define CASE break; case
int main(int argc, char **argv)
{
REG int i = 1;
FOREVER
PRINT_STR("hello")
return 0;
}
这里还有关于文件读写的例子,发现老是要写fopen,fclose函数,写了好多遍,不如封装在一个通用文件中,用宏是个好选择:
#define FOPEN_COMMON(file_name) \
FILE *fp = fopen((file_name), "r+"); \
if(!fp) \
{ \
perror("fopen error"); \
return -1; \
}
#define FCLOSE_COMMON \
fclose(fp);
总之,有什么代码觉得多的地方,用宏做替换是个好选择,不过要清楚这里面的风险。
Q7: 既然宏就是字符串的替换,那么是否也可以将一种基本类型定义成另一种基本类型?
A: c语言标准并没有规定宏字符串不能是关键字,所以上面的情况是可以的。不过,这样很可能打乱以前的逻辑,可以在某些场合做测试。
#include
#include
#include
#include
#define PRINT_D(longValue) printf(#longValue" is %ld\n", ((long)longValue));
#define PRINT_STR(str) printf(#str" is %s\n", (str));
#define double int
int main(int argc, char **argv)
{
double i = 1.5;
PRINT_D(i)
return 0;
}
可以看到,上面将double字符串定义成int字符串,main函数中的定义i的代码也就变成了int i = 1.5; 最后的输出结果:
i is 1
Q8: 有时,为了方便输出不同类型的变量,写了如下两个宏:
#define PRINT(longValue) printf(#longValue" is %ld\n", ((long)longValue));
#define PRINT(str) printf(#str" is %s\n", (str));为什么,再使用 PRINT(1) 时会崩溃?
A: 这在于宏并不像函数一样支持重载,它就是个名称,不会因为仅仅宏参数不一样而导致编译器认为宏不一样。所以,上面两个PRINT宏其实是被编译器看做是同一个宏,还会出现重定义的警告:
warning: "PRINT" redefined所以,PRINT(1)其实使用的是第二个宏。可以使用预处理命令得到预处理后的结果,首先是源代码,保存为preprocess_macro.c :
#include
#define PRINT(longValue) printf(#longValue"is %ld\n", (longValue));
#define PRINT(str) printf(#str" is %s\n", (str));
int main()
{
PRINT(1)
return 0;
}
使用预处理得到如下:
由上图可以看出,确实使用了PRINT(str)这个宏来替换,所以导致崩溃。
Q9: 有时,希望可以根据sizeof(int)的数值来做一些提示,为什么下面的代码会出现编译错误?
#if sizeof(int) == 4
#elif sizeof(int) == 8
#warning "sizeof(int) == 8"
#endif
A: 这个原因在于c语言标准规定预处理根本不能计算sizeof的值,且#if后面的条件表达式必须是整形常量,所以会提示错误。
Q10: 如果我希望可以控制int类型是4字节或者8字节大小,该怎么办?
A: 其实,这个不是程序员可以控制的,这是平台确定的。在mac下,gcc4.2.1, i386模式int是4字节,x86_64模式int也是4字节; 但是,long类型在i386是4字节,x86_64模式是8字节,知道上面这些信息后,最好自定义类型在恰当模式使用自己需要大小的类型。下面是对long类型的测试:
#include
int main()
{
printf("%u\n", sizeof(long));
return 0;
}
保存为preprocess_if.c .
gcc -o preprocess_if preprocess_if.c -arch i386编译后执行,结果为4.
gcc -o preprocess_if preprocess_if.c -arch x86_64编译后执行,结果为8.
作者:陈曦
日期:2012-7-28 18:19:55
环境:[Mac 10.7.1 Lion Intel i3 支持64位指令 gcc4.2.1 xcode4.2]
转载请注明出处