an error about C struct array in formal parameter
Asked Answered
E

3

10

I have got the following code:

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

The IDE gives an error

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

But if I use void paiming1(struct student_info *student); it works OK. Why is that? I am using GCC.

Enterotomy answered 24/5, 2018 at 7:29 Comment(1)
Why the down votes? This is a very good question and the code is a MCVE.Lightface
C
8

С language unconditionally requires array element type in all array declarations to be complete. Period.

6.7.6.2 Array declarators
Constraints
1 [...] The element type shall not be an incomplete or function type. [...]

No exception is made for array declarations used in function parameter lists. This is different from C++ - the latter drops this completeness requirement for function parameter lists

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

Considering that in parameter list declaration type T [] is later adjusted to type T * anyway, this requirement might appear to be excessive. (And this is why C++ relaxed it.) Nevertheless, this restriction is present in C language. This is just one of the quirks of C language.

As you already know, you can explicitly switch to the equivalent

void paiming1(struct student_info *student); 

form to work around the issue.

Churchy answered 24/5, 2018 at 18:58 Comment(11)
So every existing C99 compiler on the market is not conforming then? Because I don't think I've seen any compiler which does not accept empty [] before adjustment to pointer. I think this is the root of the problem: allowing empty [] while at the same time not allowing objects of incomplete type. Supposedly because there was an industry de facto standard established pre-C99, since C90 didn't specify the behavior. So the compilers try to eat the cake and keep it at the same time.Lightface
@Lundin: Could you please clarify what you mean? I don't see any non-conformance here. int[] is accepted because of the rule you quoted yourself (competeness is checked after adjustment and int * is complete). struct S[] in my example is rejected because of a different, higher-level rule: the array declaration is flat-out invalid.Churchy
Adjustments play no role in rejection of struct S[] whatsoever. We don't even get to that point. struct S[] is invalid immediately, by itself, regardless of context. If is invalid in any context.Churchy
It all boils down to whether the check if the declaration is incomplete happens before or after array adjustment. Just because C99 added a clarification that arrays cannot have incomplete type elsewhere, there is nothing saying that the check against 6.7.6.2 happens before array adjustment. This order is what I thought I had found in the standard, but apparently it is not defined. Either this check happens before array adjustment, and then both struct S[] and int i[] are invalid. Or it happens after array adjustment, and then they are both valid. Here gcc behaves inconsistently.Lightface
The point being: the compiler can't just do a check if an element of the array is of incomplete type, it has to check if the array in itself is complete or not at the very same time. Anything else would be quite irrational.Lightface
@Lightface The rule that prohibits incomplete element type applies immediately and directly to array declarators. If the element type is incomplete, the declarator is invalid (constraint violation). Type adjustments in parameter lists apply to declarations that declare arrays. I.e. they only apply to valid array parameter declarations. If the declarator itself is invalid, there's no parameter declaration. There's nothing to apply adjustments to.Churchy
struct S[] is an invalid declaration. Since it is invalid, it is not an array declaration. It is simply invalid. It does not even get a chance to reach adjustments. (And even if it did, it woudn't matter, since it is an invalid declaration, not an array ddclaration.) Meanwhile, int [] is valid array declaration. It reaches adjustments and processed as an array declaration. After adjustments it becomes int *, which is complete.Churchy
@Lightface Yes, the compiler can do a check for completness of element type. Completness of element type is a completely separate and independent issue, separate from completeness of the entire array type. In C incompleteness of array element type is always unconditionally a constraint violation. Incompleteness of the whole array type (due to unknown size) is a different story: it can be ok, or it can be an error depending on the context. For some reason you are trying to fuse these two competely independent points into one.Churchy
Still the order that these checks are done is not specified anywhere. Where does it say that the checks must be done in this order: 1) immediately check if array elements are valid 2) perform array adjustment of function parameters 3) check if the array type is valid/complete. This order is AFAIK not specified in the standard and it doesn't look very rational either.Lightface
@Lightface Once again: array adjustments can only be applied to arrays. They cannot possibly take place before you know that you are dealing with an array. But for that you need a valid array declaration. Just seeing a pair of [] is not enough! A valid array declaration requires more than just a pair of []. It also requires a complete element type. Until you jump through all hoops to prove that you are dealing with a valid array declaration, you cannot apply array adjustments. This is what absolutely strictly dictates that element type completeness check shall come before adjustments.Churchy
@Lightface if you read the whole paragraph it is very clear that the applies to parameters too - there are many things that apply only to function parameters in that paragraph, and only to the outermost array.Shrier
A
6

