alinsoar alinsoar - 8 days ago 6
C Question

type qualifier without type specifier

In the ISO/IEC 9899:1999, in §6.7.7 Type definitions, the 3rd example (the 6th paragraph marked on the border) is incorrect. The explanation from the example is so (see the code in the bottom):


The first two bit-field declarations differ in that unsigned is a
type specifier (which forces
t
to be the name of a structure member),
while
const
is a type qualifier
(which modifies
t
which is
still visible as a typedef name
).


It states that a declaration
const t:5
will make constant a previous definition of the type name
t
. If we test this using gcc we see that we can mutate such a variable, so gcc ignores
const t
. The same happens with with a compilation with clang.

I understand that it is possible to exist variables defined/declared via type qualifiers without type specifiers, which was contrary to my belief that a qualifier was only an attribute of a node keeping a type specifier, etc.

Where is the ambiguity in the C99?

typedef signed int t;
typedef int plain;
struct tag {
unsigned t:4;
const t:5;
plain r:5;
};

int
main()
{
t x;
x = 10;
return 0;
}


On the other hand, a function like the next one also compiles:

int
main()
{
struct tag x = {.t = 3};
x.t = 4;
return 0;
}


REMARK:

I used clang and gcc.

DOCUMENTS:

Page 138, §6.7.8 from this edition

ISO/IEC 9899:201x Committee Draft — April 12, 2011 N1570

OR

Page 124, §6.7.7 from this edition

ISO/IEC 9899:TC3 Committee Draft — September 7, 2007 WG14/N1256

Answer

The complete spiel for the example (from ISO/IEC 9899:2011) is:

EXAMPLE 3 The following obscure constructions

typedef signed int t;
typedef int plain;
struct tag {
    unsigned t:4;
    const t:5;
    plain r:5;
};

declare a typedef name t with type signed int, a typedef name plain with type int, and a structure with three bit-field members, one named t that contains values in the range [0, 15], an unnamed const-qualified bit-field which (if it could be accessed) would contain values in either the range [−15, +15] or [−16, +15], and one named r that contains values in one of the ranges [0, 31], [−15, +15], or [−16, +15]. (The choice of range is implementation-defined.) The first two bit-field declarations differ in that unsigned is a type specifier (which forces t to be the name of a structure member), while const is a type qualifier (which modifies t which is still visible as a typedef name). If these declarations are followed in an inner scope by

t f(t (t));
long t;

then a function f is declared with type ‘‘function returning signed int with one unnamed parameter with type pointer to function returning signed int with one unnamed parameter with type signed int’’, and an identifier t with type long int.

Note that the field identified by const t:5; is anonymous and therefore cannot be accessed (the standard mentions that with the 'if it could be accessed' comment, and the statement that it is an 'unnamed const-qualified bit-field'). The field named t is not const-qualified; it can be modified.

And the const field is part of the structure struct tag and does not affect anything outside that structure type.

Both your example programs should compile — it is just as well that GCC and Clang do compile them.

Note, too, that the example is marked 'obscure constructions'. Anyone actually writing code like that for production should be … shunned until they repent of their evil ways.

Comments