C语言中的数组初始化

19

我对以下代码有疑问:

int main()
{
    int array1 = {1,2,3,4,5}; //error in c++ , warning in c
    int array2[] = {1,2,3,4,5};
    int array3[5] = {1,2,3,4,5};
}

这段代码在第3行会导致的错误,但不会在中出错?

我知道array1实际上是一个int,而array2array3是数组,那么为什么编译器只会显示警告:“标量初始化中有过多的元素”,而不是一个错误?

这样的定义有什么用处,为什么它在中是有效的?


2
你使用的是哪个编译器?因为VS 2012会将其视为错误。 - Ali Kazmi
21
C和C++不是同一种编程语言。 - Basile Starynkevitch
2
哪一行出现了错误?第4行是array2的声明,但唯一有问题的行是array1的那一行。 - Mike Seymour
1
他/她应该说出自己的意思。 - Lightness Races in Orbit
2
在这种情况下,它们确实是。几乎逐字逐句。 - James Kanze
显示剩余4条评论
7个回答

26

这不是有效的C语言。请参考C11 6.7.9:

没有初始化器可以为未包含在正在初始化的实体中的对象提供值。

我猜你正在使用gcc编译器。如果你想让你的程序遵循严格的标准C语言,那么就要以此方式进行编译:

gcc -std=c11 -pedantic-errors

提供

错误:标量初始化程序中存在过多的元素


4
这是一个限制违规,即使没有 -pedantic-errors,gcc 也会正确发出诊断,符合 ISO C 的要求。实现不一定需要拒绝这段代码。 - P.P
2
@BlueMoon 这是因为标准中没有警告和错误这样的东西。对于程序员来说,错误更好。 - Lundin

15

这在C语言中是无效的。它只对代码进行了较少的检查。这是未定义的行为。

来自:C11草案N1570;6.7.9初始化

引用:

限制
2.没有初始化器应尝试为未包含在正在初始化的实体中的对象提供值。
3.要初始化的实体的类型应为大小未知的数组或不是变长数组类型的完整对象类型。

肯定违反了限制2。一个int是一个完整的对象类型吗?

来自附录J.2(未定义行为):

标量的初始化程序既不是单个表达式,也不是括在花括号中的单个表达式(6.7.9)。

额外信息:
@James Kanze:

