当前位置:首页 > 公众号精选 > typedef
[导读]有个粉丝关于条件编译的问题,程序一直编译报错。我整理了关键的部分,下面代码是头文件中的定义。

粉丝问答

有个粉丝关于条件编译的问题,程序一直编译报错。我整理了关键的部分,下面代码是头文件中的定义。

#ifdef EXTERN  #undef EXTERN #define EXTERN #else #define EXTERN extern #endif EXTERN int a;

报错的内容就是error: unknown type name 'EXTERN,报错的地方是EXTERN int a;

分析

乍一看条件编译好像并没有什么不妥,但编译器确实已经报错了,那就只能说确实没有定义?。

首先,用户的意图是想用宏EXTERN代替关键字extern。

针对此模块,要保证EXTERN就是extern,所以需要判断外部是否已经定义了EXTERN。如果已经定义了,则需要取消宏定义后再重新定义。

那这里又有同学问了,如果外部已经定义了EXTERN,为什么还要取消宏定义再重新定义呢,直接用不可以吗?

严格来说是不行的,假设外部有个地方定义了

#define EXTERN const 

那么此时EXTERN就不再是extern了,意义完全变了。这时候又有同学说了,怎么可能会有人把EXTERN定义成const呢,我只能说,我也希望不会出现这种情况吧...

如果外部没有定义EXTERN会进入else分支,则在本模块直接定义。

看起来都是这么合情合理。

查看预编译文件

上面已经分析了一次,逻辑上好像没问题(指出问题了我还这么继续写下文)。既然分析没有问题,那我们直接去看预处理文件吧,简单写个Demo,头文件不变。

gcc生成预编译命令为gcc -E .\main.c -o main.i参数E代表预编译处理,o代表输出,后面跟一个文件名。代表将源文件预编译处理将处理的结果输出到main.i文件中。

内部定义EXTERN

假设我们在源文件包含头文件之前已经定义了EXTERN,如下:

#include #define EXTERN extern #include"main.h" 

然后对源文件预编译处理并截取关键信息:

# 10 ".\\main.h" int a;
# 4 ".\\main.c" 2 

未定义EXTERN

源文件部分代码,此时没有定义EXTERN。

#include #include"main.h" 

同样对源文件预编译处理并截取关键信息:

# 10 ".\\main.h" extern int a;
# 4 ".\\main.c" 2 

对比

当外部定义了EXTERN时,头文件预编译的结果没有对变量a做extern声明。外部定义了EXTERN时,头文件预编译的结果对变量a做了extern声明。

结果

宏的本质是代码替换,上述对比表明,当外部定义了EXTERN时,在处理EXTERN int a;前EXTERN变成了一个空宏。

那我们再回头看看头文件中的条件编译,如果外部定义了EXTERN则取消宏定义,并重新定义EXTERN,但是在重新定义宏的时候,这里仅仅是定义了EXTERN。并没有指明EXTERN是extern。所以编译总是提示unknown type name 'EXTERN

问题2-宏的处理顺序

问题已经找出来了,但是粉丝现在又有了新的疑问,同一个宏在多个文件是如何处理的?

其实上面的两种测试代码已经能够表明了,因为预编译也是按照先后顺序依次处理的,所以宏的处理过程也是按照先后顺序。这也是我为什么在包含main头文件之前定义EXTERN,而不是在包含头文件之后定义EXTERN

延伸问题-宏判断

发现有些同学对于一些宏的判断有些分不清楚,不清楚应该在何种场合使用。在此仍然以EXTERN为例吧。

第一种

#define EXTERN 

依稀记得当时刚毕业出来工作,当我第一次看到代码有这种定义的时候,也是挺懵逼的。甚至怀疑是不是写错了?因为我当时认为后面少了数字。为什么是少数字呢,因为在我的印象中宏都是下面这种使用方法。

#define VALUE (100) 

第一种方式宏定义仅仅代表定义了这个标识符,而不用关心它具体是什么。

常用的就是在调试模式下定义DEBUG,发布程序的时候将DEBUG关闭。主要是用来打印一些信息,方便程序员调试用的,如下:

#ifdef DEBUG //打印信息 #endif 

第二种

#if EXTERN 

