1

Is the following code under every compliant compiler optimization asserted to return the value 14? Is this recommendable to use? Is this more efficient than introducing two pointers onto a common array? Has the below stack technique a specific name? Which better practice yields the exact same binary upon compilation under reasonable or admirable assumptions?

#include <iostream>

class A{
public:
    double x[10];
    double y[10];
};

int main(){
    A a;
    a.y[3] = 14;
    std::cout << a.x[13] << "\n";
}

Where can I look up determinations like these? (links, references). I guess there is a trick to reading the standard efficiently.

Benjamin Buch
  • 4,752
  • 7
  • 28
  • 51
fuse tee
  • 69
  • 5
  • Remark: I just crossed today that GCC g++ 11.3.0 on Ubuntu 20.04 with optimization -O3 -march=native -ffast-math on sandybridge will not initialize static int[10] to zeros. That's why I am pedantic. Further, my code eventually might have to run on embedded targets, where compilers can be dubious; but let's keep this as a separate consideration. – fuse tee Mar 29 '23 at 16:54
  • 1
    There is no such requirement. If you get the result you expect it's only by accident. – Pete Becker Mar 29 '23 at 16:57
  • It's **undefined behavior** under every compliant compiler. Also, `static int[10]` should not compile with GCC g++ 11.3.0. – Eljay Mar 29 '23 at 17:05
  • One question per question please. Though in this case there is only one answer - it does not work. – Slava Mar 29 '23 at 17:16
  • Possible dup https://stackoverflow.com/questions/1239938/accessing-an-array-out-of-bounds-gives-no-error-why – Slava Mar 29 '23 at 17:21
  • Remember that C++ need not use a stack and thus not care one bit about stack order. – user4581301 Mar 29 '23 at 17:23
  • @user4581301 in this case I think it's really just a figure of speech, the question is more about object layout than the stack per se. – Mark Ransom Mar 29 '23 at 18:30
  • The C++ language does not guarantee that members will be contiguously laid out in memory. The compilers are allowed to insert padding between members; but members must be laid out in order of declaration. If you need contiguous values, use an array. – Thomas Matthews Mar 29 '23 at 18:35
  • @ThomasMatthews I think problem is deeper here rather than padding etc - compiler is allowed to completely eliminate illegal code. – Slava Mar 29 '23 at 19:23

2 Answers2

4

A number of people have already pointed out in the comments that your code currently has undefined behavior.

As far as ordering goes, you are guaranteed that as you've defined things right now x and y are in order--that is, y will have a higher memory address than x. But there could be padding in between them, so that there's less overlap than you expect, or possibly no overlap at all. In addition, the standard is written to allow (but not require) that array indices can be checked for validity, so even if they're immediately next to each other, the compiler is allowed to check that since you defined x to have 10 elements, any attempt at indexing beyond the 10th element will fail (e.g., cause a hardware interrupt or fault).

That leaves the question about how to get the result you apparently want, but with defined behavior. You have a couple of choices. One is to allocate a single array, and add an alias that allows you to treat half of it as if it were a separate array:

#include<iostream>

class A{
public:
    double x[20];
    double *y = &x[10];
};

int main(){
    A a;
    a.y[3] = 14;
    std::cout << a.x[13] << "\n";
}

The other direction you could go would be to keep the two arrays separate, but then add a small proxy to allow addressing into both arrays as if they were one:

#include<iostream>

class proxy {
    double *x_;
    size_t N;
    double *y_;
public:
    template <class T, std::size_t N>
    proxy(T (&x_)[N], T *y) : x_(x_), N(N), y_(y) {}

    double &operator[](std::size_t n) {
        if (n < N)
            return x_[n];
        else
            return y_[n-N];
    }
};

class A{
    double x_[10];
public:
    double y[10];
    proxy x{x_, y};
};

int main(){
    A a;
    a.y[3] = 14;
    std::cout << a.x[13] << "\n";
}

Unless you really need to use separate arrays for the two items, the first is probably preferable – it's clearly simpler and probably faster.

Benjamin Buch
  • 4,752
  • 7
  • 28
  • 51
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Treating two arrays as one is dangerous, because it's too tempting to use them as if they were contiguous - trying to write them to a binary file for example. – Mark Ransom Mar 29 '23 at 18:28
3

This is undefined behavior. You can't guarantee that there won't be padding between the arrays.

Rob K
  • 8,757
  • 2
  • 32
  • 36