3
votes

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?
2
{0} creates a temporary sockaddr_in with that initializer and then = copies the entire temporary over.T.C.
If there are less initializers than members to initialize, the remaining members are initialized as if they were static (i. e. with 0).The Paramagnetic Croissant
@TheParamagneticCroissant, that's the case for C, but for C++ not all types can be initialised with 0. They are value-initialized, which might set them to zero, but a default constructor can do something different.Jonathan Wakely
@T.C. there is no temporary created for aggregate initialization, or for C++11 list initialization, even though it is syntactically copy-initialization. See [dcl.init] p17, bullet 17.6.2. Instead the members of my_addr are copy-initialized straight from the list elementsJonathan Wakely
@T.C. ah, I thought you were referring to the struct sockaddr_in my_addr = { 0 }; snippet earlier in the question. I should have noticed that you said {0} not { 0 } :-)Jonathan Wakely

2 Answers

2
votes

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).

1
votes

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