Question

When we initialize an array like this int a[5] = {0}, the compiler makes all 5 elements 0. That is really good, compact-initialization and useful feature.

But I wonder why the compiler doesn't initialize int a[5]={1} similarly? Why does it not make all 5 elements 1? Why the Standard doesn't mandate it? Would it not been an awesome feature? Isn't it missing?

Also, if the number of elements in the initializer is less than the size of the array, then the compile could initialize the remaining elements with the last element in the initializer. Means, int a[5]={1,2,3} is equivalent to int a[5]={1,2,3,3,3}. And similarly, int a[10]={1,2,3,0} is equivalent to int a[10]={1,2,3,0,0,0,0,0,0,0};.

Would it all not be an awesome feature if the Standard mandates it? Or is there any good reasons for this missing feature?


And there is something called designated initializer in C99, which is used like:

Designated initializers can be combined with regular initializers, as in the following example:

int a[10] = {2, 4, [8]=9, 10}

In this example, a[0] is initialized to 2, a1 is initialized to 4, a[2] to a[7] are initialized to 0, and a[9] is initialized to 10.

Quite interesting. But even this feature is not in C++.

Was it helpful?

Solution

I personally find more "logical" (i.e. simple) having a fixed default initializer instead of another rule of repeating last one just for arrays. That may appear "practical" (i.e. useful) but it's IMO more logically complex.

That said however I think you're making a big mistake in trying to apply logic to a language like C++.

C++ is a complex language whose rules are the result of a long evolution history, and its current form is the result of the work of many people and even of formal committees (the last part alone could explain anything).

A language like C++ cannot be inferred by logic, it must be studied like history. Unless you're Hari Seldon there's really no way you can infer history using logical reasoning.

There are places of C++ where you're going to suffer a lot if you try to use logic instead of studying. Just to name a few...

  • Why is default dispatch static (i.e. wrong)?
  • Why there's no keyword for the null pointer?
  • Why the difference of two unsigned is unsigned?
  • Why a sum between a signed and an unsigned is unsigned?
  • If unsigned means "element of Z_{2^n}" then why sizes are unsigned?
  • Why std::string s; s=3.141592654; is perfectly valid C++?
  • Why in C++0X i = i++ + 1; is undefined behavior and i = ++i + 1; is valid?
  • Why double x=3.14; int y(int(x)); doesn't mean y will be 3?

OTHER TIPS

Why does it not make all 5 elements 1?

Because you're misunderstanding what {} means. (Actually, in C++ the better way to do this is {} rather than {0}). The syntax {0} does not mean that you want all elements in the aggregate set to zero. Rather, it says that you want an aggregate with the first element zero assigned to the indicated variable (which can be either an array or a class type in C++). Because the aggregate usually has more fields than that one value zero, the remaining elements in the aggregate are default constructed. The default value of a builtin or POD type is to set all of the fields to zero, so you've effectively set the entire aggregate to zero.

As for why specifically, consider the following. According to the current standard, none of the assertions below will fail:

struct abc
{
    char field1;
    int field2;
    char field3;
};

int main()
{
    abc example = {'a', static_cast<int>('b')};
    //All three asserts pass
    assert(example.field1 == 'a');
    assert(example.field2 == static_cast<int>('b'));
    assert(example.field3 == '\0');

    int example2[3] = {static_cast<int>('a'), 42};
    assert(example2[0] == static_cast<int>('a'));
    assert(example2[1] == 42);
    assert(example2[2] == 0);
}

What would you expect the value of field3 to be in your proposed standard change? Even if you define it as the last element in the aggregate initializer as you've shown above, that's going to break compatibility with existing code which assumes the rest of the elements are default constructed.


EDIT: Just realized that your question is asked in terms of arrays - but the answer is the same with either structures or arrays, so it really doesn't matter.

EDIT2: To make this more in keeping with the standard, references to class/structure have been replaced with "aggregate" below, which covers the structures and arrays cases.

Yes, they could have done that, but they didn't, and it's far too late now to change such behavior. The decisions behind C and C++ were made with thought given to performance and minimalism at almost every step, so I imagine that, if nothing else, comes into play here as well.

Such a feature just doesn't strike me as all that awesome. It's a very simple piece of syntactic sugar, and I rarely find the need to initialize an array like that to anything other than 0 anyway.

Typical runtime libraries provide a feature that makes it easy to initialise data to 0. In general terms, this is stored in a specific section in the executable, organised by the compiler and linker. At program startup, the runtime startup code uses something like memset() to clear out all the initialised data to 0. This means that the zero bytes don't have to be stored inside the executable itself.

The converse is that if you initialise data to something other than zero, then the bytes for that data must be stored in the executable itself, since the automatic initialiser only initialises to zero.

Therefore, if you were to declare a big array of char (say a megabyte?) and initialise it with, say, {0}, then there would not be bytes stored in the executable for that array. On the other hand, if you were to initialise it with {1} under your scheme, a megabyte of 1 bytes would have to be stored in the executable itself. By changing one character in the initialiser list, the size of the executable increases by a megabyte.

I believe such a scheme would violate the principle of least surprise.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top