在C语言中,可以使用const变量来声明数组的大小吗?

b4wnujal  于 2023-10-16  发布在  其他
关注(0)|答案(5)|浏览(167)

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

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(flashEEPROM等)。到时候会发生什么?

roejwanj

roejwanj1#

这只是语言的局限性。静态绑定数组的大小需要是 * 常量表达式 *,不幸的是,在C中,这只是一个字面常量或sizeof表达式之类的东西,而不是 * const类型的变量。
(As Simon指出,自C99以来,还有 * 运行时有界 * 数组,或“可变长度数组”,其大小可以由任何变量的值给定。这是一种不同的动物)。
你可能会有兴趣听到C中的规则是不同的,其中static const int确实是一个常量表达式,C11甚至添加了一个新的关键字constexpr,以允许更通用的常量表达式,其中包含更多的东西,其值“可以在编译时合理地确定”。

6l7fqoea

6l7fqoea2#

在C中,constread-only 的误称。const变量可以改变它们的值,例如完全可以宣布

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

你可以读它,每次读它都会得到一个不同的值,但不能写它。因此,语言规范将const限定对象而不是视为适合数组大小的 * 常量表达式 *。

bf1o4zei

bf1o4zei3#

VLA的两种主要替代方案:enum和宏

使用anonymousenum

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

如:

#include <stdio.h>

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

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

这是因为枚举成员是常量表达式:Can enum member be the size of an array in ANSI-C?
使用宏:

#define N 5
int is[N];

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

ymzxtsji

ymzxtsji4#

我刚刚在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上找到。
注意,静态声明的不能是变长数组。
至于你的其他一些问题:

  • Q:在代码中,常量什么时候被替换为它们的实际值?*

如果常量是一个常量变量,那么它可能永远不会被“替换”,并且总是可以作为内存区域访问。这是因为地址运算符&仍然必须处理变量。然而,如果变量地址从未被使用过,那么它可以被“替换”,并且不分配内存。C标准:
该实现可以将非易失性的常量对象放置在存储器的只读区域中。此外,如果一个对象的地址从未被使用过,那么实现就不需要为它分配存储空间。
下一个问题

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

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

  • Q:只要代码中出现x,x就被替换为值5。但这是什么时候发生的?编译时间? Boot 时间?预处理时间?*

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

eivnm1vs

eivnm1vs5#

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

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

如果你需要元素的数量

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

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

相关问题