In the C89 standard, I found the following section:
3.2.2.1 Lvalues and function designators
Except when it is the operand of the sizeof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue). If the lvalue has qualified type, the value has the unqualified version of the type of the lvalue; otherwise the value has the type of the lvalue. If the lvalue has an incomplete type and does not have array type, the behavior is undefined.
If I read it correctly, it allows us to create an lvalue
and applies some operators on it, which compiles and can cause undefined behavior during runtime.
Problem is that, I can't think of an example of "an lvalue with incomplete type" which can pass compiler's semantic check and triggers undefined behavior
.
Consider that an lvalue is
An lvalue is an expression (with an object type or an incomplete type other than void) that designates an object.
and that incomplete type is
Types are partitioned into object types (types that describe objects), function types (types that describe functions), and incomplete types (types that describe objects but lack information needed to determine their sizes).
A failed program I tried:
struct i_am_incomplete;
int main(void)
{
struct i_am_incomplete *p;
*(p + 1);
return 0;
}
and got the following error:
error: arithmetic on a pointer to an incomplete type 'struct i_am_incomplete'
*(p + 1);
~ ^
Anyone can think of an example on this ? An example of "an lvalue with incomplete type" which can pass compiler's semantic check and triggers undefined behavior
.
UPDATE:
As @algrid said in the answer, I misunderstood undefined behavior
, which contains compile error
as an option.
Maybe I'm splitting hairs, I still wonder the underlying motivation here to prefer undefined behavior
over disallowing an lvalue to have an incomplete type
.
Some build systems may have been designed in a way would allow code like:
to be processed successfully without the compiler having to know or care about the actual representation of struct foo [for example, some systems may process pass-by-value by having the caller pass the address of an object and requiring the called function to make a copy if it's going to modify it].
Such a facility may be useful on systems that could support it, and I don't think the authors of the Standard wanted to imply that code which used that feature was "broken", but they also didn't want to mandate that all C implementations support such a feature. Making the behavior undefined would allow implementations to support it when practical, without requiring that they do so.