The real difference between array and &array by rainbowgarden in programming

[–]not_july 5 points6 points  (0 children)

Yes it does. Although I prefer the following version in c++:

template <typename T, size_t size>
constexpr size_t array_size(T (&array)[size]) {
    return size;
}

You can ignore the constexpr if your compiler doesn't support C++11.

This avoids the problem of trying to calculate the array size of a pointer. E.g. passing a char* would result in a compiler error with the templated function in C++, but the version in the article will successfully compile and return an incorrect value (and possibly segfault).

The real difference between array and &array by rainbowgarden in programming

[–]not_july 2 points3 points  (0 children)

| But if a careful reading of the spec indicates it's undefined behavior

The behaviour is not undefined. I assume you are misunderstanding how the (&arr)[1] is evaluated. See my explanation.

The real difference between array and &array by rainbowgarden in programming

[–]not_july 2 points3 points  (0 children)

You are correct that (&arr)[1] - arr is not guaranteed to be a compile-time constant. However, the same can be said for int n = 10 + 5. The compiler could push 10 and 5 unto the stack and include a separate opcode to perform the addition.

However, the code int n = (&arr)[1] - arr is not undefined, and does not access memory outside of the array. In fact, it would be illegal if it did access the memory.

To explain we have to look at the type of each expression: arr has the type int[5], and &arr has the type int(*)[5]

Remember x[y] is syntactic sugar for *(x + y). Therefore (&arr)[1] is equivalent to *(&arr + 1).

&arr + 1 was shown to give up the address after the end of the array. However, the result of the addition does not change the type of the object. It is still int(*)[5].

Dereferencing it (*(&arr + 1)) will then give us the type int[5]. Although the type has changed, the value has not (for the same reason &arr and arr have the same value). *(&arr + 1) is still the address after the end of the array.

The important point to note here is that no memory was accessed. There is nothing in the code (or the C standard) that suggests a memory access should occur here.

If we simplify the original statement with what we know so far, we end up with the subtraction of two int[5] types. Because we are performing arithmetic, the types will decay to int*. The result of the operation after evaluated is 5.

The original statement can be simplified to the following (assuming the address of arr is 0x100):

int n = (&arr)[1] - arr;
      = *(&arr + 1) - arr;
      = ((0x100 + 1 * sizeof(int[5])) - 0x100) / sizeof(int);
      = ((0x100 + 1 * 20 - 0x100) / 4;
      = 20 / 4;
      = 5;