注意这种使用方法,是一个判断语句,跟我们编程用if判断逻辑相同,也就是EXTERN不为0才能成立。

这种用法通常都是在定义宏时直接定义成1,例如:

#define EXTERN (1) 

第三种

#ifdef EXTERN 

这种方式和第二种有区别,这种判断只关注有没有定义,但凡你定义了,不管是什么牛马蛇神,条件都是成立的。

最后

关于宏相关的知识点,之前写过一篇文章。有兴趣的可以看看。

C语言中宏定义的盲区

这篇文章主要也是宏相关的,主要是实际编程中的问题。条件编译报错,可以去查看预处理文件,反向分析。如果你对C理解得比较深,可能一眼就能看出问题所在了。

每个人都是从小白过来的,需要时间慢慢沉淀,慢慢也就好了,加油~

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

首先要看英文和C语言的关系,有多大层度是需要英文的支持。我们都知道C语言中一共有32个关键词,控制语句仅有9种,也就是说只需要掌握以上内容就可以对C语言进行应用,对于一些复杂的指针等更合英文没有关系。我相信短短的32个关...

关键字: C语言

C语言是一门面向过程的、抽象化的通用程序设计语言,广泛应用于底层开发。C语言能以简易的方式编译、处理低级存储器。C语言是仅产生少量的机器语言以及不需要任何运行环境支持便能运行的高效率程序设计语言。尽管C语言提供了许多低级...

关键字: C语言 单片机

为什么会写篇栈变化的文章?做系统分析的话你肯定遇到过一些crash,oops等棘手问题,一般大家都会用gdb,objdump或者addr2line等工具分析pc位置来定位出错的地方。但是这些分析工具背后的本质原理就不见得...

关键字: 函数 ARM C语言 AI

点击蓝字关注我们请把努力当成一种习惯,而不是三分钟热度。每一个你羡慕的收获,都是努力用心拼来的。相信人生不会亏待你。12月6日华清远见武汉中心迎来了嵌入式21111班的开班典礼班主任介绍班级班规无规矩不成方圆,班级班规的...

关键字: 嵌入式 C语言 LAN TARGET

一直觉得C语言较其他语言最伟大的地方就是C语言中的指针,有些人认为指针很简单,而有些人认为指针很难,当然这里的对简单和难并不是等价于对指针的理解程度。为此在这里对C语言中的指针进行全面的总结,从底层的内存分析,彻底让读者...

关键字: 微信 C语言 UNSIGNED INCLUDE

提到C语言,很多人的第一反应是:C语言,不都过时了?还有必要研究吗? 当然有必要。用一个词来形容:C生万物。听起来有点浮夸,但也没说错。现今这个世界上,几乎所有重要的软件都跟C有关系: 主流操作系统是C写的,绝大多数语言...

关键字: C语言

▼点击下方名片,关注公众号▼欢迎关注【玩转单片机与嵌入式】公众号,回复关键字获取更多免费资料。回复【加群】,限时免费进入知识共享群;回复【3D封装库】,常用元器件的3D封装库;回复【电容】,获取电容、元器件选型相关的内容...

关键字: C语言

▼点击下方名片,关注公众号▼欢迎关注【玩转单片机与嵌入式】公众号,回复关键字获取更多免费资料。回复【加群】,限时免费进入知识共享群;回复【3D封装库】,常用元器件的3D封装库;回复【电容】,获取电容、元器件选型相关的内容...

关键字: C语言 软件

c语言入门C语言一经出现就以其功能丰富、表达能力强、灵活方便、应用面广等特点迅速在全世界普及和推广。C语言不但执行效率高而且可移植性好,可以用来开发应用软件、驱动、操作系统等。C语言也是其它众多高级语言的鼻祖语言,所以说...

关键字: C语言

星标「嵌入式大杂烩」,一起进步!进度条的应用在软件中无处不在,拷贝一个文件需要一个进度条,加载一个文件也需要一个进度条,来标志完成与否。那么,一个进度条有哪些元素呢:一个不断向右生长的容器(直观的看出当前的进度)一个数据...

关键字: C语言

typedef

6 篇文章

关注

发布文章

编辑精选

技术子站

关闭