作者:陈曦
日期: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<stdio.h> INCLUDE_STDIO int main() { return 0; }
保存为preprocess_header.c
A: 如果是预处理出了问题,我们可以使用-E查看预处理后的结果,来分析到底哪里出了问题。
Q2: 由上面看,好像没有什么问题。
A: 看起来是没有什么问题,问题在于上面的结果是预处理后的结果。编译器编译预处理后的源代码还出现了#include, 编译器是不识别的,所以会报错。换句话说,预处理做预处理的事情,编译器做编译源代码的事情(不过编译器常常被看做包括预处理的功能).预处理器可以处理#include, 而编译器根本无法识别#include.
Q3: 预处理当发现INCLUDE_STDIO符号是#include<stdio.h>时为什么没有继续预处理此头文件的内容?
A: 这就在于c语言标准规定#define定义的符号是进行重新预处理的,但是仅限于#define的符号,遇到#include是不做处理的。
Q4: 有时,需要测试数据,写了好多相同名称开头的变量,最后进行赋值,有什么好方法?
A: 这当然需要用到##符号了,它可以正确生成你需要的变量。如下代码:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> #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 <stdio.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> #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 <stdio.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> #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 <stdio.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> #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; }
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 <stdio.h> #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 <stdio.h> 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]
转载请注明出处