What Are Primitive Types Default-Initialized to in C++

What are primitive types default-initialized to in C++?

You are not correct. The object is not default-initialized but value-initialized. And its value is well-defined

int = 0, 
bool = false,
float = 0.0f,
enum = (enum type)0,
pointer = null pointer
pointer to member = null member pointer

Note that zero is in the range of values for any enumeration, even if it doesn't contain an explicit enumerator with that vaue, so it's safe to initialize an enumeration variable to that value.

In particular for pointer to data members, the representation used in practice is not all-zero bits. In the so-called C++ Itanium ABI used by at least GCC and Clang, pointer to data members have an all-one bits null representation.

What are primitive types default-initialized to in C?

Only objects with static storage duration are initialized to 0 if there is no explicit initializer.

#include <stdio.h>

float f; // initialized to 0, file scope variables have static storage
static float g; // initialized to 0

int main(void)
{
float h; // not initialized to 0, automatic storage duration
static float i; // initialized to 0

return 0;
}

Objects with automatic storage duration (like h in the example above) that are not explicitly initialized have an indeterminate value. Reading their value is undefined behavior.

EDIT: for the sake of completeness, since C11 objects with thread storage duration are also initialized to 0 if there is no explicit initializer.

Why C++ primitive types are not initialized like the rest of types?

No. It is not inconsistency.

What if your class is defined as:

struct MyClass
{
int x;
float y;
char *z;
};

then this line does NOT do that you think it does:

MyClass myInstance; 

Assuming the above is declared inside a function, it is same as:

int x; //assuming declared inside a function

In C++, the types are broadly divided into 3 kinds viz. POD, non-POD, Aggregates — and there is a clear distinction between them. Please read about them and their initialization rules (there are too many topics on them. Search on this site). Also read about static initialization and dynamic initialization.

Are members of a built-in type ever default-initialised?

First let's go over some examples and the correct terminology.

T a1;            // default initialization
T a2{}; // value initialization
T(); // also value initialization
new T; // default initialization
new T(); // value initialization
new T{}; // also value initialization
class C1 {
C1() {}
T x;
}; // no initializer for C1::x; default-initialized
class C2 {
T x;
}; // implicit default constructor default-initializes C2::x
class C3 {
C3() : x() {}
T x;
}; // C3::x will be value-initialized.
class C4 {
C4() : x{} {}
T x;
}; // C4::x will also be value-initialized.
// DANGER
T a(); // declares a function; not value initialization (quirk of C++)

Generally, the rule is that when there is no initializer, it is default initialization, and when the initializer is () or {}, it is value initialization. Note that there is an exception for statics and thread-locals, which I'll discuss later.

For an integer or floating-point type, value initialization sets it to 0. For a pointer type, value initialization sets it to null. Default initialization does nothing for scalar types. Therefore, if an object of scalar type only receives default initialization, then it has indeterminate value.

a) Does initialisation of members of a built-in type have anything to do with wheher a user-defined constructor is defined or not,

The default constructor for a class default-initializes members. A member is also default-initialized when no mem-initializer is explicitly provided for it. The examples C1 and C2 illustrate this. However, note that when a class type is value-initialized, and the class's default constructor is either implicitly defined or explicitly defaulted, the class's members will be zeroed out. This zeroing out occurs only in this case, and doesn't occur for a user-provided default constructor. So the answer to your question is "yes" in this sense.

C1 y1;   // y1 is default-initialized; y1.x is indeterminate
C1 y2{}; // y2 is value-initialized; y2.x is indeterminate
C2 y3; // y3 is default-initialized; y3.x is indeterminate
C2 y4{}; // y4 is value-initialized; y4.x is set to 0
C3 y5; // y5 is default-initialized; y5.x is set to 0
C3 y6{}; // y6 is value-initialized; y6.x is set to 0

b) do members of a built-in type always need to be initialised manually, and c) are there any circumstances under which an object's storage is zeroed-out before the constructor is called?

I assume you mean "class members with built-in type". I covered a case above in which they are automatically initialized to 0: where the class object is value-initialized and its constructor is not user-provided (or deleted). Another case is when the class object has static or thread-local storage duration. In this case, the members will also be zeroed out at the very beginning, so there is no chance of them ending up with indeterminate value.

C++: Are local automatic variables initialized or not? Stroustrup example

It's easy. You can leave the built-in type variables uninitialized or you can ask the compiler to zero-initialize them.

S s1;   // string is default initialized, int is left uninitialized
S s2{}; // All fields are initialized.

Notice the {}. That (in this case) asks the compiler to initialize all fields.

Default initialization of class data members in C++11

when the ctor A::A() is called, how the data member A::i initialized

If no initializer is provided, the rules of default initialization apply. Your constructor does no initialization of A::i so it's left uninitialized; it's value is indeterminate. No doubt about that. Excerpt from the documentation on default initialization:

If T is a class type, the constructors are considered and subjected to overload resolution against the empty argument list. The constructor selected (which is one of the default constructors) is called to provide the initial value for the new object.


why only a1.i and aa1[0~2].i are initialized to 0, while others are uninitialized?

The global data memory is initialized to zero i.e. the whole section is zeroed out and so you see global A::is initialized to 0. Note that the constructor would not be doing this. Excerpt from the documentation:

Static initialization

[...]

2) For all other non-local static and thread-local variables, Zero initialization takes place. In practice, variables that are going to be zero-initialized are placed in the .bss segment of the program image, which occupies no space on disk, and is zeroed out by the OS when loading the program.

However, for the vector, the vector itself is in the non-local static memory while its elements are allocated in free store (heap) and hence their members are uninitialized too.

Initialize all the variables of a specific type to a specific default value in C++

In C++11, you could write T var{}; to get value initialization to the default value.

In C++03, you could write a non-POD wrapper, whose default constructor will get called by T var;:

template<class T>
struct default_initializer{
default_initializer() : value() {}
default_initializer(T const& v) : value(v) {}
T value;
};

// somewhere in code
default_initializer<T> var; // calls default ctor and initializes 'value'
// to its default value

This will allow you to safely default initialize even primitive , POD and aggregate types, which are normally left uninitialized by the T var; declaration.

Default initialization of POD types in C++

Local variables with automatic storage duration are not being initialized automatically. Since using uninitialized variables produces undefined behavior, it is a good practice to explicitly initialize your variables even when it's redundant.

About POD types that are being zero-initialized, C++03 standard 3.6.2 Initialization of non-local objects states:

§1 Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place. Zero-initialization and initialization with a constant expression are collectively called static initialization; all other initialization is dynamic initialization. Objects of POD types (3.9) with static storage duration initialized with constant expressions (5.19) shall be initialized before any dynamic initialization takes place.

So it's guaranteed by standard that POD types with static storage duration (whatever their scope is) will be zero-initialized.

POD members of a class (without explicit initialization in a constructor)

This situation is described in 12.6.2 Initializing bases and members, that states (selected parts):

If a given nonstatic data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then:

— If the entity is a nonstatic data member..., and the entity class is a non-POD class, the entity is default-initialized (8.5)...

Otherwise, the entity is not initialized...

After the call to a constructor for class X has completed, if a member of X is neither specified in the constructor’s mem-initializers, nor default-initialized, nor value-initialized, nor given a value during execution of the body of the constructor, the member has indeterminate value.

Example:

class C
{
public:
C(int x, int z) : x(x), z(z) { }
int x, y, z;
};

int main(void)
{
C* c = new C(1,3);
std::cout << c->y; // value of y is undetermined !!!
}


Related Topics



Leave a reply



Submit