-1

I am a newbie who is learning C++ at random order.

In the following first three cases I can digest what is going on because the pattern of implicit decay is clear.

"an array of X" is implicitly decayed to "a pointer to X".

void case1()
{
    int a[] = { 1,2,3,4,5,6 };
    int *b = a;// array of int ---> pointer to int
}

void case2()
{
    int input[][3] = { {1,2,3},{4,5,6} };
    int(*output)[3] = input;// array of int[] ---> a pointer to int[]

    for (int i = 0; i < 2; i++)
        for (int j = 0; j < 3; j++)
            cout << output[i][j] << endl;
}

int case3()
{
    int input[][3] = { {1,2,3},{4,5,6} };

    int* aux[2];

    aux[0] = input[0];// array of int ---> pointer to int
    aux[1] = input[1];// array of int ---> pointer to int

    int** output = aux;// array of int* ---> pointer to int*

    for (int i = 0; i < 2; i++)
        for (int j = 0; j < 3; j++)
            cout << output[i][j] << endl;
}

Question

However, I am really confused with the fourth case as follows.

int case4()
{
    int input[][3] = { {1,2,3},{4,5,6} };

    int* aux[2];
    aux[0] = (int*)input;// array of int[] ---> pointer to int
    aux[1] = aux[0] + 3;

    int** output = aux;// array of int* ---> pointer to int*

    for (int i = 0; i < 2; i++)
        for (int j = 0; j < 3; j++)
            cout << output[i][j] << endl;
}

How can "an array of int[]" can be explicitly decayed to "a pointer to int"?

aux[0] = (int*)input;// array of int[] ---> pointer to int

Any easy explanations are welcome!

Second Person Shooter
  • 14,188
  • 21
  • 90
  • 165
  • `aux` is an array of pointers-to-int so `aux[0]` is an `int *`. – 001 Jan 10 '19 at 16:32
  • Possible duplicate of [C++: Pointers in 2D-arrays are confusing](https://stackoverflow.com/questions/53445395/c-pointers-in-2d-arrays-are-confusing) – Matthieu Brucher Jan 10 '19 at 16:33
  • @JohnnyMopp: Yes. Of course I know it. But how can `input` of type "array of `int[]`" can be decayed to `aux[0]` of type `int*`? – Second Person Shooter Jan 10 '19 at 16:34
  • 1
    `(int*)input` that's wrong. It doesn't work like that. See the duplicate. – Matthieu Brucher Jan 10 '19 at 16:35
  • 1
    Adding `(int*)` means you are no longer dealing with an implicit conversion. – François Andrieux Jan 10 '19 at 16:36
  • @FrançoisAndrieux: Implicit conversion is not possible. – Second Person Shooter Jan 10 '19 at 16:37
  • Exactly, so you are using a `reinterpret_cast` instead. Just read the duplicate. – Matthieu Brucher Jan 10 '19 at 16:37
  • 2
    [How are multi-dimensional arrays formatted in memory?](//stackoverflow.com/a/2565048) – 001 Jan 10 '19 at 16:38
  • 1
    @GodMustBeCrazy Right, so `input` gets implicitly converted to a `int(*)[3]` and then `(int*)` tells the compiler "This thing is an `int*`. Trust me, I know what I'm doing.". From that point on the reason it works is because of how arrays are laid out in memory as the previously linked question explains. Hard c style casts lets you cast any pointer to any other pointer regardless of the rules of the language but then the burden of making sure you don't screw up using that converted pointer falls to you. – François Andrieux Jan 10 '19 at 16:40
  • 1
    I even think that `aux[0] = (int*)input;` is pedantically UB. – Jarod42 Jan 10 '19 at 16:46
  • @Jarod42: what is "UB"? – Second Person Shooter Jan 10 '19 at 16:49
  • 1
    UB stands for **U**ndefined **B**ehavior. – Jarod42 Jan 10 '19 at 16:52
  • 1
    @GodMustBeCrazy [Undefined Behavior](https://en.cppreference.com/w/cpp/language/ub) is a dangerous but necessary concept that c and c++ have and that few other languages have. It describes situations where the language imposes no specific behavior, but in practice it is much more complex. Notably, compilers are allowed to assume that undefined behavior never occurs (because even if it does, whatever the compiler chooses to do is allowed) and can lead to surprising behaviors. In short, any code or input that leads to UB is wrong and code that appears to work may contain UB and still be wrong. – François Andrieux Jan 10 '19 at 16:55
  • 1
    See [Undefined, unspecified and implementation-defined behavior](https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior). – François Andrieux Jan 10 '19 at 16:58

2 Answers2

3

How can "an array of int[]" can be explicitly decayed to "a pointer to int"?

To be pedantic about terminology: "explicitly decaying" is an oxymoron. Decaying is by definition an implicit conversion.

To answer "how can [array] be explicitly be [converted] to [a pointer that isn't type of the first element]?":

It is because an array can decay to a pointer, and all data pointers can be explicitly converted (reinterpreted) to any other data pointer type. In this case, input decays to a int(*)[3] which is then explicitly converted to int*.

Although it is certainly well formed, another matter is whether indirecting through an explicitly reinterpreted pointer has defined behaviour. The rules around reinterpretation of pointers are complex and subtle - it's rarely safe to assume that it's guaranteed to behave the way you observe. I would be more confident writing instead:

aux[0] = *input;

Here, the input array decays to pointer to first subarray, this pointer is indirected to get lvalue, which then decays to pointer to the element of the subarray.


More generally, be very careful when using explicit conversion (T)expr or functional conversion T(expr) or reinterpret cast reinterpret_cast<T>(expr). Unless you can quote the standard rule which makes their use well defined, don't use them.

eerorika
  • 232,697
  • 12
  • 197
  • 326
1

Hmm, this sounds like it works but is it legal?.

What is known it that the first element of an array and the array itself share the same address in memory.

What works in common implementations is that all pointers share the same representation, and that this representation is just the memory address of their first byte. Simply this is not guaranteed by the standard.

But it is enough to successfully convert the address of an array to the address of its first element. The explicit cast re-interprets the representation of the address of the array as the address of the first element and because of the implementation it just happens to work.

That is what is great with Undefined Behaviour: it does not forbid the natural behaviour but does not guarantee it either.

So according to a string reading of the standard, and specifically of the strict aliasing rule converting a pointer to an array to a pointer to its first element, and using that pointer to dereference the element is UB, because an array and its element type are different types. Simply AFAIK it works on all common implementations.

TL/DR: it works on all common implementations, but do not use it if you want to write standard compliant programs.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252