I expected it to be possible to apply alignas/_Alignas to an entire struct declaration, like this:
#include <stddef.h>
#include <stdalign.h>
struct alignas(max_align_t) S {
int field;
};
struct S s = { 0 };
but both gcc and clang reject the declaration:
(gcc 6.3)
test.c:4:8: error: expected ‘{’ before ‘_Alignas’
struct alignas(max_align_t) S {
^
(clang 3.8)
test.c:4:1: error: declaration of anonymous struct must be a definition
struct alignas(max_align_t) S {
^
What gives? Note that both compilers accept this construct if I compile the file as C++, or if alignas is replaced with the equivalent GCC extension,
struct __attribute__((aligned(__alignof__(max_align_t)))) S {
int field;
};
Also note that the other plausible placements of alignas,
alignas(max_align_t) struct S { ... };
struct S alignas(max_align_t) { ... };
struct S { ... } alignas(max_align_t);
also throw syntax errors (albeit different ones).
C11 is not very clear on these things, but a consensus has emerged how this is to be interpreted. C17 will have some of this clarified. The idea of not allowing types to be aligned is that there should never be different alignment requirements for compatible types between compilation units. If you want to force the alignment of a struct type, you'd have to impose an alignment on the first member. By that you'd create an incompatible type.
The start of the "Constraint" section as voted by the committee reads:
An alignment specifier shall appear only in the declaration specifiers
of a declaration, or in the specifier-qualifier list of a member
declaration, or in the type name of a compound literal. An alignment
specifier shall not be used in conjunction with either of the
storage-class specifiers typedef or register, nor in a declaration of
a function or bit-field.
With GCC 7.2.0 (compiled on macOS Sierra, running on macOS High Sierra), and also with GCC 6.3.0 on the same platform, and with Clang from XCode 9, I get:
$ cat align17.c
#include <stddef.h>
#include <stdalign.h>
alignas(max_align_t) struct S
{
int field;
}; // Line 7
struct S s = { 0 };
// Alternative
struct S1
{
int field;
};
alignas(max_align_t) struct S1 s1 = { 1 };
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -c align17.c
align17.c:7:1: error: useless ‘_Alignas’ in empty declaration [-Werror]
};
^
cc1: all warnings being treated as errors
$ clang -O3 -g -std=c11 -Wall -Wextra -Werror -c align17.c
align17.c:4:1: error: attribute '_Alignas' is ignored, place it after "struct" to apply attribute to type
declaration [-Werror,-Wignored-attributes]
alignas(max_align_t) struct S
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/9.0.0/include/stdalign.h:28:17: note:
expanded from macro 'alignas'
#define alignas _Alignas
^
1 error generated.
$
It appears that these compilers only allow you to apply the _Alignas (or alignas) alignment specifier to a variable declaration, not to a type definition. Your code — the code which produces the error (warning converted to error) — attempts to apply the alignment to the type.
Note that putting alignas(max_align_t) betweeen struct and S clearly violates the standard. Putting it in other places does not seem to work unless there is actually a variable defined, despite the message hinting "place it after "struct" to apply attribute to type declaration". Thus, this code compiles because it defines variable s0. Omit s0 and it fails to compile.
struct S
{
int field;
} alignas(max_align_t) s0;
ISO/IEC 9899:2011 — The C11 Standard
6.7 Declarations
Syntax
1 declaration:
declaration-specifiers init-declarator-listopt ;
static_assert-declaration
declaration-specifiers:
storage-class-specifier declaration-specifiersopt
type-specifier declaration-specifiersopt
type-qualifier declaration-specifiersopt
function-specifier declaration-specifiersopt
alignment-specifier declaration-specifiersopt
6.7.2 Type specifiers
Syntax
1 type-specifier:
void
char
short
int
long
float
double
signed
unsigned
_Bool
_Complex
atomic-type-specifier
struct-or-union-specifier
enum-specifier
typedef-name
6.7.2.1 Structure and union specifiers
Syntax
1 struct-or-union-specifier:
struct-or-union identifieropt { struct-declaration-list }
struct-or-union identifier
struct-or-union:
struct
union
6.7.5 Alignment specifier
Syntax
1 alignment-specifier:
_Alignas ( type-name )
_Alignas ( constant-expression )
Constraints
2 An alignment attribute shall not be specified in a declaration of a typedef, or a bit-field, or a function, or a parameter, or an object declared with the register storage-class specifier.
Semantics
6 The alignment requirement of the declared object or member is taken to be the specified
alignment. An alignment specification of zero has no effect.141) When multiple
alignment specifiers occur in a declaration, the effective alignment requirement is the
strictest specified alignment.
7 If the definition of an object has an alignment specifier, any other declaration of that
object shall either specify equivalent alignment or have no alignment specifier. If the
definition of an object does not have an alignment specifier, any other declaration of that
object shall also have no alignment specifier. If declarations of an object in different
translation units have different alignment specifiers, the behavior is undefined.
141) An alignment specification of zero also does not affect other alignment specifications in the same
declaration.
Interpretation
These rules do not allow the alignment specifier between struct and its tag, or between structtag and the { … } structure definition (even if the GCC __attribute__ notation worked in such contexts).
The rules in §6.7.5 ¶2 do not self-evidently preclude the alignment specifier in a structure type definition, though other wording implies that it could be used with a structure member (inside the { … } section), or associated with a variable declaration or definition. However, the fact that an alignment specifier is not allowed in a typedef has implications that it shouldn't be allowed in a type definition either.
In the short term, you will, I think, have to accept that what you tried to do (attach the alignment specifier to the structure type declaration) doesn't work. You can consider whether it is worth looking for a GCC (or Clang) bug report, and if there is none, whether it is worth creating one. It is marginal, to my way of thinking, whether it is actually a bug, but I can't see anything in the quoted standard material that precludes what you attempted directly. However, if you can't apply alignment to a typedef, it also makes sense not to be able to apply it to a structure type declaration — which is why I consider it marginal (I wouldn't be surprised to find that any attempted bug report is rejected).
Related
I've heard quite a bit about this line: void typedef name.
Compiles well:
void typedef name;
int main(){}
What does void even mean? Doesn't it just mean that something is just void? How can it be used before a keyword like typedef? Isn't it invalid use of void? It seems to be the same as doing:
typedef void name;
And makes name an alias for void.
Does the compiler rearrange typedef and void?
I thought void is only usable with functions:
void func();
But for what things can void be used and what does it mean? And how does void typedef name work even though it's not a function declaration?
typedef is by syntax a storage class specifier, of which there can only be one in the declaration specifiers (except _Thread_local).
The order of declaration spcifiers (the "T", not the "D" part of a declaration) is free - most put const char and not char const. But e.g. char const*const x is also used.
Semantically, typedef creates "only" a synonym for the given type. So yes, the compiler does rearrange (parses that declaration)
While a void x; declaration gives an error, it compiles with a typedef. Depends how you use it later e.g. for a pointer:
typedef void _0;
_0 *y;
Even though _0* y is the same, the position of the * can not be switched. It is on the left of the declarator ("D") giving T*D with all white-space variations.
In C, a declaration is one or more declaration-specifiers followed by a list of declarators with optional initializers. Humans tend to write the specifiers in a particular order, but the C grammar allows them to be written in any order. They are:
storage-class-specifier: typedef, extern, static, _Thread_local, auto, and register.
type-specifier: void, char, short, int, long, float, double, signed, unsigned, _Bool, _Complex, _Atomic (type-name), and structure specifiers, union specifiers, enum specifiers, and (previously defined) typedef names.
type-qualifier: const, restrict, volatile, and _Atomic.
function-specifier: inline and _Noreturn.
alignment-specifier: _Alignas (type-name) and _Alignas (constant-expression).
There are some rules about how many times various specifiers may appear and rules about which may appear with which. However, there is no rule about the order in which they appear. So all of the above can be rearranged as desired. That includes putting some of the specifiers in between the type-specifiers that we normally group together, such as const long static int _Alignas (double) long signed x;, which contains signed long long int in a confusing way. The compiler does not care; it remembers everything and figures it out.
So void typedef name is the same as typedef void name; it defines name to be an alias for void.
As you can see from the list above, void is a type-specifier. Quite simply, it specifies a type. That type is defined to be incomplete, meaning it has no defined size. Further, the C standard specifies it cannot be completed; no size can be given to it. The fact that it has no size serves to prevent programs from accidentally referring to void objects: If p is a void *, then *p would refer to a void object, and, if a program actually attempts to access it, the compiler should issue a diagnostic message.
Supplementary Note
The declarators in a declaration are the things built around identifiers being declared:
A plain identifier.
A declarator followed brackets [ and ] to indicate an array, with various options for inside the brackets.
A declarator followed by parentheses ( and ) to indicate a function, with various options for inside the parentheses.
A declarator preceded by * to indicate a pointer, optionally with type-qualifiers (like const) between the * and the declarator.
A declarator inside parentheses, to override the default grouping in complicated declarators mixing pointers and arrays and such.
This question is by mere curiosity. It is not about the empty struct.
I just stumbled over an interesting typo of the kind
struct {
int member1; /*comment*/ ; /* <-- note the ';' */
int member2;
} variable[] = { /* initializers */ };
which the compiler (xc32, derived from gcc) accepted without any
complaints. Of course, I corrected this but the software was running
smoothly before and after the correction and the additional ; seemingly
causes no problems. I then tried various lengthes of ;;; in he struct definition an they seem to make no difference neither to functionality
nor to sizeof. So in a struct any sequence of ;;;; seems to be
equivalent to a single ;.
I couldn't find anything about such "empty members" of a struct/union
in the specification, neither that they are allowed nor that they are
disallowed. To me it seems as if the grammar rejected them. This contrasts
to "empty declarations" ; at the top level of a compilation unit, which the
standard clearly forbids and the "null" statement ; in functions which is a
clearly allowed language feature.
Does anyone know about this behaviour? Is it compiler specific or does the
C specification somehow tolerate such empty struct members?
The syntax is specified in C11 6.7.2.1
struct-declaration:
specifier-qualifier-list struct-declarator-listopt ;
static_assert-declaration
There is 1 semicolon at the end, so that's the only allowed syntax. You cannot skip the semicolon, you can't add extra ones. And that's that.
(You can however have a static assert inside a struct declaration, from C11.)
The standard doesn't talk about that, it's just a gcc tolerance. See 6.7.2.1:
struct-or-union-specifier:
struct-or-union identifieropt { struct-declaration-list }
struct-or-union identifier
struct-or-union:
struct
union
struct-declaration-list:
struct-declaration
struct-declaration-list struct-declaration
struct-declaration:
specifier-qualifier-list struct-declarator-listopt ;
static_assert-declaration
specifier-qualifier-list:
type-specifier specifier-qualifier-listopt
type-qualifier specifier-qualifier-listopt
(type-specifier and type-qualifier can't be empty, see the related sections in the standard for details.)
Some compilers, like gcc, tolerate extra semi-colons, but -Wpedantic option reveals that it's only a tolerance:
struct foo {
int a;
;;;
};
int main() {
;;;
}
With -pedantic option gcc complains, not on the main empty statements, but on the extra semicolons of the structure declaration.
<source>:3:5: warning: extra semicolon in struct or union specified [-Wpedantic]
;;;
^
<source>:3:6: warning: extra semicolon in struct or union specified [-Wpedantic]
;;;
^
<source>:3:7: warning: extra semicolon in struct or union specified [-Wpedantic]
;;;
Other compilers may not be that friendly, so the typo must be fixed, since it doesn't bring anything useful.
I have the following struct types:
typedef struct PG_Point PG_Point;
struct PG_Point
{
int x;
int y;
};
typedef struct PG_Size PG_Size;
struct PG_Size
{
int width;
int height;
};
typedef struct PG_Bounds PG_Bounds;
struct PG_Bounds
{
union
{
struct
{
PG_Point topLeft;
PG_Size size;
};
struct
{
struct
{
int x;
int y;
};
struct
{
int width;
int height;
};
};
};
};
with the following initializers:
#define PG_Point_init(ix, iy) {.x=(ix), .y=(iy)}
#define PG_Size_init(iwidth, iheight) {.width=(iwidth), .height=(iheight)}
#define PG_Bounds_init(ix, iy, iwidth, iheight) { \
.topLeft=PG_Point_init((ix),(iy)), \
.size=PG_Size_init((iwidth),(iheight)) }
From what I understand, it's correct in c11 to initialize the fields of an anonymous struct as if they were directly fields of the containing struct? But with gcc 4.9.2, this gives the following warning:
warning: missing initializer for field ‘size’ of ‘struct <anonymous>’ [-Wmissing-field-initializers]
It works if I change the initializer to this version:
#define PG_Bounds_init(ix, iy, iwidth, iheight) {{{ \
.topLeft=PG_Point_init((ix),(iy)), \
.size=PG_Size_init((iwidth),(iheight)) }}}
That is, explicitly having the union and struct as sub aggregates.
Is this even allowed? Do I have to expect other compilers to reject this?
From what I understand, it's correct in c11 to initialize the fields of an anonymous struct as if they were directly fields of the containing struct?
There are two parts to that. First of all, we need to tackle the question of whether such members can be initialized at all, because Paragraph 6.7.2.1/13 identifies anonymous structure and union members as specific kinds of "unnamed members", and paragraph 6.7.9/9 says
Except where explicitly stated otherwise, for the purposes of this subclause unnamed members of objects of structure and union type do not participate in initialization.
The rest of section 6.7.9 (Initialization) nowhere says anything that I would interpret as explicitly applying to anonymous structure and anonymous union members themselves, but I don't think the intent is to prevent initialization of the named members of anonymous members, especially given that they are considered members of the containing structure or union (see below). Thus, I do not interpret the standard to forbid the initialization you are trying to perform.
So yes, I read C11 to allow your initializer and to specify that it has the effect you appear to intend. In particular, paragraph 6.7.2.1/13 of the standard says, in part,
The members of an anonymous structure or union are considered to be members of the containing structure or union. This applies recursively if the containing structure or union is also anonymous.
Your initializer therefore satisfies the constraint in paragraph 6.7.9/7, that the designators within specify names of members of the current object (in your case, a struct PG_Bounds). The following paragraphs of section 6.7.9 present the semantics for initializers, and I see no reason to interpret them to specify anything other than initialization of the overall object with the values you have provided.
At this point, I reiterate that gcc is issuing a warning, not rejecting your code, and in this case I think the warning is spurious. I wrote a test program such as I suggested in comments that you do, and tried it on gcc 4.8.5 in C11 mode. Although gcc emitted the same warning you presented (but only with -Wextra enabled), I was able to demonstrate that your initializer initialized all members of a subject struct PG_Bounds to the intended values.
You also observe that gcc does not warn if you change the initializer to a version that uses nested brace-enclosed initializers, and ask
Is this even allowed? Do I have to expect other compilers to reject this?
This could be viewed as more problematic with respect to paragraph 6.7.9/9, so in that sense it is perhaps riskier. I am uncertain whether there is any compiler that actually rejects it or does the wrong thing with it. I think the intent of the standard is to allow this initializer, but I would prefer the other form, myself.
From the C99 standard, 6.7(5):
A declaration specifies the interpretation and attributes of a set of identifiers. A definition of an identifier is a declaration for that identifier that:
for an object, causes storage to be reserved for that object;
for a function, includes the function body;
for an enumeration constant or typedef name, is the (only) declaration of the identifier.
If identifiers with typedef are in fact definitions, then why are they allowed to be declared more than once? Example:
int main()
{
typedef int x;
typedef int x;
}
Above program compiles with no errors. How is this possible? I was expecting the program to give me a multiple definition error.
C99 is different from C11
The rules change between C99 and C11 (and the C11 rules match the C++ rules, as I understand it). Note that in both standards, ¶3 is in the Constraints section and ¶5 is in the Semantics section. That is important for error messages — constraint violations require a diagnostic.
ISO/IEC 9899:1999 §6.7 Declarations
¶3 If an identifier has no linkage, there shall be no more than one declaration of the identifier
(in a declarator or type specifier) with the same scope and in the same name space, except
for tags as specified in 6.7.2.3.
5 A declaration specifies the interpretation and attributes of a set of identifiers. A definition
of an identifier is a declaration for that identifier that:
for an object, causes storage to be reserved for that object;
for a function, includes the function body;98)
for an enumeration constant or typedef name, is the (only) declaration of the
identifier.
ISO/IEC 9899:2011 §6.7 Declarations
¶3 If an identifier has no linkage, there shall be no more than one declaration of the identifier
(in a declarator or type specifier) with the same scope and in the same name space, except
that:
a typedef name may be redefined to denote the same type as it currently does,
provided that type is not a variably modified type;
tags may be redeclared as specified in 6.7.2.3.
¶5 A declaration specifies the interpretation and attributes of a set of identifiers. A definition
of an identifier is a declaration for that identifier that:
for an object, causes storage to be reserved for that object;
for a function, includes the function body;119)
for an enumeration constant, is the (only) declaration of the identifier;
for a typedef name, is the first (or only) declaration of the identifier.
Lundin noted that the Standard C Committee's web site contains n1360, a one page document detailing why this change was made. Basically: C++ does it; some compilers already do it; it is neither hard to do nor subverting anything to permit (require) it.
GCC doesn't always enforce the standard
If your code is compiling, then it is being compiled under the C11 rules, or C++ rules. It is not being compiled under (strict) C99 rules.
Note that sufficiently recent versions of GCC allow the redefinition unless you insist otherwise, but also note the report by John Bollinger that GCC 4.4.7 (on an unidentified platform) does not allow the redefinition at all in C99 mode.
Consider the file retypedef.c:
int main(void)
{
typedef int x;
typedef int x;
x y = 0;
return y;
}
Compiling on Mac OS X 10.9.5 with GCC 4.9.1, I get:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -c retypedef.c
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -pedantic -c retypedef.c
$ gcc -O3 -g -std=c99 -Wall -Wextra -Werror -c retypedef.c
$ gcc -O3 -g -std=c99 -Wall -Wextra -Werror -pedantic -c retypedef.c
retypedef.c: In function ‘main’:
retypedef.c:4:17: error: redefinition of typedef ‘x’ [-Werror=pedantic]
typedef int x;
^
retypedef.c:3:17: note: previous declaration of ‘x’ was here
typedef int x;
^
cc1: all warnings being treated as errors
$
It doesn't complain unless -pedantic is used, and then only if C99 is requested (it is standard compliant to redefine a typedef in the same scope in C11).
Your program compiles without error because your compiler is being lax (or compiling for a different standard). If I compile your code with gcc 4.4.7 then it in fact does report an error about the redefinition of x.
For the same reason that other declarations are allowed to be declared multiple times. For example:
void foo(int a);
void foo(int a);
int main()
{
foo(42);
}
void foo(int a)
{
printf("%d\n", a);
}
This allows more than one header to declare a function or structure, and allow two or more such headers to be included in the same translation unit.
typedefs are similar to prototyping a function or structure -- they declare identifiers that have some compile time meaning, but no runtime meaning. For example, the following wouldn't compile:
int main()
{
typedef int x;
typedef int x;
x = 42;
}
because x does not name a variable; it is only a compile time alias for the name int.
According to me, it is zero but there seems to be bit confusion here
I have tested it with gcc compiler and it gives me zero as output. I know that in C++, size of an empty class is 1. Let me know if I am missing anything here.
A struct cannot be empty in C because the syntax forbids it. Furthermore, there is a semantic constraint that makes behavior undefined if a struct has no named member:
struct-or-union-specifier:
struct-or-union identifieropt { struct-declaration-list }
struct-or-union identifier
struct-or-union:
struct
union
struct-declaration-list:
struct-declaration
struct-declaration-list struct-declaration
struct-declaration:
specifier-qualifier-list struct-declarator-list ;
/* type-specifier or qualifier required here! */
specifier-qualifier-list:
type-specifier specifier-qualifier-listopt
type-qualifier specifier-qualifier-listopt
struct-declarator-list:
struct-declarator
struct-declarator-list , struct-declarator
struct-declarator:
declarator
declaratoropt : constant-expression
If you write
struct identifier { };
It will give you a diagnostic message, because you violate syntactic rules. If you write
struct identifier { int : 0; };
Then you have a non-empty struct with no named members, thus making behavior undefined, and not requiring a diagnostic:
If the struct-declaration-list contains no named members, the behavior is undefined.
Notice that the following is disallowed because a flexible array member cannot be the first member:
struct identifier { type ident[]; };
The C grammar doesn't allow the contents of a struct to be empty - there has to be at least an unnamed bitfield or a named member (as far as the grammar is concerned - I'm not sure if a struct that contains only an unnamed bitfield is otherwise valid).
Support for empty structs in C are an extension in GCC.
In C++ and empty struct/class member-specification is explicitly permitted, but the size is defined to be 1 - unless as part of the empty base optimization the compiler is allowed to make an empty base class take no space in the derived class.
In C99: "If the struct-declaration-list contains no named members, the behavior is undefined."
The syntax doesn't really allow it anyway, though I don't see anything that says a diagnostic is required, which puts it pretty much back in the "undefined behavior" camp.
on VC 8 It gives error if we try to get the sizeof empty struct, on the other way round on linux with gcc it gives size 1 because it uses gcc extention instead of c language specification which says this is undefined behaviour.
struct node
{
// empty struct.
};
int main()
{
printf("%d", sizeof(struct node));
return 0;
}
on windows vc 2005 It gives compilation error
on linux with gcc it gives size 1 because gcc extension
http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Empty-Structures.html#Empty-Structures
(As Pointed out by Michael Burr)