I've seen code like this:
char str[1024] = {0, };
and suspect that it is similar to doing this:
char str[1024];
str[0] = '\0';
But I couldn't find anything on it so I'm not sure.
What is this (called) and what does it do?
Disclaimer: I'm aware this might have been asked and answered before, but searching for {0, } is astonishingly hard. If you can point out a duplicate, I'll happily delete this question.
No, they are not the same.
This statement
char str[1024] = {0, };
initializes the first element to the given value 0, and all other elements are to be initialized as if they have static storage, in this case, with a value 0. Syntactically this is analogous to using
char str[1024] = {0};
Quoting C11, chapter 6.7.9, p21
If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.
and, from p10 (emphasis mine)
If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. If an object that has static or thread storage duration is not initialized explicitly, then:
if it has pointer type, it is initialized to a null pointer;
if it has arithmetic type, it is initialized to (positive or unsigned) zero;
if it is an aggregate, every member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;
if it is a union, the first named member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;
On the other hand
char str[1024];
str[0] = '\0';
only initializes the first element, and the remaining elements remains unitialized, containing indeterminate values.
The {0, } initializer is the same as the more common {0} initializer.
The trailing comma is allowed by the syntax but it makes no difference.
6.7.9--Initialization:
initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }
initializer-list:
designationopt initializer
initializer-list , designationopt initializer
designation:
designator-list =
designator-list:
designator
designator-list designator
designator:
[ constant-expression ]
. identifier
The semantics are such that the 0, which is syntactically required because at least one initializer-list item is syntactically required (it's a kind of a arbitrary requirement: compilers frequently support an empty {} as well) initializes the first subobject recursively (6.7.9p17):
Each brace-enclosed initializer list has an associated current object. When no designations are present, subobjects of the current object are initialized in order according to the type of the current object: array elements in increasing subscript order, structure members in declaration order, and the first named member of a union.148) In contrast, a designation causes the following initializer to begin initialization of the subobject described by the designator. Initialization then continues forward in order, beginning with the next subobject after that described by the designator.
and the rest is initialized as it would be if the whole object had static storage duration (6.7.9p19,6.7.9p21). This practically means to 0 (as with memset(,0,) although with the caveat that paddings need not be initialized and that 0-initialized pointers need not necessarily be "all-bits zero".
As far as I know, compilers on usual platforms (where pointers are all-bits-zero) just mostly do what they would do with memset(,0,).
This "universal" zero initialization works because the first 0 will recursively hit a scalar type (number on pointer) which can be invariably initialized with the 0 initializer. The default "as-with-static-storage-duration" then initialization applies to the rest.
A perhaps slightly more interesting of the trailing initializer comma doing nothing would be:
int main()
{
char one[]={0,}; //<the comma doesn't introduce another member
_Static_assert(sizeof(one)==1,""); //holds
}
Related
Say I have a struct typedef that is:
typedef struct {
int32_t memberOne;
int32_t memberTwo;
} myStruct_t;
I instantiate a const of that type as follows:
const myStruct_t myConst = {.memberTwo = 32};
What does C say the compiler should set memberOne to be? I've tried it, of course, and I happen to get 0 on the compilers I've tried, what I'm after here is does the C standard require uninitialised members of a const struct to be initialised to something or other by the compiler, i.e. would the code above be considered portable? Clause 6.7.9 of C99 says:
If an object that has static or thread storage duration is not initialized explicitly, then:
— if it has pointer type, it is initialized to a null pointer;
— if it has arithmetic type, it is initialized to (positive or unsigned) zero;
— if it is an aggregate, every member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;
— if it is a union, the first named member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;
...but what about consts? Are they considered to be of static storage type, just without the static keyword?
does the C standard require uninitialised members of a const struct to be initialised to something
Yes, they are guaranteed to be set to zero/null pointers as long as you initialize at least one member explicitly. const plays no part in it.
You've already found the relevant part about how objects with static storage duration are initialized. Just keep reading the same chapter:
C17 6.7.9 §19
The initialization shall occur in initializer list order, each initializer provided for a
particular subobject overriding any previously listed initializer for the same subobject;
all subobjects that are not initialized explicitly shall be initialized implicitly the same as
objects that have static storage duration.
C17 6.7.9 §21
If there are fewer initializers in a brace-enclosed list than there are elements or members of an
aggregate, or fewer characters in a string literal used to initialize an array of known size than there
are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as
objects that have static storage duration.
This question already has answers here:
What will be the value of un-initialized element in the array?
(1 answer)
Are unmentioned struct fields *always* initialised to zero (i.e. when the struct is on the stack)?
(2 answers)
Closed 5 years ago.
Is it safe to initialize some of the elements in the array like this?
const char *str_array[50] = {
[0] = "str_0",
[10] = "str_10",
[24] = "str_24",
[45] = "str_45",
};
Can I rely on the other elements of the array being properly initialized?
The initialization shown in the question is safe, and the elements not specifically initialized with a designated initializer are (in this context) initialized to NULL. In general, the uninitialized elements are initialized the same as a static variable of the same type would be initialized, which is some variation on the theme of 'zero'.
The relevant section of the C standard (ISO/IEC 9899:2011) is §6.7.9 Initialization, and specifically ¶19:
The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject;151) all subobjects that are not initialized explicitly shall be initialized implicitly the same as objects that have static storage duration.
151) Any initializer for the subobject which is overridden and so not used to initialize that subobject might not be evaluated at all.
There was a rejoinder in the comments:
What if I want other elements to set to same default value?
Unfortunately, you have to choose NULL (in this case; zero in the general case) as the default value. You don't have any other alternatives in standard C (unlike sophisticated modern languages such as, oh, I dunno — let's think of Fortran 66). There's no way in standard C to repeat an initializer other than by writing it many times.
GCC has an extension that allows you to do that (which is documented in the GCC manual in a section with the title Designated Initializers that documents both standard behaviour and non-standard behaviour). Using the GNU extension, you could write:
const char *str_array[50] = {
[1 ... 49] = "empty string", // GCC extension
[0] = "str_0",
[10] = "str_10",
[24] = "str_24",
[45] = "str_45",
};
Note that it is OK to specify two initializers for a cell (such as 10, 24, 45 — the other is via the repeated initializer); the last one mentioned wins. Also note the space separating the ... from the 1 (and 45); that is crucial because of the 'maximal munch rule' which means that [1...45] would be tokenized as [, 1., ., .45, ], where the floating point numbers are not what's wanted.
Yes, it's quite safe.
See N1570 section 6.7.9.
Paragraph 19, discussing initializer lists:
... all subobjects that are not initialized explicitly shall be
initialized implicitly the same as objects that have static storage
duration.
Paragraph 10:
If an object that has automatic storage duration is not initialized
explicitly, its value is indeterminate. If an object that has static
or thread storage duration is not initialized explicitly, then:
if it has pointer type, it is initialized to a null pointer;
if it has arithmetic type, it is initialized to (positive or unsigned) zero;
— if it is an aggregate, every member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;
if it is a union, the first named member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;
This seems like a hole in my knowledge. As far as I am aware, in C99 if you initialise a single element of a struct and no others, the others are zero initialised. Does the following code zero initialise all the members of a struct though?
typedef struct
{
int foo;
int bar;
char* foos;
double dar;
} some_struct_t;
some_struct_t mystructs[100] = {};
Update: There are some comments indicating that this syntax is an extension. If that is the case, is there any way of doing this that is pure C99 compliant?
As per C11, chapter §6.7.9, Initialization syntax, (for the sake of completeness, same mentioned in chapter §6.7.8 in C99)
initializer:
assignment-expression
{ initializer-list }
{ initializer-list , }
initializer-list:
designationopt initializer
initializer-list , designationopt initializer
designation:
designator-list =
designator-list:
designator
designator-list designator
designator:
[ constant-expression ]
. identifier
Which implies, the brace closed initializer list should have at minimum one initializer element (object).
In your code, the empty initializer list
some_struct_t mystructs[100] = {}; //empty list
is not a valid pure C syntax; it's a compiler extension.
You need to mention a single element in the list to make it standard conforming, like
some_struct_t mystructs[100] = {0};
which meets the criteria, from paragraph 21 of same standard(s),
If there are fewer initializers in a brace-enclosed list than there are elements or members
of an aggregate, or fewer characters in a string literal used to initialize an array of known
size than there are elements in the array, the remainder of the aggregate shall be
initialized implicitly the same as objects that have static storage duration.
So, in this case, you have one explicit 0 and remaining implicit zero-initialization (or similar).
Because it's array initialisation, you would need
some_struct_t mystructs[100] = { 0 }; // ensure all array elements (struct) being zero initialisation
For structs/unions (and arrays) there is a rule saying that if it is partially initialized, the rest of the items that didn't get initialized explicitly by the programmer are set to zero.
So by typing some_struct_t mystructs[100] = {0}; you tell the compiler to explicitly set foo to zero. And then the rest of the struct members gets set to zero as well, implicitly.
This has nothing to do with C99, but works for all C standard versions. Although in C99/C11, a designated initializer {.foo=0} would have achieved the very same result.
I have an array of 3 elements. But I only want to initialize 2 of them.
I let the third element blank.
unsigned char array[3] = {1,2,};
int main(){
printf("%d",array[2]);
return 0;
}
The print result is 0. I tested it on IAR, and some online compiler.
Is there any C rule for the value of third element?
Is there any compiler filling the third element by 0xFF ? (Especially cross compiler)
Yes, the C standard does define what happens in this case. So no, there should be no C standard compliant compiler that intialises with 0xFF in this case.
Section 6.7.9 of the standard says:
Initialisation
...
10 ...If an object that has static or thread storage duration is not
initialized explicitly, then:
if it has pointer type, it is initialized to a null pointer;
if it has arithmetic type, it is
initialized to (positive or unsigned) zero;
if it is an aggregate,
every member is initialized (recursively) according to these rules,
and any padding is initialized to zero bits;
if it is a union, the first named member is initialized (recursively) according to these rules, and any padding is initialized to zero bits;
...
21 If there are fewer initializers in a brace-enclosed list than there
are elements or members of an aggregate, or fewer characters in a
string literal used to initialize an array of known size than there
are elements in the array, the remainder of the aggregate shall be
initialized implicitly the same as objects that have static storage
duration.
From this post, it appears that that syntax will initialize all elements after the comma to zero. Moreover; all uninitialized data in the data segment of the program (in other words all uninitialized global variables) are automatically set to zero, so if you are looking for undefined behavior in this program, there isn't any; it will always be 0.
This can be achieved with gcc extension as below
unsigned char array[10] = {1,2,[2 ... 9] = 0xFF};
Suppose the following initialization:
char mystr[4] = "";
Does the C99 standard guarantee that a character array initialized to an empty string will initialize all elements in the character array to null bytes? For example, does the standard guarantee that mystr[2] == '\0'?
How about these initializations:
char myfoo[4] = { '\0' };
char mybar[4] = { 0 };
While I'm pretty certain that explicitly setting the first element of a character array will guarantee the implicit initialization of the rest of the elements to 0, I suspect a string literal initialization results in a copy to the array -- thus meaning a single \0 is copied to the array while the remaining elements are left uninitialized.
Section 6.7.8, paragraph 21:
If there are fewer initializers in a brace-enclosed list than there are elements or members
of an aggregate, or fewer characters in a string literal used to initialize an array of known
size than there are elements in the array, the remainder of the aggregate shall be
initialized implicitly the same as objects that have static storage duration.
And how are objects with static storage duration initialized?
Section 6.7.8, paragraph 10:
If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate. If an object that has static storage duration is not initialized explicitly, then:
if it has pointer type, it is initialized to a null pointer;
if it has arithmetic type, it is initialized to (positive or unsigned) zero;
if it is an aggregate, every member is initialized (recursively) according to these rules;
if it is a union, the first named member is initialized (recursively) according to these
rules.
char is an arithmetic type, so it's initialized to 0. Huzzah, you can rest easy.
C language follows "all or nothing" approach to all aggregate initializations. It means that any attempt to supply an explicit initializer for any part of the aggregate (regardless of how small that part is) immediately guarantees that the entire aggregate will be initialized. The parts without an explicit initializer will be zero-initialized.
In your example the entire array is guaranteed to be initialized with zeros. In case of struct initialization, all fields that are not initialized explicitly get zero values.
One consequence of that principle is that in C language the = { 0 } initializer serves as an idiomatic universal zero initializer. Since the language allows = { value } syntax in scalar object initializers as well, one can use = { 0 } to initialize any object to all-zero state
#define UNIVERSAL_ZERO { 0 }
double d = UNIVERSAL_ZERO;
char s[100] = UNIVERSAL_ZERO;
struct { int x, y, z; } xyz = UNIVERSAL_ZERO;
int *p = UNIVERSAL_ZERO;
Yes, excess elements of an array are always zero-initialized, and arrays of char initialized using a string literal are no exception either.
From the C99 Standard, section 6.7.8.21 (page 139 of this document):
21 If there are fewer initializers in a brace-enclosed list than there are elements or members
of an aggregate, or fewer characters in a string literal used to initialize an array of known
size than there are elements in the array, the remainder of the aggregate shall be
initialized implicitly the same as objects that have static storage duration.