1、#error的用法
#error编译指示字用于生成一个编译错误消息,并让预编译器停止编译,这个错误消息是开发人员自定义的。用法:#error Message (Message不需要用双引号引起来)
2、#warning的用法
#warning编译指示字用于生成一个编译警告,输出到编译器的消息窗口,当编译器不会停止编译。用法:#warning Message (Message不需要用双引号引起来)
示例:
#include <stdio.h>
int main()
{
#ifndef COMMAND
#warning Compilation will be stoped ...
#error No defined Constant Symbol COMMAND
#endif
printf("%s\n",COMMAND);
return 0;
}
首先,我们试着用命令行预编译一下:
如上所示,没有定义COMMAND宏,警告和错误消息都被显示出来了。
预编译时使用-D选项定义COMMAND宏:
可以看到编译成功,没有任何错误提示!
最后,编译成可执行文件,运行一下试试:
可以看到,顺利编译并运行了。
关于编译选项-D的说明:
gcc -D ..... 这个命令用来在编译时定义宏,可以给宏指定值,等效于我们在代码中直接定义宏,使用格式如上图,值得说明的是,这里面输入的引号、空格都使用的是C中的转义符,\40是用八进制表示的值为32的空格字符。
-D 参数在实际开发中还是经常使用的,比如可通过定义不同的宏,实现选择性编译,编译时定义不同的宏就编译相对应的代码。
3、#line的用法
#line编译指示字用于强制指定新的行号和编译文件名,并对源程序的代码重新编号,本质上#line编译指示字是对__LINE__和__FILE__内置宏的重定义。用法:#line number filename (filename可省略)
例如:
#include <stdio.h>
int main()
{
printf("行号:%d\n", __LINE__);
printf("文件名:%s\n", __FILE__);
printf("%s\n","定义#line编译指示字后...");
#line 14 "Hello.c"
printf("行号:%d\n", __LINE__);
printf("文件名:%s\n", __FILE__);
return 0;
}
运行结果:
即从定义#line指示字的下一行开始,行编号变为14,这个只是字很早以前所有的源代码都在一个文件中时会使用它,用来区分当前的代码时谁写的,原本属于哪个文件!结构化程序设计以后,很少使用了吧。
4、#运算符的用法
#运算符用在预编译时期,用于将宏参数转换为字符串,即是加上双引号。例如:
#include <stdio.h>
#define COMMAND(x) #x
int main()
{
printf("%s\n", COMMAND(Hello world!));
printf("%s\n", COMMAND(100));
printf("%s\n", COMMAND(while));
printf("%s\n", COMMAND(return));
return 0;
}
预编译后:
int main()
{
printf("%s\n", "Hello world!");
printf("%s\n", "100");
printf("%s\n", "while");
printf("%s\n", "return");
return 0;
}
可以看到,传入的宏参数自动被转换成了字符串。
#运算符的妙用:
#include <stdio.h>
#define CALL(f, p) (printf("Call function %s\n", #f), f(p))
int square(int n)
{
return n * n;
}
int f(int x)
{
return x;
}
int main()
{
printf("1. %d\n", CALL(square, 4));
printf("2. %d\n", CALL(f, 10));
return 0;
}
运行结果:
可以看到,运行结果中输出了当前调用的函数的名称,这是用函数实现不了的功能,如果用函数来实现,只能在程序中把要输出的函数名写死,没有使用宏这么通用便捷!
5、##运算符的使用
##运算符用于在预编译期粘连两个符号,增大了宏的使用灵活性!示例:
#include <stdio.h>
#define NAME(n) name##n
int main()
{
int NAME(1);
int NAME(2);
NAME(1) = 1;
NAME(2) = 2;
printf("%d\n", NAME(1));
printf("%d\n", NAME(2));
return 0;
}
预编译后:
int main()
{
int name1;
int name2;
name1 = 1;
name2 = 2;
printf("%d\n", name1);
printf("%d\n", name2);
return 0;
}
可以看出,预编译时,宏参数n被替换,并和name连接成一个整体,然后整体替换NAME宏。
##运算符的妙用:
#include <stdio.h>
#define STRUCT(type) typedef struct _tag_##type type;\
struct _tag_##type
STRUCT(Student)
{
char* name;
int id;
};
int main()
{
Student s1;
Student s2;
s1.name = "s1";
s1.id = 0;
s2.name = "s2";
s2.id = 1;
printf("%s\n", s1.name);
printf("%d\n", s1.id);
printf("%s\n", s2.name);
printf("%d\n", s2.id);
return 0;
}
预编译后:
typedef struct _tag_Student Student;struct _tag_Student
{
char* name;
int id;
};
int main()
{
Student s1;
Student s2;
s1.name = "s1";
s1.id = 0;
s2.name = "s2";
s2.id = 1;
printf("%s\n", s1.name);
printf("%d\n", s1.id);
printf("%s\n", s2.name);
printf("%d\n", s2.id);
return 0;
}
这样使用它,相当于扩展了C语言,能用一种“新”的方式去定义结构体,高通以前就用过这种方式,可提高代码的可读性与可维护性!
看到预编译后的代码,可能会有疑问,可以在结构体定以前就对结构体使用typedef定义别名么?实际上是可以的,typedef关键字可以随意使用,只要被重定义的类型有被声明就行!