58

为什么下面的代码会抛出错误?

const int a = 5;
int b[a]={1,2,3,4,5};

而且当我尝试编译上面没有“const”关键字的代码时,我得到了同样的错误:

int a = 5; 
int b[a]={1,2,3,4,5};

为什么会这样?我在这里做错了什么?

还有另一个问题:什么时候用代码中的实际值替换常量,即如果我声明一个变量说: const int x= 5; 我知道在 RAM 中没有为变量 x 分配内存,但 ROM 中的常量变量区域保存值 5,并且 x 在代码中出现 x 的任何地方都被值 5 简单地替换。但这种情况什么时候发生?编译时间?开机时间?预处理时间?

PS:我说的是嵌入式 C(在微控制器等上运行),而不是在我的桌面上运行的 C。所以嵌入式系统必然要有ROM(Flash、EEPROM...)。那时会发生什么?

4

5 回答 5

47

这只是语言的限制。静态有界数组的大小需要是常量表达式,不幸的是,在 C 中,这只是像文字常量或sizeof表达式之类的东西,而不是类型const变量。

(正如 Simon 指出的那样,从 C99 开始,还有运行时有界数组,或“可变长度数组”,其大小可以由任何变量的值给出。但那是另一种动物。)

您可能有兴趣听到 C++ 中的规则有所不同,其中 astatic const int确实是一个常量表达式,而 C++11 甚至添加了一个新关键字 ,constexpr以允许更一般地使用常量表达式,其中包含更多具有“值”的事物可以在编译时合理地确定”。

于 2013-09-17T11:25:36.393 回答
46

在 C 中,是read-onlyconst的误称。变量可以改变它们的值,例如声明是完全可以的const

const volatile int timer_tick_register; /* A CPU register. */

您可以在每次读取时读取并获得不同的值,但不能写入。因此,语言规范将const合格对象视为适合数组大小的常量表达式。

于 2013-09-17T13:25:32.173 回答
34

VLA 的 2 个主要替代方案:enum和宏

使用匿名 enum

enum { N = 5 };
int is[N];

如:

#include <stdio.h>

enum { N = 5 };
char is[N];

int main(void) {
    printf("%ju\n", sizeof(is));
}

这是因为枚举成员是常量表达式:枚举成员可以是 ANSI-C 中数组的大小吗?

使用宏:

#define N 5
int is[N];

s的优点enumenums 具有范围,并且是编译步骤的一部分,因此它们也可能导致更好的错误消息。

宏的优点是您可以更好地控制常量的类型(例如#define N 1vs #define N 1u),而enums 固定为某些实现定义的类型: sizeof(enum) == sizeof(int) 总是吗?但在这种情况下,这并不重要。

在 Ubuntu 21.04、GCC 10.3.0 gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c、.

为什么要避免 VLA

于 2015-05-18T21:05:23.613 回答
4

编辑:刚刚在 wikipedia 上读到 C11 已将可变长度数组降级为可选功能 :( Doh!帖子的前半部分可能没有那么有用,但后半部分回答了您的其他一些问题 :)

作为对 Kerrek SB 帖子的补充,C99 (ISO/IEC 9899:1999) 确实具有可变长度数组的概念。该标准给出了以下示例:

#include <stddef.h>
size_t fsize3(int n)
{
    char b[n+3]; // variable length array
    return sizeof b; // execution time sizeof
}

sizeof运算符扩展如下:

sizeof 运算符产生其操作数的大小(以字节为单位),它可以是表达式或带括号的类型名称。大小由操作数的类型决定。结果是一个整数。如果操作数的类型是变长数组类型,则计算操作数;否则,不计算操作数,结果是一个整数常量。

另一个很好的例子可以在wikipedia上找到。

请注意,静态声明的不能是变长数组。

至于你的其他一些问题:

问:什么时候用代码中的实际值替换常量?

如果常量是 const 变量,那么它可能永远不会被“替换”,并且始终可以作为内存区域进行访问。这是因为地址运算符&仍然必须与变量一起使用。但是,如果从不使用变量地址,则可以“替换”它并且不分配内存。来自 C 标准:

该实现可以将非易失性的 const 对象放置在存储的只读区域中。此外,如果从未使用过此类对象的地址,则实现不需要为此类对象分配存储空间。

下一个问题...

问:我知道在 RAM 中没有为变量 x 分配内存,但 ROM 中的常量变量区域保存值 5

这取决于您的系统。如果你有 ROM 并且编译器知道 ROM 的位置,那么它很可能放在 ROM 中。如果没有 ROM,编译器(实际上是链接器)将拥有的唯一选择是 RAM。

问:在代码中出现 x 的任何地方,x 都被简单地替换为值 5。但这种情况什么时候发生?编译时间?开机时间?预处理时间?

如前所述,这取决于如何使用常量。如果从不使用 const 变量的地址并且编译器足够聪明,那么在编译时。否则,“替换”永远不会发生,它是一个在内存中具有位置的值;在这种情况下,变量在内存中的放置发生在链接时。它永远不会在预处理过程中发生。

于 2013-09-17T11:33:33.530 回答
2

也许值得一提,在这里你可以使用

int b[] = {1, 4, 5};

如果您需要元素数量

 size_t sz = sizeof(b)/sizeof(b[0]);

我相信这取决于工具链,决定在哪里存储常量、闪存或 RAM

于 2018-07-05T09:08:31.737 回答