C语言特点 有预处理,预处理,它有时很神奇----小话c语言(24)

作者:陈曦

日期: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

0818b9ca8b590ca3270a3433284dd417.png

A: 如果是预处理出了问题,我们可以使用-E查看预处理后的结果,来分析到底哪里出了问题。

0818b9ca8b590ca3270a3433284dd417.png

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;

}

使用预处理得到如下:

0818b9ca8b590ca3270a3433284dd417.png

由上图可以看出,确实使用了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]

转载请注明出处

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值