【问题标题】:Why is int x[n] wrong where n is a const value?为什么 int x[n] 错误,其中 n 是 const 值?
【发布时间】:2016-05-11 18:20:27
【问题描述】:

我不明白为什么这样做是错误的:

const int n = 5; 
int x[n] = { 1,1,3,4,5 };

即使n 已经是一个常量值。

虽然这样做似乎适合 GNU 编译器:

const int n = 5;
int x[n]; /*without initialization*/

我知道 C99 的 VLA 功能,我认为它与正在发生的事情有关,但是 我只是需要澄清一下背景中发生的事情。

【问题讨论】:

  • 我怀疑如果你在n 的定义上加上static,它会起作用; static const 将是真正的全局编译时间常量,而不是堆栈存储常量。
  • @ShadowRanger:C 不是 C++。 const 的语义是区别之一。 C 没有符号常数。例如。 1 是 C 标准中的 整数常量const int i 仍然是一个变量!
  • const 只是一个承诺给编译器你不会直接或间接更改变量。对于第一个大多数编译器会发出警告,对于后者(例如通过指针),它们在检测所有可能的违规行为方面非常有限。如果您违反合同,您将调用未定义的行为。
  • @Olaf,C 有 一些 符号常量,您可能不会想到它们,即枚举。
  • @JensGustedt:是的,我总是忘记那些。它们是一些特殊的类型,主要是因为您不能显式指定它们的类型(C11 也应该从 C++11 复制一些东西,使 enums 最终有用)。还有两个“布尔”常量_True_False,它们不是用户可定义的。嗯,正如您所写的“即”,这意味着还有其他人:我忘记了任何其他用户可定义的符号常量吗? (严肃的问题,我可能忽略了一些简单的事情——如果你在树林里,你只会看到树)

标签: c arrays constants c99


【解决方案1】:

要记住的关键是const 和“常量”意味着两个完全不同的东西。

const 关键字真正的意思是“只读”。 constant 是数字文字,例如 421.5(或枚举或字符常量)。 常量表达式是一种特殊类型的表达式,可以在编译时求值,例如2 + 2

所以给出一个声明:

const int n = 5;

表达式n 指的是对象 的值,它不被视为常量表达式。典型的编译器会优化对n 的引用,将其替换为用于文字5 的相同代码,但这不是必需的——以及表达式是否为常量 的规则由语言决定,而不是由当前编译器的聪明程度决定。

const(只读)和常量(编译时计算)之间的区别示例如下:

const size_t now = time(NULL);

const 关键字意味着你不能在初始化之后修改now 的值,但是time(NULL) 的值显然要等到运行时才能计算出来。

所以这个:

const int n = 5;
int x[n];

在 C 中并不比没有 const 关键字更有效。

语言可以(恕我直言可能应该)将n 评估为常量表达式;它只是没有这样定义。 (C++ 确实有这样的规则;有关血腥细节,请参阅 C++ 标准或体面的参考资料。)

如果你想要一个值为5的命名常量,最常见的方法是定义一个宏:

#define N 5
int x[N];

另一种方法是定义一个枚举常量:

enum { n = 5 };
int x[n];

枚举常量是常量表达式,并且总是int 类型(这意味着此方法不适用于int 以外的类型)。这可以说是对enum机制的滥用。

从 1999 年标准开始,数组可以定义为非常量大小;这是一个 VLA 或可变长度数组。此类数组只允许在块范围内使用,并且可能没有初始化程序(因为编译器无法检查初始化程序是否具有正确数量的元素)。

但鉴于您的原始代码:

const int n = 5; 
int x[n] = { 1,1,3,4,5 };

你可以让编译器从初始化器中推断出长度:

int x[] = { 1,1,3,4,5 };

然后您可以根据数组的大小计算长度:

const int x_len = sizeof x / sizeof x[0];

