首页 /  技术专区  /  C 宽屏模式 >

预编译关键字——#error

1、有哪些关键字

        #error 

        defined 

        #line

        #和## 

        #pragma

2、#error

不过我们先不忙着介绍#error,先讲讲如果宏定义不存在的话,如何报宏定义不存在的错误。

2.1 如果引用某个宏时,该宏没有被定义的话,怎么报错

(1)例子

a.c 

#include <stdio.h>

int main(void)
{
    printf("%f\n", PI);
    return 0;
}

由于PI这个宏没有定义,因此预编译阶段处理时,没办法对PI进行替换,所以PI这个符号会一直留到编译阶段,第二阶段编译时就会报PI不存在的错误。

也就是说,引用的宏没有定义时,正常情况下是由第二阶段编译来报的错误。


但是由“编译阶段”来报宏的错误存在如下缺点:

1)编译器无法精准报错

它只会提示缺少符号PI,但是并不能提示缺少的PI是一个宏,因为编译器只知道PI这个符号没有定义,但是并不知道PI其实是一个宏,所以我们自己排错时,需要我们自己区分PI是个函数、变量、还是宏。

总之,报错时如果能够告诉你符号PI是个宏的话,可以帮助我们快速排查PI这个宏相关的错误。

疑问:我自己写的代码,我自己难道还不知道PI是个啥吗?

答:问题是

(a)很多复杂的源码并不是自己写的,而是你到网上下载别人的源码,如果只是提示缺符号PI,你并不知道PI到底是个啥,不利于精确排错。

(b)就算是你自己的写c工程,当工程写复杂后,定义的符号必然非常多,冷不丁的提示缺符号PI,其实你也很难一下反应过来PI是一个宏。


2)当源码很庞大时,编译时报错会非常迟缓

对于庞大的项目源码来说,往往可能有成百上千个.c文件,编译时间动辄就会花上30分~十几小时,某个引用“未定义宏”的.c,可能要等30分钟后才编译到它,然后再报符号未定义的错误,此时这个报错就太迟缓了。

对于编译的四个过程来说,第二个阶段“编译”所花费的时间最久,预编译的时间最短,所以如果在“预编译阶段”就报宏的错误,而且准确提示未定义的符号是一个宏的话,宏的排错效率非常高。

而且宏本来就是在“预编译阶段”处理的,所以在“预编译”阶段报宏的错误,也是最贴切的。

所以综合起来就是,宏没有定义的错误就应该在“预编译”阶段进行报告,而不是留到编译阶段。

当然那些只能编译阶段报的错误,你就不能到预编译阶段报错了,比如某个变量、函数没有定义,像这种的就只能在编译阶段报错了。

(2)准确报“宏不存在的错误”有什么意义?

你现在可能觉得没有什么意义,那是因为你的级别还不够,你还局限于你自己写的小程序,对于小的C程序来说,你自己仔细挨个检查文件就能搞清楚未定义的符号是个宏,还是个啥。

但是我们在实际开发中(比如嵌入式开发),往往需要下载官方大型的C源码(比如uboot,Linux OS)来进行移植,在移植中修改源码时,宏的修改会比较多的,因为条件编译需要这些宏来打开和关闭,所以在预编译阶段如果就能准确的提示缺少某个宏的话,可以帮助我们快速排查宏的错误。

对于下载的官方源码来说往往很复杂,如果不能准确提示缺少的符号是个宏,而需要你自己去阅读源码来确定这个符号是个宏,然后解决宏的错误,这无疑就太痛苦了。

#error这个玩意就能够帮助我们在“预编译”阶段,及时准确的报宏相关的错误。

2.2 正式说说#error

(1)作用

        在“预编译阶段”打印提示相关信息。

        可以打印任何你要的信息,具体什么信息,可以由你自己定义。

例子:

a.c 

#include <stdio.h>
#error helloworld  //注意helloworld不需要""

int main(void)
{
    return 0;
}

其实“干用#error”的意义并不大,前面就说过,#error常用于提示宏相关的错误,比如举一个简单的例子:

#include <stdio.h>

int main(void)
{
#ifdef PI 
    printf("%d\n", PI); 
#else 
# error PI not defined 
#endif
    retutn 0;
}

(2)#error的特点总结

        1)#error是在“预编译阶段”由预编译器处理的“预编译关键字”

        2)执行#error后,“预编译”的处理过程会被立即终止

        3)#error输出字符串时,信息内容不需要使用""括起来

        #error helloworld

(3)#error提示宏不存在时怎么办

1)如果该宏定义在了某个.h中

我们只需要将该.h包含到.c中,该宏就有了。

疑问:我怎么知道这个宏在哪个头文件呢?

如果是常用宏,一般我们自己都知道这个宏定义在了哪个.h中,比如stdin这个宏就定义在了stdio.h中。

但是在移植官方源码时,源码会非常复杂,涉及到的.h会非常多,很难搞清楚在哪个.h中,不过源码除了会使用

#error报某个宏未定义外,还会精确的提示你这个宏在哪个.h,你应该包含这个.h。


举一个简单例子演示下。

#ifndef stdin
#error macro stdin not defined, please include stdio.h
#endif

int main(void)
{
    fprintf(stdin, "hello world\n"); //等价于printf("hello world\n");
    return 0;
}

这个例子很傻瓜,没有太大的现实意义,但是在复杂的C工程源码中,这种类似的做法确实非常常见的。

很多同学在编译某些源码时,经常看到“你需要包含什么头文件”的提示,就是使用#error在预编译阶段提示的。

2)如果宏没有定义在某个头文件中呢,怎么办呢

此时可能就需要我们在某个.h或者.c亲自去定义了。

1)例子1

#include <stdio.h>

int main(void)
{
#ifdef PI 
    printf("%d\n", PI); 
#else 
# error PI not defined 
#endif
    retutn 0;
}

直接在.c头上、或者在自己的某个.h中,自定义PI(#define PI 3.1415)。

对于自己写的简单c程序来说,一般都是这么办的就这么办。

2)例子2

对于从官方下载的复杂c源码来说,好些宏是与条件编译相关,由于源码太过复杂,而且又不是你自己写的,如果你想通过阅读源码,然后再源码中去定义缺失的宏,这种方式几乎是不可能实现的。

所以在复杂的C源码中,如果是与条件编译相关的宏,我们需要去修改配置文件,已得到相应的宏。



头像
0/200
图片验证码