prog.c:4:12: error: expected identifier or ‘(’ before numeric constant
  int i = 1,2,3,4,5;
            ^

你可以这么做,但是需要将它变成一个表达式:

int i = (1,2,3,4,5); //need parenthesis, evaluates to 5, values 1-4 thrown away.

使用用初始化列表初始化的int编译时会产生警告(在gcc中):

prog.c:5:2: warning: excess elements in scalar initializer [enabled by default]
  int j = {1,2,3,4,5};
  ^

但似乎编译器足够智能,只初始化整数而不是其后的内存。

演示

2
不确定约束违规会导致未定义的行为。C标准是否声明或暗示了这一点? - P.P
但是 1, 2, 3, 4, 5 可以被解释为单个初始化器。(int x = 1, 2, 3, 4, 5; 明确是合法的。) - James Kanze
2
但是{1,2,3,4,5} != 1,2,3,4,5。前者是初始化列表,而后者是使用逗号运算符分隔的5个不同的值。我认为这适用:标量的初始化器既不是单个表达式,也不是用花括号括起来的单个表达式(参见6.7.9)。如果有错误,请纠正我。 - Baldrickk
@JamesKanze 添加了 int i = {12345};int j = (1,2,3,4,5); 之间的比较。 - Baldrickk

11

根据C规范,这是有效的。

C11第6.7.9节(初始化)引用:

语法

1 initializer:
    assignment-expression
    { initializer-list }
    { initializer-list , }
因此,初始化器可以是直接表达式(如上方的assignment-expression),也可以是用大括号括起来的初始化器列表。标准中该部分的约束并不限制非数组或非指针变量的这种情况。
这使您可以编写例如:
int a = { 1 };

这是来自答案列表中最相关(甚至正确)的答案。 - bitcell
我的C编译器可以接受单个值{1},但对于OP的array1则显示“错误C2078:初始化项过多”。 - Weather Vane
C语言继续让我感到惊喜。 - Marco A.
9
提到的语法下面的第一篇规范文本是什么?“不应该在初始化实体时试图为未包含在其中的对象提供值。”你引用的语法确实允许“int a = {1}”,但这并不意味着“int array1 = {1,2,3,4,5}”是被允许的,因为它与不允许“int array1[1] = {1,2,3,4,5}”的原因相同。 - Lundin
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - James Kanze
4
不加限制地引用语法并不能完全说明问题,这只是一半的答案,文本中的限制对于这个问题很重要。 - Shafik Yaghmour

3

和其他人一样,我假设你想知道的是这行代码:

int array1 = {1,2,3,4,5};

在这种情况下,C和C++没有区别;该行在两种语言中都是合法的,但并不意味着你可能认为的那样。在C和C++中,有一条规定,如果类型是标量类型(如int),那么{...}的内容必须是单个表达式。而1,2,3,4,5可以被解释为一个单一的表达式(使用逗号运算符);类似于:
int array1 = 1, 2, 3, 4, 5;

显然是合法的。

但是有些含糊不清,因为在两种语言中,这种初始化的语法使,标点符号而不是运算符。因此这是一个解释问题;这个语句的内容必须是单个表达式的约束是语法上的限制(这将导致逗号成为运算符),还是对指定语法求值结果的限制。我的直觉感觉是第二种情况是意图,这个语句应该会出现错误。但这种区别不在于C和C++之间,而在于编译器作者对标准的解释方式。

编辑:

重新仔细阅读后发现:在C++标准中,明确指出

如果T是标量类型,则形式为

    T x = { a };

的声明等同于

    T x = a;

这就没有多少余地了:在C++中,这个语句似乎是合法的;只有在C中存在一些模糊性。


5
我认为这里不存在歧义,语法根本不允许,成为逗号运算符。如果我们查看6.7.8中的初始化器语法,其中包括assignment-expression,但没有使用()无法将其转换为逗号运算符...因此,我们可以写{(1,2,3,4,5)},但其实际用处令人怀疑。 - Shafik Yaghmour
@ShafikYaghmour 这是一个很好的观点。但这似乎意味着 int a = 1, 2, 3; 也是非法的。我相当确定我曾经使用过它(或类似的东西)。或者我总是用括号括起来;我不确定。(我会使用括号来向读者明确只涉及一个表达式。) - James Kanze
gcc -std=c99 -pedantic=errors 拒绝编译 int a = 1, 2;。您提出的标准阅读方式也会导致问题,例如在块作用域中,当文件作用域声明 b 可见时,int a = 1, b = 2; 或者使用 GCC 的语句表达式扩展(GCC 扩展旨在不与 ISO C 冲突)。 - mafso
@mafso 是的,我认为你是正确的;我可能在那里有额外的括号。 - James Kanze

2

在 C/C++ 中初始化数组的正确方法是:

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

这里的方括号实际上告诉编译器这是一个数组,你的情况下是一组数字。在这种初始化方式中,不需要指定数组的长度,编译器会自动识别。

第二种方法是定义一个数组并进行后期初始化:

int array3[5];
int *array = new int[5];

在这种情况下,您需要告知编译器数组的大小。此外,在第二种情况下,您需要手动删除内存。

那不是对问题的回答。 - Jens Gustedt

2

正如您的编译器所说,这对于C语言也是不正确的。在这里需要注意的重要事情是,在出现这种“约束违规”(官方术语)的情况下,编译器可能只会生成一条诊断信息(就像您的编译器所做的那样)并继续执行。

因此,通常应确保您的代码在没有任何诊断信息的情况下编译通过。


1

C是一种低级的宽容语言。它允许影响指向int的指针。

int array1 = {1,2,3,4,5};

编辑:

在写蠢话之前,我应该在不同的编译器下进行测试。

这在 MSVC(2008)中被拒绝作为错误。

gcc 和 clang 都会发出警告 excess elements in scalar initializer,并将 1 直接赋值给 a


编译器如何知道它是一个char数组?为什么不是整数?双精度浮点数呢?抱歉,但你的答案是无意义的。在C语言中,初始化列表没有类型。 - GreenScape
你可以进行一些不好的重新解释转换,使指针成为“int”,尽管我怀疑在初始化时不行。因为犯错并从中学习而得到加分。 - geometrian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接