【讨论】:

  • “常量是数字文字”。这是真的吗?一定还有一些其他类型的常量?
  • OT,但是:“C++ 确实有这样的规则 [将 n 评估为常量表达式]”。好吧,只是有时;要使这句话真正准确,您需要替换 constexpr。仅使用 const 在某些情况下可能会起作用,但它不能保证 C++ 中的“常量表达式”,具体取决于上下文。例如:stackoverflow.com/questions/18996258/…
  • @artm:你说得对,C 中有 4 种常量:整数、浮点、枚举和字符。
  • @underscore_d:是的,C++ 规则有点复杂——但它确实适用于这种特殊情况。我添加了一些黄鼠狼的话。
【解决方案2】:

为什么int x[n] 是错误的nconst 值?

n 不是常数。 const 只承诺 n 是一个“只读”变量,不应在程序执行期间修改。
请注意,在 中,与 不同,const 限定变量不是常量。因此,声明的数组是一个变长数组。
您不能使用初始化列表来初始化可变长度数组。

C11-§6.7.9/3:

要初始化的实体的类型应该是一个未知大小的数组或一个完整的对象类型,不是可变长度数组类型

您可以使用#defineenumn 设为常量

#define n 5
int x[n] = { 1,1,3,4,5 };   

【讨论】:

  • @Yahia Farghaly n 是一个变量,它是 const,但不是常量。 const 实际上意味着在 C 中是只读的。
  • @chux:其实并不保证是只读的。这只是程序员对编译器的保证。编译器将无法检测到所有违规行为,运行时环境也不需要。
  • @Oalf - 没错,这是对 OP 的过度简化的误导。
  • 注意:在 C++ 中,const-qualified 变量可能是也可能不是常量表达式,例如const int n = rand() % 5; int x[n]; 在 C 中是正确的,但在 C++ 中是正确的!
  • @artm;您可以使用#defineenum
【解决方案3】:

如果你要详尽地初始化一个数组,那么让编译器推断数组大小会更容易、更安全、更易于维护:

int x[] = { 1,1,3,4,5 };
const int n = sizeof(x) / sizeof(*x) ; 

然后要更改数组大小,您只需更改初始化器的数量,而不是更改大小要匹配的初始化器列表。当有许多初始化程序时特别有用。

【讨论】:

    【解决方案4】:

    即使nconst,您也不能使用它来定义数组的大小,除非您希望创建 VLA。但是,您不能使用初始化列表来初始化 VLA。

    使用宏创建固定大小的数组。

    #define ARRAY_SIZE 5
    
    int x[ARRAY_SIZE] = { 1,1,3,4,5 };
    

    【讨论】:

    • @chux:你不能拥有静态 VLA。
    • 如果您想将常量保持在块或函数范围内,枚举常量也可以:enum { n = 5 }; int x[n] = { 1,1,3,4,6 };
    • 嗯,这不是一个严肃的建议;一个不会污染符号的全局空间,暗示潜在的并且很难找到错误,只是为了创建一个固定大小的数组。
    • 呃。 懒得改名字就成了这个糟糕的建议。每个人,永远不要命名任何这样的宏。想象一下劫持任何常用名称(尤其是单个字母)和尤其是极其通用的n 可能导致的恐怖。宏(或至少 99.9% 的宏)应该有HUGE_SHOUTY_AND_UGLY_NAMES精确地 以使它们难以使用并且不吸引(ab)使用。
    • @underscore_d,n 的使用仅用于说明目的,但要点很好。
    【解决方案5】:

    您的代码在语义上与myfunc() 不同吗:

    void myfunc(const int n) { 
        int x[n] = { 1,1,3,4,5 };
        printf("%d\n", x[n-1]);
        *( (int *) &n) = 17;    //  Somewhat less "constant" than hoped...
        return ;
    }
    
    int main(){
        myfunc(4);
        myfunc(5);
        myfunc(6);  //  Haven't actually tested this.  Boom?  Maybe just printf(noise)?
        return 0;
    }
    

    n 真的那么稳定吗?您认为编译器应该为x[] 分配多少空间(因为这是编译器的工作)?

    正如其他人指出的那样,cv-qualifier const 确实 not 表示“在编译期间和之后一直保持不变的值”。它的意思是“一个本地代码不应该改变的值(尽管它可以)”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-02
      • 2020-02-23
      • 2021-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多