Careful reading of the standard makes it clear that in C99 and C11 the declaration is supposed to be a constraint violation. C11 6.7.2.6 Array declarations p1

  1. In addition to optional type qualifiers and the keyword static, the [ and ] may delimit an expression or *. If they delimit an expression (which specifies the size of an array), the expression shall have an integer type. If the expression is a constant expression, it shall have a value greater than zero. The element type shall not be an incomplete or function type. The optional type qualifiers and the keyword static shall appear only in a declaration of a function parameter with an array type, and then only in the outermost array type derivation.

Since this contains references to the * that is valid only in function declarations that are not definitions, and nowhere else, the constraints as whole needs to be taken as applying to parameters.


For C90 the case is more complicated. This is actually discussed in C90 Defect Report 47 from 10 December, 1992.

2 of the 6 declarations given there are

/* 1. */ struct S;
/* 3. */ struct S *g(struct S a[]) { return a; }

and the defect report asks if these are strictly-conforming. It must be noted that unlike the question, these are prototypes that are part of definition rather than just declaration.

Nevertheless, the standard committee responds by saying that

The struct S is an incomplete type (subclause 6.5.2.3, page 62, lines 25-28). Also, an array of unknown size is an incomplete type (subclause 6.5.4.2, page 67, lines 9-10). Therefore, arrays of either of the above are not strictly conforming (subclause 6.1.2.5, page 23, lines 23-24). This makes declarations 3, 4, and 6 not strictly conforming. (But an implementation could get it right.)

As an aside, array parameters are adjusted to pointer type (subclause 6.7.1, page 82, lines 23-24). However, there is nothing to suggest that a not-strictly-conforming array type can magically be transformed into a strictly conforming pointer parameter via this rule.

The types in question can be interpreted two different ways. (Array to pointer conversion can happen as soon as possible or as late as possible.) Hence a program that uses such a form has undefined behavior.

(emphasis mine)

Since this has not been clarified in writing since 1992, we must agree that the behaviour is undefined and therefore the C standard imposes no requirements, and a compiler that successfully compiles this this can still conform to C90.

The committee also states that there is no constraint violation in C90, therefore a C90 conforming compiler need not output any diagnostics.

I've edited the answer; I previously claimed that this would apply to C99 and C11 alike, but the text was changed in C99 as above, therefore this is a constraint violation in C99, C11.

Absorb answered 24/5, 2018 at 19:30 Comment(3)
The answer is incorrect. The comments in the old DR#47 are based on C89/90, which indeed has no constraint requiring array element type to be complete. However, C99 added additional constraints on array declarations (6.7.5.2). The corresponding constraint is quoted in my answer. The original code contains a full-blown constraint violation. A compiler that issues no diagnostic for this code is broken. "Since this has not been clarified in writing since 1992..." - this just does not make sense. It has been well-clarified in C99.Churchy
@AnT I am referring to whether or not the array type should be considered before or after the conversion to pointerShrier
@AnT well, it was the other parts of the 6.7.6.2p1 that were needed to convince me :PShrier
M
0

Compiler doesn't know anything about size of struct student_info until it is declared. This should work:

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[]); 

When you declare it as a pointer using *, compiler knows the size of the argument (its an address).

Mechelle answered 24/5, 2018 at 7:37 Comment(1)
I think that this answer misses the point. Compiler doesn't need to know the size due to array adjustment rules in N1570 $6.7.6.3.7. Question is, is there some other rule that has higher precedence, or is this simply a compiler bug?Bertie

© 2022 - 2024 — McMap. All rights reserved.