代码之家  ›  专栏  ›  技术社区  ›  Judge Maygarden

常量C表达式是在编译时还是在运行时计算的?

  •  31
  • Judge Maygarden  · 技术社区  · 17 年前

    如果我写一封信 使用其他预处理器常量执行操作的,是否每次运行时宏出现时都计算最终值?这取决于编译器中的优化,还是在标准中涵盖?

    例子:

    #define EXTERNAL_CLOCK_FREQUENCY    32768
    #define TIMER_1_S                   EXTERNAL_CLOCK_FREQUENCY
    #define TIMER_100_MS                TIMERB_1_S / 10
    

    32768/10 每次使用TIMER\u 100\u MS宏时是否在运行时发生?

    #define EXTERNAL_CLOCK_FREQUENCY    32768
    #define TIMER_1_S                   EXTERNAL_CLOCK_FREQUENCY
    #define TIMER_100_MS                3276
    

    总结

    9 回复  |  直到 9 年前
        1
  •  34
  •   David Thornley    17 年前

    TIMER_100_MS 在程序中是一种奇特的写作方式 32768 / 10 .

    32768/10 ,这是一个常数积分表达式。我认为该标准在这里不需要任何特定的行为(因为运行时和编译时的求值实际上是不可区分的),但任何半途而废的编译器都会在编译时求值。

        2
  •  27
  •   Johannes Schaub - litb    8 年前

    这里的大多数答案都集中在宏观替代的效果上。但我想他想知道

    32768 / 10
    

    在编译时计算。首先,这是一个算术常量表达式,另外还有一个整型常量表达式(因为它只有整数类型的文本)。实现可以在运行时自由地计算它,但它还必须能够在编译时计算它,因为

    1. 如果常量表达式在其表达式所具有的类型中不可表示,则它必须给出诊断消息

    如果编译器基本上可以在编译时计算结果,那么它应该使用该值,而不是在运行时重新计算。但也许还有一些理由这么做。我不知道。

    对不起,我回答的问题好像是关于C++的。注意,今天您询问了C。表达式中的溢出在C中被视为未定义的行为,不管它是否发生在常量表达式中。当然,第二点在C中也是如此。

    编辑 :作为注释,如果宏被替换为表达式,如 3 * TIMER_100_MS ,那么这将进行评估 (3 * 32768) / 10 不,它不会每次都在运行时发生,因为由于优先级和关联性规则,除法可能根本不会发生

        3
  •  13
  •   Brian L    17 年前

    我不知道有什么标准可以保证它会被优化。预处理器将用32768/10代替计时器100μMS,您可以通过运行gcc-c看到。要查看编译器是否在进一步优化,请运行gcc-S并检查汇编程序。在gcc 4.1中,即使没有任何优化标志,在编译过程中也会减少到常量:

    #include <stdlib.h>
    #include <stdio.h>
    
    #define EXTERNAL_CLOCK_FREQUENCY    32768
    #define TIMER_1_S                   EXTERNAL_CLOCK_FREQUENCY
    #define TIMER_100_MS                TIMER_1_S / 10
    
    int main(int argc, char **argv)
    {
      printf("%d\n", TIMER_100_MS);
    
      return(0);
    }
    
    gcc -S test.c
    cat test.s
    
    ...
        popl    %ebx
        movl    $3276, 4(%esp)
        leal    LC0-"L00000000001$pb"(%ebx), %eax
        movl    %eax, (%esp)
        call    L_printf$stub
    ...
    
        4
  •  11
  •   Roddy    17 年前

    #define TIMER_100_MS      TIMERB_1_S / 10
    

    #define TIMER_100_MS      (TIMERB_1_S / 10)
    

    考虑:

    i = 10 * TIMER_100_MS;
    

        5
  •  10
  •   Judge Maygarden    17 年前

    WG14/N1124 Committee Draft — May 6, 2005 ISO/IEC 9899:TC2 :

    6.6常量表达式


    条件表达式

    在翻译过程中进行评估,而不是 可用于任何需要常数的地方

    不应使用常量表达式 包含赋值、增量、, 操作员,除非他们是 包含在子表达式中,该子表达式

    每个常量表达式应 its的epresentable值的范围 类型

        6
  •  9
  •   Bill the Lizard    17 年前

    每次使用 时间b_100_MS 宏?

    代码中使用的每个地方 TIMERB_100_MS ,它将被替换为 32768 / 10

        7
  •  8
  •   Norman Ramsey    17 年前

    伙计们,这种转换被称为“常数折叠”,甚至大多数学生编译器都会这样做。只要你有一个不是你或你大学室友的编译器,并且你正在编译一种静态类型的语言,即使没有打开优化,你也可以依赖它。如果你在处理一种可以改变语言含义的古怪的动态语言,那就不同了 / .

        8
  •  0
  •   Radon    9 年前

    在这里查看更多编译和优化方面: http://www.agner.org/optimize/#manuals

        9
  •  -4
  •   David Lehavi    17 年前

    编辑

    一位评论员要求提供参考-

    窗体的控制行

    #define identifier token-sequence 
    

    标识符的后续实例 具有给定的令牌序列; 讨论了罗肯序列

    编辑结束