一个关于 C 结构体数组在形式参数中出错的错误

10

我有以下代码:

struct student_info;
void paiming1(struct student_info student[]); 
struct student_info  
{
    int num; 
    char name[6]; 
};

该IDE显示错误

error: array type has incomplete element typestruct student_info’
 void paiming1(struct student_info student[]);

但如果我使用void paiming1(struct student_info *student);,它可以正常运行。这是为什么?我正在使用GCC。


3
为什么会有人给踩呢?这是一个非常好的问题,而且代码也是 MCVE。 - Lundin
3个回答

8

C语言在所有数组声明中无条件要求数组元素类型必须完整。即使在函数参数列表中使用的数组声明也不例外。这与C++不同,后者对于函数参数列表放宽了这个完整性要求。

6.7.6.2 数组声明符
约束
1 [...] 元素类型不能是不完整的或函数类型。[...]

请注意,上述约束适用于所有数组声明,包括函数参数列表中的数组声明。

struct S;
void foo(struct S[]); // OK in C++, invalid in C

考虑到在参数列表声明中,类型T []后来总是会被调整为类型T *,因此这个要求可能看起来过于严格(这也是C++放宽了这一要求的原因)。然而,在C语言中存在这种限制。这只是C语言的其中一个怪异之处。
正如你已经知道的那样,你可以明确地切换到相当的
void paiming1(struct student_info *student); 
可以解决这个问题。

那么市场上所有现有的C99编译器都不符合标准了吗?因为我认为我没有看到任何一个编译器在指针调整之前不接受空的[]。我认为这是问题的根源:允许空的[],同时不允许不完整类型的对象。据说这是因为在C99之前建立了行业事实标准,因为C90没有指定行为。所以编译器试图一边吃蛋糕,一边保持它。 - Lundin
@Lundin:您能否澄清一下您的意思?我在这里没有看到任何不符合规范的地方。int[]是被接受的,因为您引用了自己的规则(完整性在调整后检查,而int *是完整的)。在我的示例中,struct S[]被拒绝是因为另一个更高级别的规则:数组声明是完全无效的。 - AnT stands with Russia
一切都归结于声明是否不完整的检查是在数组调整之前还是之后发生。尽管C99在其他地方增加了对数组不能具有不完整类型的澄清,但并没有说明针对6.7.6.2的检查是在数组调整之前还是之后发生。这个顺序是我认为在标准中找到的,但显然没有定义。要么这个检查发生在数组调整之前,那么struct S[]int i[]都是无效的。要么它发生在数组调整之后,然后它们都是有效的。在这里gcc的行为不一致。 - Lundin
@Lundin 禁止不完整元素类型的规则直接适用于数组声明符。如果元素类型不完整,则声明符无效(约束违规)。参数列表中的类型调整仅适用于声明数组的声明。即它们仅适用于有效的数组参数声明。如果声明符本身无效,则不存在参数声明。因此,没有任何可供调整的内容。 - AnT stands with Russia
@Lundin 如果您阅读整个段落,就会非常清楚地看到它也适用于参数 - 在该段落中,有许多仅适用于函数参数的事情,以及仅适用于最外层数组的事情。 - Antti Haapala -- Слава Україні
显示剩余6条评论

6
仔细阅读标准可以明确,在C99和C11中,声明应该是一个约束违规。C11 6.7.2.6 Array declarations p1 此外,除了可选的类型限定符和关键字static之外,[]还可以界定表达式或*。如果它们界定了一个表达式(指定数组的大小),则该表达式应具有整数类型。如果表达式是常量表达式,则其值应大于零。元素类型不应为不完整或函数类型。可选的类型限定符和关键字static只应出现在具有数组类型的函数参数的声明中,并且只应出现在最外层的数组类型派生中。
由于这包含对*的引用,该符号仅在非定义的函数声明中有效,而其他地方无效,因此整个约束需要视为适用于参数。
对于C90来说,情况更为复杂。这实际上在1992年12月10日的C90缺陷报告47中讨论过。
其中6个声明中的2个如下:
/* 1. */ struct S;
/* 3. */ struct S *g(struct S a[]) { return a; }

缺陷报告询问这些是否严格符合规范。需要注意的是,与问题不同,这些是属于定义而不仅仅是声明的原型。

尽管如此,标准委员会回应说

struct S是不完整类型(子句6.5.2.3,第62页,第25-28行)。此外,未知大小的数组也是不完整类型(子句6.5.4.2,第67页,第9-10行)。因此,任何一个上述数组都不是严格符合规范的(子句6.1.2.5,第23页,第23-24行)。这使得声明3、4和6不是严格符合规范的。(但实现可能会做正确。)

顺便说一下,数组参数被调整为指针类型(子句6.7.1,第82页,第23-24行)。然而,没有什么迹象表明,一个不严格符合规范的数组类型可以通过这个规则神奇地变成严格符合规范的指针参数。

所涉及的类型可以有两种不同的解释方式。(数组到指针的转换可以尽早或尽晚发生。)因此,使用这种形式的程序具有未定义的行为。

(我强调)

自1992年以来,这一行为没有得到书面澄清,因此我们必须同意该行为是未定义的,因此C标准没有任何要求,一个成功编译此代码的编译器仍然符合C90标准。
委员会还指出,在C90中不存在任何约束违规,因此符合C90标准的编译器不需要输出任何诊断信息。
我编辑了答案;我之前声称这适用于C99和C11,但是在C99中更改了文本,因此这是C99、C11中的约束违规。

答案不正确。旧的DR#47中的注释基于C89/90,它确实没有要求数组元素类型必须完整的约束条件。但是,C99在数组声明(6.7.5.2)上添加了额外的约束条件。相应的约束条件在我的答案中引用。原始代码包含完整的约束违规。对于此代码不发出诊断的编译器是有问题的。 “自1992年以来,这个问题还没有得到书面澄清...”-这根本就没有意义。在C99中已经很好地解释了这一点。 - AnT stands with Russia
@AnT 我指的是在转换为指针之前还是之后应该考虑数组类型。 - Antti Haapala -- Слава Україні
@AnT 嗯,是 6.7.6.2p1 的其他部分让我信服了 :P - Antti Haapala -- Слава Україні

0
编译器在结构体student_info被声明之前并不知道它的大小。以下代码应该可以正常工作:
struct student_info                                                              
{                                                                                
    int num; //学号                                                              
    char name[6]; //姓名                                                         
    char sex[5]; //性别                                                          
    char adress[20]; //家庭住址                                                  
    char tel[11]; //电话                                                         
    int chinese,math,english,huping,pingde,jiaoping,paiming1,paiming2;           
    double ave,zhongping;                                                        
};                                                                               

void paiming1(struct student_info student[]);                                    
void paiming2(struct student_info student[]); 

当你使用*将其声明为指针时,编译器知道参数的大小(它是一个地址)。


3
我认为这个回答偏离了重点。由于N1570 $6.7.6.3.7中的数组调整规则,编译器并不需要知道大小。问题是,是否存在其他优先级更高的规则,还是这只是编译器的一个错误? - user694733

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