• 条件编译总报错,该如何解决?


    粉丝问答

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

    #ifdef EXTERN 
      #undef EXTERN
      #define EXTERN
    #else
      #define EXTERN extern
    #endif
    
    EXTERN int a;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

    分析

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

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

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

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

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

    #define EXTERN const
    
    • 1

    那么此时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"
    
    • 1
    • 2
    • 3

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

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

    未定义EXTERN

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

    #include
    #include"main.h"
    
    • 1
    • 2

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

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

    对比

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

    结果

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

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

    问题2-宏的处理顺序

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

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

    延伸问题-宏判断

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

    第一种

    #define EXTERN
    
    • 1

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

    #define VALUE (100)
    
    • 1

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

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

    #ifdef DEBUG
      //打印信息
    #endif
    
    • 1
    • 2
    • 3

    第二种

    #if EXTERN
    
    • 1

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

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

    #define EXTERN (1)
    
    • 1

    第三种

    #ifdef EXTERN
    
    • 1

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

    最后

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

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

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

    原文链接

    条件编译总报错,怎么办?这招好使

  • 相关阅读:
    使用vue3 和Springboot 通过 websocket实现前后端通信
    AtCoder Beginner Contest 231(D-F,H)
    JAVA毕业设计二手车商城计算机源码+lw文档+系统+调试部署+数据库
    FBX文件结构解读【文本格式】
    FRED应用:真实玻片设置
    kubesphere devops使用
    2014年亚太杯APMCM数学建模大赛A题无人机创造安全环境求解全过程文档及程序
    2022.08.18 第三组 高小涵
    MySQL数据库备份的三种方式
    Excel大量表格选择,快速定位表格
  • 原文地址:https://blog.csdn.net/DZRYWYBL/article/details/126473684