Why Are My Struct's Members Not Properly Initialised Using '{}'

Why are my struct's members not properly initialised using `{}`?

When you write = {0}, that only explicitly initialises the first member; the rest are zero-initialised implicitly according to the standard, so it appears at first glance that you explicitly initialised all members with the 0 that you wrote, but you didn't.

That place where you wrote 0 only affects the first member. So when, one day, you changed it to 1 thinking that it'll change all members, you'll have a bug, like here. It's misleading/dangerous/silly/fragile code.

For that reason, without an accompanying explanatory comment, = {0} will not pass code review in my team. You should originally have written:

T t = {};

And now, to solve your problem according to the new requirements, you should write:

T t = {1,1,1};

or, if you don't mind your struct potentially losing POD-ness, give T a constructor.



Formal wording

[C++11: 8.5.1/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. If the initializer-clause is an expression and a narrowing conversion (8.5.4) is required to convert the expression, the program is ill-formed. [..]

[C++11: 8.5.1/6]: An initializer-list is ill-formed if the number of initializer-clauses exceeds the number of members or elements to initialize.

[C++11: 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 an empty initializer list (8.5.4).

Array of structs on heap not properly initialized


Why the weird numbers?

Because no memory was allocated to back them. The pointer is pointing at Crom knows what. That structure should not compile.

Won't the A objects in the array get properly constructed somehow if I don't define a constructor?

Without a constructor all of the members will be initialized to their defaults. int's and most Plain Old Datatypes have no defined default value. In a typical implementation they get whatever value happens to already be in their allocated memory block. If a member object is of a type that doesn't default constructor and is unable to make one, you get a compiler error.

And why do I need to specify the array size explicitly even though it will compile and link just fine without?

It shouldn't compile, mismatch between the size of the array (unspecified and an error unto itself) and the number of elements in the initializer list, so the compiler has a bug. Linker is not involved at this point.

Initializing arrays on the stack this way does not give me a runtime error (I tested it to be sure).

In the static version the compiler can count the number of elements in initialization list. Why the dynamic version with new can't, gotta say I have no good answer. You'd think it would be a simple bit of counting that initializer list, so there's something deeper preventing it. The folk who debated and then approved the standard either never considered allocating a dynamic array that way or couldn't find a good way to make it work in all cases. Same reason variable length arrays still aren't in the standard.

"And why do I need to specify the array size explicitly even though it will compile and link just fine without? It shouldn't compile, ...." To be clear: If I add the constructor to A and run it, it runs just fine up until the delete[] statement. Only then it crashes but cout << c[0] works as 'expected'

This is because you are unlucky. That constructor is writing into memory that your program owns, but didn't allocate to c. Printing those values works, but whatever was supposed to be in memory at that point has been overwritten. This will probably cause your program to crash sooner or later. This time it's later.

My suspicions, and this is guesswork based on specific because you've ventured far into the realms of the undefined, are the crash on delete[] is because

A* c = new A[]

Allocated A[1] and assigned it to c rather than failing to compile. c has one A to work with. The initializer list tries to stuff in 4 and writes 3 into c[0] and the 4,5, and 6 over the heap control information that delete needs to put the data back. All looks good until delete tries to use that overwritten information.

Oh and this:"Without a constructor all of the members will be initialized to their defaults. int's and most Plain Old Datatypes have no defined default value.". For structs a user defined ctor seems optional because you can initialize a struct by providing arguments corresponding to its data fields.

A struct has a much more permissive attitude toward data encapsulation than a class and defaults to public access where a class defaults to private. I've never tried it, but I'm betting that you can use the same struct trick to init all the public members of a class.

OK. Just tried it. Works in GCC 4.8.1. Not going to make that claim in general without looking it up in the standard. Got to get a copy of it.

Why can't I initialize my fields in my structs?

The reason is mainly performance. Consider the following,

var a = new MyStruct[1000];

If C# allowed initialization of fields in a struct then the initialization would have to be performed 1000 times, once for each element in the array. C# wanted to avoid such kinds of implicit behavior as might be found in other languages.

Compiler gives error when struct is not initialized and if we try to access the property but not with variable

There's a tremendous amount of confusion in this thread.

The principle is this: until all of the fields of an instance of a struct are definitely assigned, you can not invoke any properties or methods on the instance.

This is why your first block of code will not compile. You are accessing a property without definitely assigning all of the fields.

The second block of code compiles because it's okay to access a field without all of the fields being definitely assigned.

One way to definitely assign a struct is to say

EmpStruct empStruct = new EmpStruct();

This invokes the default parameterless constructor for EmpStruct which will definitely assign all of the fields.

The relevant section of the specification is §5.3 on Definite Assignment. And from the example in §11.3.8

No instance member function (including the set accessors for the properties X and Y) can be called until all fields of the struct being constructed have been definitely assigned.

It would be more helpful (ahem, Eric Lippert!) if the compiler error message were along the lines of

Use of not definitely assigned local variable empStruct.

Then it becomes clear what to search for the in the specification or on Google.

Now, note that you've defined a mutable struct. This is dangerous, and evil. You shouldn't do it. Instead, add a public constructor that lets you definitely assign firstNumber and secondNumber, and remove the public setter from EmpStruct.FirstNumber.

Properly initialise a struct in C++, specifically addrinfo

In C++ the initialization stacks. A class member and all base classes are initialized before the program enters the body of the constructor and will call the appropriate constructors to do so. Everything afterward, including inside the constructor's body, is assignment. To initialize a member you need to use either a default member initializer or a Member Initializer List in the class's constructor.

It's not important to this question, but explicitly initializing in the member initializer list is often superior to default initialization followed by assignment in the constructor body. If the default initialization is expensive, you won't want to perform it and then repeat a lot of the work later with the assignment.

addrinfo is a C structure, what we used to call a Plain Old Data type, and they are as simple as it gets. They require no specialized initialization and have no constructors and nothing they contain does either. So no initialization is performed. Their initial values are undefined and tend to reflect whatever happened to be in the memory location where the addrinfo now sits, basically garbage. As the asker found, this "garbage" is detrimental and needs to be cleared.

Simple code example:

class info_base
{
protected:
addrinfo info = addrinfo(); // default member initializer
// will fall back on zero initialization in this case
};
class info_tcp:public info_base
{
public:
info_tcp() // initialization includes default initialization of info_base
// which will use the default member initializer
{
info.ai_socktype = SOCK_STREAM;
info.ai_protocol = IPPROTO_TCP;
}
};

One of the advantages of being so simple is it can be aggregate initialized and as of C++20 we can do simple stuff like this (Note there is no noticeable performance advantage to this--far as I can see--but knowing you can do this could come in handy later)

class info_base
{
public: // note public now. Necessary to be an aggregate
addrinfo info = addrinfo();
};
class info_tcp:public info_base
{
public:
info_tcp(): info_base({.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP})
// Aggregate initialization of info_base via member
// Initializer List taking advantage of designated
// initializers added in C++20
{
}
};

Because info_base and its info member are being explicitly initialized, the default member initializer is skipped. I don't much like this one because it made info public and now any shmuck can mess with it, but it is really simple.

class info_base
{
protected:
addrinfo info;
info_base(int type,
int protocol): info {.ai_socktype = SOCK_STREAM,
.ai_protocol = IPPROTO_TCP}
{

}
};
class info_tcp:public info_base
{
public:
info_tcp(): info_base(SOCK_STREAM, IPPROTO_TCP)
{
}
};

Now info_base doesn't have a default constructor, you can add it back if you like, but the caller can specify the important parameters to use to initialize info and no one outside of the family can interact with addr.

C - Static array of structs with string member not properly initialised?

This expression is wrong:

lookuptable + i * sizeof(symstruct_t)

It's enough with

lookuptable + i

The compiler already knows the size of each member in the array, so it knows that adding i to the array means that it will use index i, i.e. it's the same thing as &lookuptable[i].

What value have int fields in struct not initialized yet?

In C there is no such thing as "partial initialization".
If a struct is initialized by specifying a value for a single member, all the other members are automagically initialized (to 0 of the proper kind).

struct book
{
int number;
char name[31];
char author[31];
int year;
char publisher[31];
};

struct book book1; // no initialization
struct book book2 = { .author = "pmg" }; // initialization of ALL of book2

struct book bkStruct1[MAXELEMENTS]; // uninitialized array
struct book bkStruct2[MAXELEMENTS] = {0}; // initialized array
// (every member of every element)

Note: some implementations may complain about missing braces on the perfectly legal array initialization. That is a problem with those implementations.

In struct nested union/array member default initialization compiles, but is not happening correctly?

What you are doing is aggregate initialization, which means the elements in sArray and dArray are value initialized when not specified. Because short and double are scalar types, this means zero initialization

Since you don't specify anything but the first element, all remaining elements will be initialized to 0

As requested in the comments, a way to initialize the arrays would be std::fill or std::fill_n:

#include <algorithm>

struct Nested {
explicit Nested() {
std::fill_n(sArray, 5, 0);
std::fill_n(dArray, 5, 0.0);
}
union {
short sArray[5];
float fVal; // Must NOT be initialized - obviously, only 1 member of a
// union can be
};

double dArray[5];
};

In general, I would recommend to instead use std::array and its fill function:

#include <array>

struct Nested {
explicit Nested() {
sArray.fill(0);
dArray.fill(0.0);
}
union {
std::array<short, 5> sArray;
float fVal; // Must NOT be initialized - obviously, only 1 member of a
// union can be
};

std::array<double, 5> dArray;
};


Related Topics



Leave a reply



Submit