27

Is the following example a valid complete translation unit in C?

struct foo;

struct foo *bar(struct foo *j)
{
    return &*j;
}

struct foo is an incomplete type, but I cannot find an explicit prohibition of dereferencing an incomplete type in the C standard. In particular, §6.5.3.2 says:

The unary & operator yields the address of its operand. If the operand has type ‘‘type’’, the result has type ‘‘pointer to type’’. If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue.

The fact that the result is not an lvalue is not germane - return values need not be. The constraints on the * operator are simply:

The operand of the unary * operator shall have pointer type.

and on the & operator are:

The operand of the unary & operator shall be either a function designator, the result of a [] or unary * operator, or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier.

Both of which are trivially satisfied here, so the result should be equivalent to just return j;.

However, gcc 4.4.5 does not compile this code. It instead gives the following error:

y.c:5: error: dereferencing pointer to incomplete type

Is this a defect in gcc?

caf
  • 216,678
  • 34
  • 284
  • 434
  • I think you're correct. This certainly won't be the first, or the last... – R.. GitHub STOP HELPING ICE Aug 04 '11 at 07:11
  • 1
    Technically, only C99 has this wording. C90 doesn't mention that the `&` and `*` cancel each other out. For C99, I'd agree with your analysis though. But since gcc doesn't claim to be fully C99 compliant, I'm not too surprised about this error message. – Sander De Dycker Aug 04 '11 at 08:25

3 Answers3

7

Yes, I think it is a bug. Even lvalues of incomplete types, so *j, seem to be allowed depending on the context:

6.3.2.1 ... An lvalue is an expression with an object type or an incomplete type other than void

Basically this should work as long as you don't do anything with such an lvalue that needs to know about the structure of the struct. So if you don't access the object or ask about its size, this is legal.

Jens Gustedt
  • 72,200
  • 3
  • 92
  • 164
  • Yes, this would be the next logical question... for example a void expression like `(void)*j` should also be allowed, by my reading. – caf Aug 04 '11 at 10:37
3

The C99 standard (ISO/IEC 9899:1999) describes the behaviour:

§6.5.3.2 Address and indirection operators

The unary & operator returns the address of its operand. If the operand has type ‘‘type’’, the result has type ‘‘pointer to type’’. If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue.

This means that &*j is equivalent to j.

However, j is supposed to be a pointer to an object, and it is only a pointer to an incomplete type, as GCC 4.4.5 says.

§6.3.2.3 Pointers

A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

Note that it distinguishes between an object type and an incomplete type; this occurs frequently in the standard.

So, this observation in the question is incorrect:

Both of which are trivially satisfied here,

The variable j is not a pointer to an object; it is a pointer to an incomplete type, which is not an object.


§6.2.5 Types

[...] Types are partitioned into object types (types that fully describe objects), function types (types that describe functions), and incomplete types (types that describe objects but lack information needed to determine their sizes).

Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
  • The part that seems to be missing from your analysis is *where* a pointer to an *object* is specifically required. The only constraint upon the operand of the `*` operator appears to be that it have "pointer type", which encompasses both pointers to objects and incomplete types. – caf Aug 04 '11 at 10:32
  • I think that `j` still is a pointer to an object. Only that its type is incomplete. The standard explicitly talks of lvalues of incomplete type for example. The fact of pointing to an object only depends on the fact that this is correctly allocated (size and alignment). – Jens Gustedt Aug 04 '11 at 14:05
  • @Jonathan, doesn't it say exactly the same as I did? "incomplete types" are "types that describe objects but lack information...". So in particular a pointer to an incomplete type is a pointer to an object. And dereferencing such a pointer produces an lvalue. And such lvalues of incomplete type are explicitly mentioned elsewhere. – Jens Gustedt Aug 04 '11 at 20:48
  • @jens: No. It says that there are object types which are complete; there are incomplete types (which are, by definition of 'partition', not the same as object types), and there are function types. So object types are distinct from and not the same as incomplete types. – Jonathan Leffler Aug 04 '11 at 20:49
  • @Jonathan no, it doesn't say that. The part that you cite says "types that describe objects", there is nothing ambiguous. – Jens Gustedt Aug 04 '11 at 20:51
  • @Jonathan, to emphasize on the parts about objects: "object types" describe objects fully, and "incomplete types" describe objects partially. – Jens Gustedt Aug 04 '11 at 20:54
  • @jens: then we are going to have to agree to disagree on the meaning of the plain English in the standard. – Jonathan Leffler Aug 04 '11 at 20:55
  • I agree with Jonathan inasmuch as *object types* and *incomplete types* are distinct, but I do not see where an object type is specifically required here. – caf Aug 05 '11 at 00:40
  • @caf, I think the confusion here is in the terminology. "object type" and type of an object are not the same thing. It would have been better if they had chosen to name the thing "complete type" in contrast to "incomplete type", since an object may have an "object type" or an "incomplete type". – Jens Gustedt Aug 05 '11 at 08:11
2

Yes. Pointers in C usually have the same size (on some embedded systems, they can be different). This means the compiler can generate the correct assembler code for this even though the type is "unknown".

You can use this approach to completely hide the internal data structure to the outside. Use a typedef to declare a pointer to a structure and only declare the structure in your internal header files (i.e. the files which are not part of your public API).

The reason why gcc 4.4.5 complains is just that: If you use pointers to the incomplete type outside of the implementation, it should work. But the code is part of the implementation and here, you probably want to have the complete type.

Aaron Digulla
  • 297,790
  • 101
  • 558
  • 777
  • 5
    It is not right, that all pointers in C have the same size. There are only very very few platforms, where it happens, but e.g. in Embedded Systems with Harvard architecture you can find different pointer sizes for data and function pointers. – flolo Aug 04 '11 at 08:12
  • @flolo: correct. This was even discussed in a recent question here. Someone posted a link to a few platforms where pointers of different type had different sizes. – Rudy Velthuis Aug 04 '11 at 08:58
  • 1
    the relevant part of the C99 standard is section 6.2.5, §27: "All pointers to structure types shall have the same representation and alignment requirements as each other."; this is not true for arbitrary pointer types – Christoph Aug 04 '11 at 09:14