2

I was learning about arrays and how when you try to write to an out of bounds it can be detrimental to your program. Upon discovering this, I tried to read out of bounds.

I played around with a char array and a char variable.

   //this occurs within int main()

   char vowels[]{'a', 'e', 'i', 'o', 'u'};
   char x = 'x';

   std::cout << vowels[5]; //this outputs x

this made me wonder if all variables are stored contiguously in the order in which they are declared.

Jason
  • 3,777
  • 14
  • 27
LoserIRL
  • 29
  • 5
  • C dupe: https://stackoverflow.com/questions/1677415/does-stack-grow-upward-or-downward trying to find the C++ version – NathanOliver Oct 30 '19 at 20:01
  • No, not all. Static variables can be created out of order for instance. What you are doing is UB: you have no guarantees at all... anything can happen. – JHBonarius Oct 30 '19 at 20:02
  • 3
    I believe this is an implementation detail, and not mandated by the standard. Using (or abusing) the implementation detail is not supported by C++, and is squarely in the realm of undefined behavior. And that's where bad things happen. – Eljay Oct 30 '19 at 20:03
  • That could happen, but AFAIK, it's not guaranteed. –  Oct 30 '19 at 20:05
  • https://en.cppreference.com/w/cpp/language/ub - Your program contains Undefined Behaviour and thus has *no meaning*. *Anything* can happen (and is a valid interpretation). – Jesper Juhl Oct 30 '19 at 20:05
  • This more or less covers it: https://stackoverflow.com/questions/230584/where-are-variables-in-c-stored – NathanOliver Oct 30 '19 at 20:05
  • Just don't write code like that.. – Jesper Juhl Oct 30 '19 at 20:12

3 Answers3

2

From the standard (7.6.9.4) :

The result of comparing unequal pointers to objects is defined in terms of a partial order consistent with the following rules:

  • If two pointers point to different elements of the same array, or to subobjects thereof, the pointer to the element with the higher subscript is required to compare greater.

  • If two pointers point to different non-static data members of the same object, or to subobjects of such members, recursively, the pointer to the later declared member is required to compare greater provided the two members have the same access control ([class.access]), neither member is a subobject of zero size, and their class is not a union.

  • Otherwise, neither pointer is required to compare greater than the other.

Since the first two clause don't apply for comparing the address of x with the address of vowels or it's elements, their address are not meaningfully comparable.

But in this scenario specifically the pointer vowels + 5 (the address that vowels[5] tries to dereference) has a special meaning. Any pointer to the hypothetical element at index n of an array with n elements is known as a "one past the end pointer". It represents the first byte after the end of the array. It's permitted to calculate this address but not dereference it. The standard has a note that explicitly indicates that you can't use vowels[5] at all (6.8.2.3) :

[...] A pointer past the end of an object ([expr.add]) is not considered to point to an unrelated object of the object's type that might be located at that address. [...]

So even if x is exactly at vowels + 5 you still aren't allowed to access it via vowels[5].

Community
  • 1
  • 1
François Andrieux
  • 28,148
  • 6
  • 56
  • 87
1

this made me wonder if all variables are stored contiguously in the order in which they are declared

No, the only thing that C++ guarantees in this case, that when your read value by using variable name you will get it. The order they stored in memory or even if they would be stored in memory at all is not guaranteed and the worst thing for you by doing this - accessing out of bounds is actually invalidates your whole program and from the language point of view you cannot expect anything anymore from your program.

Slava
  • 43,454
  • 1
  • 47
  • 90
0

this made me wonder if all variables are stored contiguously in the order in which they are declared.

No, or at least the language doesn't specify it (see François Andrieux's answer). The layout of variables in memory (specifically non-member variables on the stack) is up to the discretion of the compiler. Since the compiler has to handle architecture requirements like alignment it can re-arrange variables and leave gaps between them. This can be important for optimization.

Here's an example (x86-64) where the compiler (clang-9) does not layout variables in declaration order...

#include <iostream>

int main() {

  char a[] {'a', 'b'};
  int x;

  std::cout << "&a[0] = " << (void*) &a[0] << std::endl;
  std::cout << "&a[1] = " << (void*) &a[1] << std::endl;
  std::cout << "&x = " << &x << std::endl;

  return 0;
}
&a[0] = 0x7ffd5becf02a
&a[1] = 0x7ffd5becf02b
&x = 0x7ffd5becf024
Jason
  • 3,777
  • 14
  • 27