【问题标题】:Why is const int x = 5; not a constant expression in C?为什么 const int x = 5;不是C中的常量表达式?
【发布时间】:2020-10-02 20:25:43
【问题描述】:

我以为 C 对我没有更多的惊喜,但这让我感到惊讶。

    const int NUM_FOO = 5;
    ....

    int foo[NUM_FOO];
==>error C2057: expected constant expression

我的 C++ 经验使我在内部尽可能地弃用 #define。所以这个真的是一个惊喜。 VS2019,用/TC编译。我认为 C99 无论如何都允许可变大小的数组。

  1. 谁能解释为什么会发生拒绝,因为编译器在编译时肯定知道数组的大小?

  2. C99不是允许变长数组吗?

【问题讨论】:

    标签: c constants visual-studio-2019 c99 variable-length-array


    【解决方案1】:
    1. C 中的const 未声明编译时常量。如果您想避免使用 #define 并想要一个可以出现在调试器中的符号名称,则可以使用 enum 常量。

    2. C99 确实支持 VLA。但是,VS2019 does not support C99.

    【讨论】:

      【解决方案2】:

      带有const 限定符的变量不符合常量表达式的条件。

      C11 standard 中关于常量表达式状态的第 6.6p6 节

      整数常量表达式应具有整数类型和 只能有整数常量的操作数,
      枚举常量、字符常量、sizeof 表达式 其结果是整数常量、_Alignof 表达式和 作为强制转换的直接操作数的浮动常量。投掷 整数常量表达式中的运算符只能转换 算术类型转换为整数类型,除了作为 sizeof_Alignof 运算符的操作数

      请注意,const 限定的整数对象不包括在内。

      这意味着int foo[NUM_FOO];是一个变长数组,定义如下从6.7.6.2p4小节:

      如果大小不存在,则数组类型是不完整类型。如果 大小是*,而不是表达式,数组类型 是一个未指定大小的变长数组类型,可以 仅用于具有函数原型的声明或类型名称 范围;这样的数组仍然是完整的类型。 如果尺寸是 整数常量表达式和元素类型有一个已知的 常量大小,数组类型不是变长数组 type; 否则,数组类型为变长数组 输入。

      至于您遇到的错误,那是因为 Visual Studio 不完全符合 C99 并且不支持变长数组。

      【讨论】:

        【解决方案3】:

        在 C 中,此声明:

        const int NUM_FOO = 5;
        

        不会使 NUM_FOO 成为常​​量表达式。

        要记住的事情(是的,这有点违反直觉)是const 并不意味着恒定常量表达式大致是可以在编译时求值的表达式(如2+242)。 const 类型限定符,尽管它的名字显然来源于英文单词“constant”,但真正的意思是“只读”。

        例如,考虑一下这些是完全有效的声明:

        const int r = rand();
        const time_t now = time(NULL);
        

        const 仅表示您无法在 rnow 初始化后修改它们的值。这些值显然要等到执行时间才能确定。

        (C++ 有不同的规则。它确实使NUM_FOO 成为常​​量表达式,并且该语言的更高版本为此添加了constexpr。C++ 不是C。)

        至于可变长度数组,是的,C 在 C99 中添加了它们(并在 C11 中使其成为可选)。但正如jamesdlin's answer 所指出的,VS2019 不支持 C99 或 C11。

        (C++ 不支持 VLA。这:const int NUM_FOO = 5; int foo[NUM_FOO]; 在 C99 和 C++ 中都是合法的,但原因不同。)

        如果要定义int 类型的命名常量,可以使用enum

        enum { NUM_FOO = 5 };
        

        或老式宏(不限于键入int):

        #define NUM_FOO 5
        

        jamesdlin's answerdbush's answer 都是正确的。我只是添加更多上下文。

        【讨论】:

          【解决方案4】:

          除了所有好的现有答案之外,const-qualified 对象基本上不能是一个常量表达式的原因,在“可以在编译时评估”的意义上(如基思的回答中提到),是它可以具有外部链接。比如你可以在foo.c

          const int NUM_FOO = 5;
          

          bar.c:

          extern int NUM_FOO;
          ...
          int foo[NUM_FOO];
          

          本例中,编译bar.c时无法知道NUM_FOO的值;直到您选择链接foo.obar.o 才知道。

          C 的“常量表达式”模型与允许翻译单元(源文件)独立翻译(编译)为不需要进一步高级转换来链接的形式的属性密切相关。这也是为什么你不能在常量表达式中使用地址的原因,除了地址常量表达式,它本质上被限制为一个对象的地址加上一个常量。

          【讨论】:

          • True(但当初始值可见时,C++ 确实允许这样做;我不确定确切的规则)。
          猜你喜欢
          • 2013-09-25
          • 1970-01-01
          • 2014-04-20
          • 1970-01-01
          • 2017-02-25
          • 1970-01-01
          • 2016-03-01
          • 2019-09-11
          • 1970-01-01
          相关资源
          最近更新 更多