为什么下面的代码会抛出错误?
const int a = 5;
int b[a] = {1, 2, 3, 4, 5};
当我尝试编译上面的代码时,没有“const”关键字,我得到了同样的错误:
int a = 5;
int b[a] = {1, 2, 3, 4, 5};
为什么会这样呢?我犯了什么错?
还有一个问题:当常量在代码中被替换为它们的实际值时,即,如果我声明一个变量说:int x= 5;
我知道RAM中没有为变量x分配内存,但ROM中的常量变量区保存值5,并且只要x出现在代码中,x就被值5替换。但这是什么时候发生的?编译时间? Boot 时间?预处理时间?
PS:我说的是 * 嵌入式 * C(运行在 * 微控制器 * 上),而不是运行在我桌面上的C。所以嵌入式系统必然要有ROM(flash,EEPROM等)。到时候会发生什么?
5条答案
按热度按时间roejwanj1#
这只是语言的局限性。静态绑定数组的大小需要是 * 常量表达式 *,不幸的是,在C中,这只是一个字面常量或
sizeof
表达式之类的东西,而不是 *const
类型的变量。(As Simon指出,自C99以来,还有 * 运行时有界 * 数组,或“可变长度数组”,其大小可以由任何变量的值给定。这是一种不同的动物)。
你可能会有兴趣听到C中的规则是不同的,其中
static const int
确实是一个常量表达式,C11甚至添加了一个新的关键字constexpr
,以允许更通用的常量表达式,其中包含更多的东西,其值“可以在编译时合理地确定”。6l7fqoea2#
在C中,
const
是 read-only 的误称。const
变量可以改变它们的值,例如完全可以宣布你可以读它,每次读它都会得到一个不同的值,但不能写它。因此,语言规范将
const
限定对象而不是视为适合数组大小的 * 常量表达式 *。bf1o4zei3#
VLA的两种主要替代方案:
enum
和宏使用anonymous
enum
:如:
这是因为枚举成员是常量表达式:Can enum member be the size of an array in ANSI-C?
使用宏:
enum
s的优点是enum
s具有作用域,并且是编译步骤的一部分,因此它们也可以产生更好的错误消息。宏的优点是你可以更好地控制常量的类型(例如,
#define N 1
vs#define N 1u
),而enum
则固定为某些实现定义的类型:Is the sizeof(enum) == sizeof(int), always?但在这种情况下,这并不重要。在Ubuntu 21.04,GCC 10.3.0,
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
上测试。为什么要避免VLA
ymzxtsji4#
我刚刚在Wikipedia上读到C11已经将变长数组降级为一个可选的功能:(Doh!这篇文章的前半部分可能没有那么有用,但后半部分回答了你的一些其他问题:)
作为Kerrek SB的文章的补充,C99(ISO/IEC 9899:1999)确实有可变长度数组的概念。该标准给出了以下示例:
sizeof
运算符扩展如下:sizeof运算符产生其操作数的大小(以字节为单位),该操作数可以是表达式或带括号的类型名称。大小由操作数的类型决定。结果是一个整数。如果操作数的类型为变长数组类型,则计算操作数;否则,不计算操作数,结果为整数常量。
另一个很好的例子可以在Wikipedia上找到。
注意,静态声明的不能是变长数组。
至于你的其他一些问题:
如果常量是一个常量变量,那么它可能永远不会被“替换”,并且总是可以作为内存区域访问。这是因为地址运算符
&
仍然必须处理变量。然而,如果变量地址从未被使用过,那么它可以被“替换”,并且不分配内存。C标准:该实现可以将非易失性的常量对象放置在存储器的只读区域中。此外,如果一个对象的地址从未被使用过,那么实现就不需要为它分配存储空间。
下一个问题
这取决于你的系统。如果你有ROM,编译器知道ROM在哪里,那么它很可能被放置在ROM中。如果没有ROM,编译器(实际上是链接器)的唯一选择就是RAM。
如前所述,这取决于如何使用常数。如果const变量的地址从未使用过,并且编译器足够聪明,那么在编译时。否则,“替换”永远不会发生,它是一个在内存中有位置的值;在这种情况下,变量在存储器中的放置发生在链接时间。在预处理过程中,它永远不会发生。
eivnm1vs5#
也许值得一提的是,在这里你可以使用
如果你需要元素的数量
我相信这取决于工具链,决定在哪里存储常量,在闪存或RAM中。