代码之家  ›  专栏  ›  技术社区  ›  David

C宏观评价

  •  1
  • David  · 技术社区  · 6 年前

    我想声明一个静态分配的数组。 让我们看看下面的代码:

    #define MAX(a,b) ((a)>(b)?(a):(b))
    #define FAST            16
    #define SLOW            6
    #define MAX_NUM         MAX(FAST,SLOW)
    U8*   pBuffers[MAX_NUM];
    

    当GCC编译器计算MAX_NUM时(FAST和SLOW是常量)? 我想确保MAX_NUM是常量,并作为编译或预处理器的一部分进行计算。

    3 回复  |  直到 6 年前
        1
  •  2
  •   Laurent H. chthonicdaemon    6 年前

    启动编译器时,将(按顺序)执行以下阶段:

    • 预处理:它管理“define”、“ifdef/”endif。。。
    • 代码生成:它生成在目标CPU上可运行的机器代码
    • 优化:它根据用户选项进行优化

    预处理 阶段,预处理器将例如用以下内容“替换”您的行:

    U8*   pBuffers[MAX(FAST,SLOW)]
    

    然后:

    U8*   pBuffers[((FAST)>(SLOW)?(FAST):(SLOW))]
    

    最后:

    U8*   pBuffers[((16)>(6)?(16):(6))]
    

    事实上,预处理器不是很聪明,也不进一步。

    代码生成 阶段,您的线路将被解释为:

    U8*   pBuffers[16]
    

    因为代码生成器非常聪明。

        2
  •  1
  •   zwol    6 年前

    The C standard 要求使用 整数常量表达式 ,在这种情况下,需要在编译时对其进行完全评估。(唯一的例外是“可变长度数组”,这些数组必须是函数局部变量,并且“自动存储持续时间”不是静态分配的。)

    因此,你的问题的一个答案是 你不用担心 . 如果你写信

    WHATEVER_TYPE variable[SOME EXPRESSION];
    

    在文件范围内,或者 SOME EXPRESSION 将在编译时计算为常量,否则编译将失败,并将得到错误。

    但一个更有用的答案是,解释如何亲眼看到 一些表达 是一个整型常量表达式,当您读取代码时。首先,您必须在精神上展开所有宏。然后,您可能会得到某种类型的算术表达式(如果不是,则是语法错误)。

    这个算术表达式是 常数 表达式,如果它没有副作用,不进行任何函数调用,也不引用任何变量的值(即使它是 const ) ( enum 不过,常量和字符串文本都很好,而且 sizeof variable 只要 variable 完全声明,不是可变长度数组)。它是一个 整数 此外,如果常量表达式不尝试执行任何浮点运算或指针运算(但允许您将浮点文字作为强制转换的直接操作数编写;例如 ((int)3.1415926) 是一个整数常量表达式)。

    所以,以你为例,

    #define MAX(a,b) ((a)>(b)?(a):(b))
    #define FAST            16
    #define SLOW            6
    #define MAX_NUM         MAX(FAST,SLOW)
    U8*   pBuffers[MAX_NUM];
    

    在宏观扩张之后

    U8* pBuffers[((16)>(6)?(16):(6))];
    

    方括号内的表达式没有副作用,不进行任何函数调用,不引用任何变量的值,也不执行任何浮点或指针运算,因此它是一个整型常量表达式,编译器需要在编译时对其求值。

    相比之下,如果使用MAX的定义:

    static inline size_t MAX(size_t a, size_t b)
    { return a > b ? a : b; }
    

    那么宏观扩张将产生

    U8* pBuffers[MAX(16, 8)];
    

    方括号内的表达式将进行函数调用,因此它不是整型常量表达式,甚至不是常量表达式,您将得到编译时错误。

    (Fy,C++中的规则要复杂得多;如果你需要知道这个问题,请问一个新问题。)

        3
  •  0
  •   Umair    6 年前

    宏总是在编译过程开始之前求值的。所以这段代码不用担心,应该可以正常工作。

    同时,这整件事都依赖于编译器,我相信 gcc 它会很好用的。也许,对于一些裸金属应用,它可能会发出警告。