C 中是否允许数组大小的变量?

Are variables in array sizes allowed in C?

提问人:Jaxon Mitchell 提问时间:10/7/2023 最后编辑:ChrisJaxon Mitchell 更新时间:10/7/2023 访问量:104

问:

我在 Linux 上使用 GCC 编译器。我不明白变量在数组大小中是如何工作的。

当我运行时:

char string[6];
string[0] = 'h';
printf("%s", string);

正如预期的那样,它输出: h

如果我运行:

int x = 1;
char string[5 + x];
string[0] = 'h';
printf("%s", string);

它输出 h 后跟 和其他随机字符。我猜它正在访问内存地址,它不应该这样做。每次角色都不同,它运行。

当我做同样的事情时,但将第二行替换为“char string[5 + x] = {}” 它错误:可变大小的对象可能未初始化。

如果我对整数数组执行相同的操作,如果我键入“int array[5 + x] = {};”而不是“int array[5 + x]”,它就可以正常工作

int 数组的大小声明中是否允许变量,但 char 数组/字符串中不允许变量?

数组 c 字符串 大小 大括号

评论

6赞 Barmar 10/7/2023
问题不在于变量。问题是您从未添加过 null 终止符。两个版本都有这个错误,你只是在第一个版本中很幸运。
2赞 Barmar 10/7/2023
添加到两个程序中。string[1] = '\0';
1赞 Chris 10/7/2023
你不能假设在函数中声明的任何数组都会被有意义地初始化,除非你自己显式地进行初始化。
0赞 user3386109 10/7/2023
可变长度数组 (VLA) 可用于任何类型,包括用于字符串的数组。你显然需要研究的是字符串在 C 中是如何工作的,C 语言中的字符串有很多非直观的特性,所以你真的需要一个很好的参考来详细解释字符串。char
0赞 Gerhardh 10/7/2023
“正如预期的那样,它输出:h” 事实上,这并不是真正可以预料到的。假设第一个代码行是函数的一部分,则数组不会初始化为 0。这意味着只有第一个元素包含特定值,而其他元素包含不确定的内容。

答:

4赞 Chris 10/7/2023 #1

如注释中所述,您的问题是以下调用了未定义的行为。

int x = 1;
char string[5 + x];
string[0] = 'h';
printf("%s", string);

未定义的行为源于用作字符串。C 中的字符串需要一个 null 终止字符来表示字符串的结束。可变长度数组尚未初始化(第一个字符除外),因此内容是不确定的。您有一个相当大的内存块,但该内存可以处于任何状态。string5 + x

在第一种情况下,显然是 ,因此以 null 终止字符串。这不能保证,正如您从第二个示例中看到的那样,其中打印了乱码,因为它在成为 null 终止符后没有看到下一个字符。string[1]0printf'h'

对于静态大小的数组,我们将使用 或 将内容设置为零来初始化它,但 VLA 可能不会以这种方式初始化。{0}{}

评论

5赞 Jonathan Leffler 10/7/2023
请注意,在 C23 中,可以使用默认初始化 VLA(或任何其他变量类型)。这是一个重大的、有益的变化;至少您将能够简洁地将数组归零,而不必使用调用或等效项。但是,这是 VLA 允许的唯一初始值设定项类型。= {};memset()
2赞 chux - Reinstate Monica 10/7/2023 #2

为了解决OP关注的标题部分:

C 中是否允许数组大小的变量?

在 C99 之前:可变长度阵列 (VLA) 不是标准。

C99:标准

C11、C17:如果已定义,则不支持 VLA。
否则支持 VLA。
__STDC_NO_VLA__

C23:(至少目前是这样提议的。像 C11、C17。“允许使用 VLA 类型,但创建具有自动持续时间的 VLA 对象除外......这是可选的。@tstanisl __STDC_NO_VLA__


数组的大小声明中是否允许变量,但数组/字符串中不允许变量?intchar

如果允许一种类型,则允许所有阵列类型使用 VLA。


printf("%s", string);是错误的代码,因为期望指向字符串的指针。 不是字符串,因为它肯定不包含 null 字符。结果:未定义的行为 (UB)。"%s"string

评论

1赞 tstanisl 10/7/2023
“除了函数原型中的 VLA”。这令人困惑。应该允许使用 VLA 类型,除了创建具有自动持续时间(也称为堆栈)的 VLA 对象,这是可选的。
0赞 Jonathan Leffler 10/8/2023
@tstanisl — 由于您也无法创建静态分配的 VLA(永远不能),这意味着 VLA 只能动态分配(通过等)。但是,普通数组可以传递给处理具有可变大小的数组的函数,因此它们在函数中被视为 VLA,即使在调用代码中它们是常规数组也是如此。malloc()
0赞 tstanisl 10/8/2023
@JonathanLeffler,从技术上讲,可以指向 VLA 的指针。.在文件范围内,不能使用 VLA,因为如果不执行代码,就不可能形成任何 VLA 类型。staticvoid foo(int n) { static int (*arr)[n]; }