2

I noticed that when I initialize array like so:

std::array<int,3> myarray;

myarray[0] = 9;
myarray[1] = 8;
myarray[2] = 7;
myarray[3] = 6;
myarray[4] = 5;

I do not get an error, even though I have more array elements than was defined in <int,3>

I am on Mac with g++ and it's C++11.

So I guess my question is: why is that, that there are no errors? Is it correct behavior? Or do I expect too much form my compiler?

Also what is this method of initialization called? Someone called it "buggy construction". I think they meant it as a joke though.

P.S. I understand that this method is undesirable.

EDIT: Some folks suggested that my question is a duplicate. But according to the best answer from Vlad it does not seem to be. According to Vlad my question is about operator overloading and not C style array range. I have to trust his expertise and what he said just makes sense.

Community
  • 1
  • 1
user
  • 2,939
  • 5
  • 26
  • 39
  • 4
    C++ doesn't hold your hand. If you mess up, you just get [*Undefined Behaviour*](https://en.wikipedia.org/wiki/Undefined_behavior). You may get a warning from your compiler with the correct option. – BoBTFish Feb 02 '16 at 14:16
  • Possible duplicate of [Access array beyond the limit in C and C++](http://stackoverflow.com/questions/18727022/access-array-beyond-the-limit-in-c-and-c) – BlackDwarf Feb 02 '16 at 14:17
  • @BlackDwarf do you think that this is still a duplicate even though I asked about std::array and the duplicate is about C style array? – user Feb 02 '16 at 14:27
  • It is the exact same concept at the very least. – Baum mit Augen Feb 02 '16 at 14:28
  • Other answers have explained why the compiler is allowed to compile this - but I would look at turning up the warning level to see if you can get it to issue a warning. Literal indexes into a std::array or C style array *ought* (in my opinion) get a warning. – Martin Bonner supports Monica Feb 02 '16 at 14:35
  • 1
    Fun fact: You can make some compilers catch this at runtime, e.g. by compiling with `-D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC` for gcc. One of the big advantages of `std::array` over C arrays. – Baum mit Augen Feb 02 '16 at 14:39
  • 1
    Most of that isn't called "initialization" at all. Only the first line is an initialization. The following three are called "assignment", and the last two "undefined behaviour". – molbdnilo Feb 02 '16 at 14:48
  • @molbdnilo I see. Thanks. – user Feb 02 '16 at 14:49

4 Answers4

3

It is undefined behavior. The compiler does not have to warn you about undefined behavior.

If you really want to know when you have gone out of the bounds of the array then use at() which will throw an exception.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
2

In general the compiler does not check indices used in the subscript operator for arrays.

As for the class std::array then the subscript operator is overloaded and looks like for example

T & operator []( size_t i );

So this record

myarray[4] = 5;

is equivalent to

myarray.operator []( 4 ) = 5;

that syntactically is correct. So the compiler does not have a reason for issueing an error.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • your response seems to be the most explanatory. – user Feb 02 '16 at 14:26
  • do you know if this [question](http://stackoverflow.com/questions/18727022/access-array-beyond-the-limit-in-c-and-c) is a duplicate of mine? Based on your response it does not seem to be. But I do not know enough about C++ to say one way or the other. – user Feb 02 '16 at 15:03
  • @user000001 It is not exactly a duplicate because you asked about the overloaded operator of a user-defined class. – Vlad from Moscow Feb 02 '16 at 15:06
2

... why is that, that there are no errors? Is it correct behaviour?

Well, the code invokes undefined behaviour. As you have observed it, it could do nothing apparent, or it could also throw a runtime error.

In these situation there are generally three types of errors you can run into;

  • Compile time array initialisation errors
  • Run time out of bounds error
  • Undefined behaviour

Compile time array initialisation errors

When initialising an array with more elements that the size of the array, the compiler can and does issue an error. It has sufficient information to diagnose these and as such, it does.

std::array<int,3> my_array = {1, 2, 3, 4, 5}; // compiler error

Run time out of bounds error

When indexing into the std::array (and other standard library containers), there are generally two forms; using the index operator [] or using a member method at(). The index operator is generally implemented for performance and hence won't do any bounds checking, the at() on the other hand does do bounds checking and will throw an exception if the indexed element is larger that the array size.

The compiler generally does not have sufficient information to diagnose these conditions as errors, hence it won't - but you might get a warning. Other tools make exist to catch these errors during the build (as could be possible in the sample given).

Undefined behaviour

This is basically the alternate situation to the bounds check. If you index an element out of the bounds of the error, the behaviour is undefined. It may result in a segmentation fault (or access violation), or it may not throw any runtime error or exception.

Niall
  • 30,036
  • 10
  • 99
  • 142
  • so do undefined behaviors ever get addressed in future releases of a language or do they stay undefined forever? – user Feb 02 '16 at 14:48
  • No hard rule there, yes/no/depends. Essentially the "undefined" is because it would be very difficult or near impossible to detect these errors using static analysis. As time goes by, the tech gets better and more warnings are issued by the compilers and associated tools (clang/llvm as good examples of this), but essentially the hard nature of the problem remains - so the undefined behaviour remains. – Niall Feb 02 '16 at 14:51
  • Many things are undefined behaviour because they don't make sense (e.g. signed integer overflow, or bit-shifting by more bits than there are in the type) not because it's hard to detect them. Leaving such nonsense as undefined allows the compiler to optimise sensible code better. – Jonathan Wakely Feb 02 '16 at 15:27
0

If you implement it using Microsoft's GSL span it will fail fast as promised by Neil Macintosh. So maybe not desirable in either case but with this approach the program terminates and indicates an issue worth investigating.

array<int, 3> a;
a[0] = 0;
a[1] = 1;
a[2] = 2;
a[3] = 3;
a[4] = 4;

for (auto& i : a) {
    cout << i << endl;
}

gsl::span<int, 3> s = a;
s[0] = 10;
s[1] = 11;
s[2] = 12;
//    s[3] = 13;

for (auto& i : s) {
    cout << i << endl;
}

outputs

0
1
2
10
11
12

Uncommenting s[3] = 13 compiles but fails.

0
1
2
libc++abi.dylib: terminating
zsh: abort      ./gsl_span
kometen
  • 6,536
  • 6
  • 41
  • 51