If I have a common linux struct like:
struct sockaddr_in
{
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int)
- sizeof(unsigned short int) - sizeof(struct in_addr)];
};
#define sin_zero __pad
and I perform aggregate initialization:
struct sockaddr_in my_addr = { 0 };
how come that this initializes every member to 0?
I mean: documentation says:
If the number of initializer clauses is less than the number of
members or initializer clauses is completely empty, the remaining
members are initialized by their brace-or-equal initializers, if
provided in the class definition, and otherwise (since C++14) by empty
lists, which performs value-initialization.
to make it easy: why does this code print 0?
struct sockaddr_in my_addr = {2, 2}; // initialize sin_family and sin_port to 2, everything else value-initialized
my_addr = {0};
std::cout << my_addr.sin_port; // Why is this 0?
This is covered in the draft C++14 standard section 8.5.4 List-initialization which says:
List-initialization of an object or reference of type T is defined as follows:
and includes:
If T is an aggregate, aggregate initialization is performed (8.5.1).
and has the following example:
struct S2 {
int m1;
double m2, m3;
}
S2 s21 = { 1, 2, 3.0 }; // OK
S2 s22 { 1.0, 2, 3 }; // error: narrowing
S2 s23 { }; // OK: default to 0,0,0
and 8.5.1 Aggregates which says:
If there are fewer initializer-clauses in the list than there are
members in the aggregate, then each member
not explicitly initialized shall be initialized from its
brace-or-equal-initializer or, if there is no
brace-or-equalinitializer, from an empty initializer list (8.5.4). [
Example:
struct S { int a; const char* b; int c; int d = b[a]; };
S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", ss.c with the value of an
expression of the form int{} (that is, 0)
Note that 8.5.4 is slightly different in C++11, it says:
List-initialization of an object or reference of type T is defined as
follows:
If the initializer list has no elements and T is a class
type with a default constructor, the object is value-initialized.
Otherwise, if T is an aggregate, aggregate initialization is performed
(8.5.1).
how come that this initializes every member to 0?
Because that's what C does when initializing structs, and C++ does the same for aggregate initialization for compatibility.
C does it because it's more convenient (it's usually what you want for members that you don't give an explicit value to) and safer that way (it doesn't leave dangerously uninitialized variables lying around).
If you really want the other members of the struct to remain uninitialized you can do it like this:
struct sockaddr_in s; // entirely uninitialized
s.sin_family = 0; // only initialize one member
Related
After doing a ton of testing and writing this answer on how to initialize a struct to zero in C++ (note: the downvote on it was before my total rewrite of it), I can't understand why = {0} doesn't set all members of the struct to zero!
If you do this:
struct data_t
{
int num1 = 100;
int num2 = -100;
int num3;
int num4 = 150;
};
data_t d3 = {0};
printf("d3.num1 = %i\nd3.num2 = %i\nd3.num3 = %i\nd3.num4 = %i\n\n",
d3.num1, d3.num2, d3.num3, d3.num4);
...the output is:
d3.num1 = 0
d3.num2 = -100
d3.num3 = 0
d3.num4 = 150
...although I expected the output to be this:
d3.num1 = 0
d3.num2 = 0
d3.num3 = 0
d3.num4 = 0
...which means that only the FIRST member was set to zero, and all the rest were set to their defaults.
I was always under the impression that initializing a struct in any of these 3 ways would zero-initialize it, but obviously I'm wrong!
data_t d{}
data_t d = {}
data_t d = {0}
My key takeaway from this answer is therefore this:
The big take-away here is that NONE of these: data_t d{}, data_t d = {}, and data_t d = {0}, actually set all members of a struct to zero!
data_t d{} sets all values to their defaults defined in the struct.
data_t d = {} also sets all values to their defaults.
And data_t d = {0} sets only the FIRST value to zero, and all other values to their defaults.
So, why doesn't initializing a C++ struct to = {0} set all of its members to 0?
Note that my key take-aways above actually contradict this rather official-looking documentation I've been using for years (https://en.cppreference.com/w/cpp/language/zero_initialization), which says that T t = {} ; and T {} ; are both zero initializers, when in fact, according to my tests and take-away above, they are NOT.
References:
How to initialize a struct to 0 in C++
Update: I was just pointed to this reference too: What does {0} mean when initializing an object?
So, why doesn't initializing a C++ struct to = {0} set all of its members to 0?
Because you are only providing one value, while the class has more than one member.
When you have T t{}; or T t = {} what you are doing is called value initialization. In value initialization, if the object/member does not have a default constructor, or a default member initializer, then the compiler falls back to zero initializing the objec/member. So with
data_t d{}
the value of the members in order would be 100, -100, 0 ,150 and that 0 for num3 happens because it has no default and you did not provide a value in the {} so the compiler falls back to zero initializing num3. This is the same with data_t d = {}. With data_t d = {0} you provide the first element, so num1 is 0, but then like the first two, all of the other members are initialized with their default value if they have one, or zero initialized if they don't, giving you 0, -100, 0, 150 for the member values.
This was a change that happened when C++11 was released and allowed for default member initializers.
If your data_t was defined like
typedef struct
{
int num1;
int num2;
int num3;
int num4;
} data_t;
then data_t d{}, data_t d = {}, data_t d = {0} would all leave you with a zero initialized class since there are no default member initializers and the only value you provide in you braced-init-list (the technical name for {...}) is zero so all members become zero.
data_t d3 = {0} is list-initialization syntax which, with aggregates such as data_t, performs aggregate initialization: the provided 0 value is used to initialized the first member and the remaining members are initialized using their corresponding defaults, and if none exist, are value-initialized (emphasis mine, edited for C++14):
If the number of initializer clauses is less than the number of members or initializer list is completely empty, the remaining members are initialized by their default member initializers, if provided in the class definition, and otherwise by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.
value-initialization means zero-initialization for non-class types. That is why the member num3, which has no default value, gets the value 0.
Note: this is not to be confused with default-initialization, which does not initialize non-class types at all. data_t d3; would be default-initialization, and the member num3 would be left in an indeterminate state.
The important point to keep an eye out for is whether or not the object being initialized is an aggregate because the initialization rules are different for aggregates vs. classes with a constructor. In case of a constructor, non-class members without a default value will be default-initialized (i.e. left in an indeterminate state).
Some examples:
struct A { // an aggregate
int num1 = 100;
int num2 = -100;
int num3;
};
struct B { // not an aggregate
int num1 = 100;
int num2 = -100;
int num3;
B() {}
B(int) {}
};
int main() {
A a1; // default-initialization: a1 is {100, -100, ???}
A a2 = {}; // aggregate initialization: a2 is {100, -100, 0}
A a3 = { 1 }; // aggregate initialization: a3 is {1, -100, 0}
A a4 = { 1,2,3 }; // aggregate initialization: a4 is {1, 2, 3}
B b1; // default-initialization: b1 is {100, -100, ???}
B b2 = {}; // copy-list-initialization invoking B::B(): b2 is {100, -100, ???}
B b3 = { 1 }; // copy-list-initialization invoking B::B(int): b3 is {100, -100, ???}
B b4 = { 1,2,3 }; // error: no B constructor taking (int,int,int)
}
Note also that aggregate initialization rules predate C++11. See for example this related pre-C++11 question: What does {0} mean when initializing an object?
Short answer: because you have in-class initializers.
Longer answer: because you are compiling for the C++14 or higher, have in-class initializers and you are using aggregate initialization. The reference provides the explanation:
If the number of initializer clauses is less than the number of
members or initializer list is completely
empty, the remaining members are initialized
by their default member initializers, if provided in the class
definition, and otherwise (since C++14) by empty lists, in accordance
with the usual list-initialization rules (which performs
value-initialization for non-class types and non-aggregate classes
with default constructors, and aggregate initialization for
aggregates).
To zero-initialize all data members, provide only data members declarations, without the in-class initializers and then use the = {0} or ={} syntax:
struct data_t
{
int num1;
int num2;
int num3;
int num4;
};
int main()
{
data_t d3 = { 0 };
printf("d3.num1 = %i\nd3.num2 = %i\nd3.num3 = %i\nd3.num4 = %i\n\n",
d3.num1, d3.num2, d3.num3, d3.num4);
}
Now, all your data members are initialized to 0.
By the rules of value initialization. Value initialization occurs:
1,5) when a nameless temporary object is created with the initializer
consisting of an empty pair of parentheses or braces (since C++11);
2,6) when an object with dynamic storage duration is created by a
new-expression with the initializer consisting of an empty pair of
parentheses or braces (since C++11);
3,7) when a non-static data
member or a base class is initialized using a member initializer with
an empty pair of parentheses or braces (since C++11);
4) when a named
variable (automatic, static, or thread-local) is declared with the
initializer consisting of a pair of braces.
Trivial example
struct A{
int i;
string s;
A(){};
};
A a{}
cout << a.i << endl // default initialized value
without explicitly declared constructor and left with defaulted default ctor // compiler generated one we get.
struct A{
int i;
string s;
};
A a{};
cout << a.i << endl // zero-initialized value
However using antoher struct.
struct A{
int i;
string s;
};
struct B{
A a;
int c;
};
B a{};
cout << a.a.i << endl // default initialized , even tho we did not , int struct B , declared A a{}.
The value of a.i is zero-initialized, even without using {} / () construct, which goes against rules ( if i am not mistaken ).
Using same logic on struct B:
struct A{
int i;
string s;
};
struct B{
A a;
int c;
};
B b;
cout << b.c << endl; // default inicialized
We get behavior according to rules.
The last example:
struct A
{
int i;
A() { }
};
struct B { A a; };
std::cout << B().a.i << endl;
B().a.i is also zero-initialized while we explicitly declared constructor and its not deleted.
Why are the these values getting zero-initialized? By rules stated here they should be default-initialized not zero-initialized.
Thanks for answers.
It's the difference between A being an aggregate or not.
[dcl.init.aggr] (Emphasis mine)
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal initializers
for non-static data members (9.2), no private or protected non-static data members (Clause 11),
So when A has no declared constructors, saying A a{} has the effect of aggregate initialization
which will construct each member with an empty initialization list:
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member
not explicitly initialized shall be initialized from an empty initializer list
So you get int{} and std::string{} which will value-initialize the integer member to zero.
When you do provide a default constructor, the aggregate property is lost and the int member remains uninitialized, so accessing it is considered undefined behavior.
To be specific:
This code is undefined behavior when accessing a.i because you provided a user-defined constructor, so the int i field remains uninitialized after construction:
struct A{
int i;
string s;
A(){};
};
A a{} ;
cout << a.i << endl;
And this code exhibits undefined behavior when accessing b.c because you did not perform list initialization on B b:
struct B{
A a;
int c;
};
B b;
cout << b.c << endl;
All the other code is okay, and will zero-initialize the integer fields. In the scenarios where you are using braces {}, you are performing aggregate-initialization.
The last example is a little tricky because you're performing value-initialization. Since B is an aggregate, it gets zero-initialized ([dcl.init]), in which:
each base-class
subobject is zero-initialized
So again you're okay accessing the integer member of the A subobject.
According to the rules of aggregate initialization, the members are indeed value initialized.
Value initialization:
In all cases, if the empty pair of braces {} is used and T is an aggregate type, aggregate-initialization is performed instead of value-initialization.
The effects of aggregate initialization are:
If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.
In C++ if I initialize the structure in the form of "= {}", as in example below, does it ensure to assign values zero to all the member of the structure?
I understand this seem duplicate question, But my question also is if it initializes zero to all members, does it also apply for complex structure ?
Like structure within structure , or for this each member has to be explicitly assigned value zero in the code?.
typedef struct s{
int i;
bool x;
};
int main ()
{
s initial = {};
printf("%d %d", initial.i, initial.x);
}
Edit: To reference complex structure,
typedef struct scomplex{
s initial;
s t[5];
};
int main (void)
{
scomplex sc = {};
printf ("%d %d %d",sc.initial.i, sc.initial.x, sc.t[0].i);
}
But my question also is if it initializes zero to all members, does it also apply for complex structure ?
Yes, all members will be initialized, including "complex" member, but might not be initialized to zero, the final effect is determined by their types.
According to your sample code, struct s is an aggregate type, then aggregate initialization is performed.
(emphasis mine)
If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates).
For this case the member i and x of struct s will be value initialized to zero.
4) otherwise, the object is zero-initialized.
If struct s has any other members, they'll be initialized (value initialized or aggregate initialized according to their types) by empty lists recursively.
EDIT
For your added sample (struct scomplex), the member initial will be value initialized, and the final effect depends on the type s. And another member is an array, which will be aggregate initialized with empty list, and all the elements of the array will be value initialized; Same as member initial, the effect depends on the type s.
Problem
Will this initialize all of the members to 0?
typedef struct s{
int i;
bool x;
};
int main ()
{
s initial = {};
printf("%d %d", initial.i, initial.x);
}
Answer: yes. Proof? Here you can see it become 0.
Better Alternatives?
This is an opinionated section. But In My Opinion (IMO), initializing it with {0} would be more readable than {}, as it notifies the user of the 0. It is actually being filled up with 0's.
s initial = {0};
What is this called?
This is called Aggregate Initialization, as Dieter Lücking defined, or Value Initialization, as songyuanyao noted. It's basically a form of initialization where you can initialize a struct with values you would like. For example, let's initialize it with the value 1 instead of 0! You would do:
// Example program
#include <stdio.h>
#include <iostream>
typedef struct s{
int i;
bool x;
};
int main ()
{
s initial = {1,1};
printf("%d %d", initial.i, initial.x);
}
You can see this compiled here. As you can see above, I am doing 1,1 which is normal initialization. As opposed to 0 initialization, you can't just initialize all the parts of the struct as easily as you can with 0.
References
cpprefrence
what is aggregate initialization
What do the following phrases mean in C++: zero-, default- and value-initialization?
Glossary
Aggregate Initialization :
Aggregate initialization is a form of list-initialization, which initializes aggregates.
Value Initialization:
Initialize values
This is the initialization performed when a variable is constructed with an empty initializer.
I am using a C-style structure which does not have any constructor, like this:
struct structName {
int mem1;
int mem2;
char mem3;
char mem4;
}
I am creating a variable of this structure and I want to initialize all members of the structure to zero. I found the following methods.
struct structName structVar = {};
struct structName structVar = {0};
struct structName structVar = struct structName();
For the first two methods, my compiler is giving "missing initializer for member" warning.
The third approach compiles without warnings.
Is it a valid C++ statement?
Am I missing some genuine warning/error by using method 3?
Is there any better alternative method, apart from memset()?
The preferred method should be one of:
structName structVar{};
structName structVar = {};
auto structName = structVar{};
there are subtle differences, but not for aggregates as in your example
This has the added advantage that it initializes structVar for any type of structName or if it cannot perform an initialization it makes the program ill-formed (the code doesn't compile) (plus it doesn't allow narrowing).
In your specific example, structName is an agregate:
C++14 draft standard:
§8.5.1 Aggregates [dcl.init.aggr]
(1) An aggregate is an array or a class (Clause 9) with no user-provided
constructors (12.1), no private or protected non-static data members
(Clause 11), no base classes (Clause 10), and no virtual functions
(10.3)
The initialization syntax I used is called List-initialization:
§8.5.4 List-initialization [dcl.init.list]
(1) List-initialization is
initialization of an object or reference from a braced-init-list.
[...] An initializer list may be empty. [...]
For our aggregate this means:
§8.5.1 Aggregates [dcl.init.aggr]
(2) When an aggregate is initialized by an initializer list, as
specified in 8.5.4, the elements of the initializer list are taken as
initializers for the members of the aggregate, in increasing subscript
or member order. Each member is copy-initialized from the
corresponding initializer-clause
(7) If there are fewer initializer-clauses in the list than there are
members in the aggregate, then each member not explicitly initialized
shall be initialized from its brace-or-equal-initializer or, if there
is no brace-or-equalinitializer, from an empty initializer list
(8.5.4).
[ Example:
struct S { int a; const char* b; int c; int d = b[a]; };
S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", ss.c with the value of an
expression of the form int{} (that is, 0), and ss.d with the value
of ss.b[ss.a] (that is, ’s’)
[...]
end example ]
So all of these are valid and do the exact same thing:
structName structVar = {};
structName structVar = {0};
structName structVar = {0, 0};
However if there is at least one initializer-clauses and less than there are members in the aggregate, gcc and clang emit a warning. It might be that you intended to initialize all members, but missed some. So the empty initializer list is the safest choice.
As a side note struct is not needed and universally not used in a declaration. So replace this:
struct structName structVar
with:
structName structVar
Is there a difference between initializing a structure these two ways:
structVar = {}
and
structVar = {0}
It really depends on the nature of the struct.
If it is an aggregate, the two are largely equivalent. However, the first one is general in that it simply value-initializes the struct. The second one requires that the struct's first member be initializable with 0, and initializes the rest of elements as if they were each initialized by an empty initializer {}, in other words,
structVar = {0, {}, {}, {}, ..... };
so each remaining element is value-initialized.
If it is not an aggregate, then it depends on what constructors, if any, have been provided. There simply isn't enough information to say.
corrected
If the structure is an aggregate, the 2 definitions perform the same thing. From the C++11 standard (N3337):
8.5.1/7 If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from its brace-or-equal-initializer or, if there is no brace-or-equal- initializer, from an empty initializer list (8.5.4).
If the structure contains two members, like
struct S
{
int x;
double y;
};
then structVar = {} will value-initialize the members, i.e. they will become zero (or if the members are non-PODs, the default ctor will be called).
The line structVar = {0} will initialize only the first member of the struct with zero. Since in this case you have an aggregate, the rest of the members are value-initialized by empty lists, so effectively you have
structVar = {0, {} };
C++14 added the possibility of aggregate initialization for aggregates containing brace-or-equal initialized members, i.e.
struct S
{
int x;
double y{1.1}; // or double y = 1.1;
}
In this case,
structVar = {0};
initializes x with 0 and y with 1.1. This was not possible before C++14.
PS: just tested the code above, clang++ compiles it with -std=c++1y, however g++ 4.9.2 rejects it even if I compile with -std=c++14.