1

Lets assume there's variable of type int** which is the pointer to a 5x5 2D array:

int** ptr_array_5by5;

and a function with the following prototype:

void print2DArray_with5Columns(int (*ptr_row)[5]);

Is there a way to cast ptr_array_5by5 into some type which would match the type of the first argument of the function?

print2DArray_with5Columns( (<some casting type>) ptr_array_5by5)
Miro
  • 1,778
  • 6
  • 24
  • 42
  • 4
    Yes, you can cast any pointer type to any other pointer type. Doing so is nearly always wrong. If you have to ask, it is *always* wrong. `int**` and `int (*)[5]` are different not because creators of C and C++ hate you personally, they are different because they really, truly, genuinely different. – n. m. could be an AI Jun 11 '14 at 14:41
  • @n.m. Ok. I was just wondering whether it's possible to cast. :) I didn't know how to write down the type of parameter. – Miro Jun 11 '14 at 14:49
  • if you redefine `ptr_array_5by5` as `int ptr_array_5by5[5][5];`, it will "just work" - is that an option ? If not, why not ? – Sander De Dycker Jun 11 '14 at 14:49
  • Five answers so far and a lot of discussion. Nice question! – TobiMcNamobi Jun 11 '14 at 15:36
  • [related FAQ](http://stackoverflow.com/questions/4810664/), see part 4, last section – fredoverflow Jun 11 '14 at 15:55

5 Answers5

3

Of course.

int** ptr_array_5by5;  
void print2DArray_with5Columns(int (*ptr_row)[5]);

print2DArray_with5Columns( (int (*)[5]) ptr_array_5by5);
print2DArray_with5Columns( reinterpret_cast<int (*)[5]>(ptr_array_5by5));

The C language declaration syntax, for all its faults, lets you create casts by simply rewriting the declaration omitting any identifiers. It compiles, and it might even work.


There is a lot of confusion here because the descriptive wording does not match the C declarations. Here is some code that implements this (peculiar) cast and shows that it can work, just as I said.

void print2DArray_with5Columns(int (*ptr_row)[5]) {
    for (int i = 0; i < 5; i++)
        cout << (*ptr_row)[i] << " ";
    cout << std::endl;
}

int main() {
    int* a;
    int** ptr_array_5by5;
    a = new int[25];
    for (int i = 0; i < 25; i++)
        a[i] = i;
    ptr_array_5by5 = (int**)a;
    print2DArray_with5Columns((int (*)[5])(ptr_array_5by5));
    return 0;
}

Please note that this declaration is not a 5x5 matrix. The cast is simply a pointer to an array of 5 ints, which decays to a simple array. This code generates a 5x5 flat matrix and prints the first row.

I suspect the real problem is that the cast is wrong and therefore the whole question is wrong.


The question has been asked whether this is the dreaded Undefined Behaviour. With suitable care it is not. The standard in effect allows any kind of a pointer-to-object to be cast to some other pointer-to-object or to a void pointer or to a large enough integer, and back again. [Pointer-to-function and pointer-to-member are treated a bit differently.] The round-tripped pointer is guaranteed to retain the same value. Therefore this cast is not UB provided the rules are followed, which is not that hard to do.

david.pfx
  • 10,520
  • 3
  • 30
  • 63
  • 2
    +1 for actually answering the question that was asked. – davmac Jun 11 '14 at 15:20
  • Let's try this: http://ideone.com/lVzNrN I admit, it is possible. But I don't think I will implement this ever. – TobiMcNamobi Jun 11 '14 at 15:31
  • 1
    It will _not_ work. The reason for the awkward syntax in C++ is precisely because such things almost never work. (There are, of course, special cases when you're writing near to hardware code.) It was felt that the C syntax made it too easy to screw up, and too difficult to find the screwups. – James Kanze Jun 11 '14 at 15:56
  • @JamesKanze unfortunately the example code, which was added later, may not be correct (though I'll bet it works on most architecture-compiler combinations anyway). However, in terms of answering the question of how to cast an `int **` to an `int (*)[5]`, the answer is bang on. – davmac Jun 12 '14 at 15:35
  • (furthermore, there are cases where performing such a cast and then dereferencing the resulting pointer would _not_ result in undefined behaviour!). – davmac Jun 12 '14 at 15:47
  • "The question has been asked whether this is the dreaded Undefined Behaviour. With suitable care it is not." - yes, but unfortunately the code example you have given does not demonstrate this suitable care. – davmac Jun 13 '14 at 12:48
  • @davmac: If you think there is UB here, then it would be more helpful to the reader to point out exactly where it is. Within certain reasonable assumptions, I think there is no UB in the sample. – david.pfx Jun 13 '14 at 14:08
  • @david.pfx, you cast an `int **` to an `int (*)[5]`, and then de-reference it. This would be valid only if you had obtained the original `int **` by casting from a valid `int (*)[5]`, which you did not. After casting one pointer to another pointer type, it is not generally specified what the result of de-referencing it is (with certain exceptions, none of which apply here); thus, undefined behaviour. – davmac Jun 13 '14 at 15:22
  • @davmac: I rely on n3797 S5.2.10/7 `An object pointer can be explicitly converted to an object pointer of a different type` and the basic concepts in S3. What do you rely on? – david.pfx Jun 14 '14 at 04:17
  • @david.pfx the section you illustrate says only that a pointer to one type can be converted to a pointer to another type, it does not define what happens when you dereference the resulting pointer (nor what it points to, if anything). Thus the dereference is undefined. I don't rely on any specific section, because there is no section that defines what happens when you dereference such a pointer. – davmac Jun 20 '14 at 08:24
  • @davmac: There would be no point in explicitly allowing a pointer-to-object to be converted to a pointer-to-object of a different type if the conversion were meaningless. The dereferencing of the pointer is adequately covered in 5.3/1 and 3.10, and clearly did not need to be spelled out in more detail. If you think a conforming implementation could generate pointers that did not work as I described, the onus is on you to show how. Give an example of a real or plausible implementation with this property. – david.pfx Jun 20 '14 at 10:02
  • @david.pfx, the conversion is not meaningless; the pointer can be converted to a different type and then back and be guaranteed to "compare equal" to (6.3.2.3p7) (and thus point at the same object as) the original pointer. But there is no restriction on the value of the converted pointer other than this "convert back" requirement. For instance the standard allows a conversion of int to float, and back, to 'xor' the pointer value with some constant - a reversible operation which changes the pointer value. More practical examples exist. Not enough space for further comment I'm afraid. – davmac Jun 20 '14 at 11:30
  • "conversion of int to float" - sorry, I meant `int *` to `float *`. Since I've now already created another comment, let me point out that the stipulation in 6.3.2.3p7 (C99/C11) that a pointer converted to another pointer type and then back must "compare equal" to the original pointer value would not be necessary if your interpretation were correct. Relevant sections in the C standards discuss the pointer dereference operation but do not define what happens if the pointer value is not valid. (I guess your 5.3/1 and 3.10 are referring to C++?) – davmac Jun 20 '14 at 11:42
  • If you want to pursue it further I suggest you ask a question. – david.pfx Jun 20 '14 at 13:54
3

The important thing to realize here is that int** is not a pointer to a 2D array of anything. It is a pointer to a 1D array of pointers. int (*)[5] is a pointer to a 2D array of int (or more correctly, it is a pointer to the first element of such an array). You can pretty much convert any pointer type to any other pointer type using reinterpret_cast, but it is also pretty much guaranteed not to work at runtime. (There are special exceptions, but they are all very platform specific, and very close to the hardware.)

If you really have an int** which points to 5 int*, each of which points to 5 int, and you need an int (*)[5], the only way you're going to be able to successfully convert is by doing a conversion on the actual underlying data (which will involve a copy), and not a conversion on the pointer. Something like:

int tmp[5][5];
for ( int i = 0; i != 5; ++ i ) {
    int* row = ptr_array_5by5[i];
    for ( int j = 0; j != 5; ++ j ) {
        tmp[i][j] = row[j];
    }
}

You can then pass tmp to your function without any casts: the implicit conversion of array to pointer will convert int [5][5] to int (*)[5].

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • The one answer that actually points out the danger within the question. I can add, that casting the `int**` to `int (*)[5]` is guaranteed *not* to work as expected, because it will interprete the pointers of the index array as integers. If the 2D region is guaranteed to be allocated in one slap, casting `*ptr_array_5by5` to `int (*)[5]` might work, but that's assuming a lot about the data layout. – cmaster - reinstate monica Jun 11 '14 at 16:36
  • 1
    @cmaster _If_ the region was allocated as an `int[25]`, and you then cast the `int*` to `int (*)[5]`, and access it through that, you formally have undefined behavior. Practically, it will work on almost all implementations; the only one I've ever heard of where it might not is ObjectCenter, which does (or did---I don't know if the compiler is still available) bounds checking. – James Kanze Jun 11 '14 at 17:29
1
  1. int** and int(*)[5] are different types (as n.m. pointed out)
  2. You may treat an array as a pointer, e.g. int a[5]; *(a+1) = 6;
  3. You may treat a pointer as an array, e.g. int *a = new int[5]; a[1] = 6;.

But treating object A as if it were an object B does not mean that it actually is object B.

What you can do though is declaring an int (*)[5], write the values of ptr_array_5by5 into it (after allocating memory, of course), and pass it to print2DArray_with5Columns.

On the other hand, yes there are casts that make your code compile. But I doubt that using one of them is getting you closer to your goal (see http://ideone.com/lVzNrN).

TobiMcNamobi
  • 4,687
  • 3
  • 33
  • 52
  • I think this is wrong. There is no need to allocate more storage and copy data into it, and a perfectly valid cast does exist. See my answer. – david.pfx Jun 11 '14 at 15:14
  • @david.pfx though I agree a valid cast does exist, I think this answer is dealing with the problem that an `int **` points to an array of int pointers rather than an array of arrays. If the original pointer actually points at the data type it describes then the conversion (via allocation / data copying) is necessary. I.e. you _can_ use a simple cast but the resulting pointer is not necessarily valid. – davmac Jun 11 '14 at 15:22
  • I don't agree with the wording "You may __treat__". You can't really treat one as the other; an array _converts_ to a pointer, but the reverse isn't true, and both `*(a + 1)` and `a[1]` are pointer operations (and when used with arrays, count on the implicit conversion). – James Kanze Jun 11 '14 at 15:55
0

I think you are confused by assuming that pointers are arrays. Pointers are not arrays and vice-versa. If ptr_array_5by5 would be a 2D array then the parameter in prototype is fine for passing ptr_array_5by5 as argument. But you declared it as int **ptr_array_5by5, it is better to change the parameter to int ** type.

haccks
  • 104,019
  • 25
  • 176
  • 264
  • Aeeays decay to pointers and pointers can be indexed as if they were arrays, so the distinction is often moot. – david.pfx Jun 22 '14 at 04:07
  • @david.pfx; No. [Distinction is very clear](http://stackoverflow.com/q/1641957/2455888). – haccks Jun 22 '14 at 08:18
  • I think if you read the first answer to that link, you will find it exactly agrees with what I said. What is your point exactly? – david.pfx Jun 22 '14 at 08:21
  • @david.pfx; My point is: *Arrays are not pointers and pointers are not arrays*. Both are different data types. – haccks Jun 22 '14 at 08:25
  • But that's blindingly obvious and quite uninteresting. My point is that you can often use a pointer where an array is expected and vice versa. That makes it much more interesting. – david.pfx Jun 22 '14 at 09:35
  • @david.pfx; May be it is *uninteresting* for you but not obvious. As you said that: `you can often use a pointer where an array is expected and vice versa`: Then try to compile this code: `int a[10], *p = a; a++; a = p;` and tell me why the difference between pointer and array is *uninteresting*? Only similarity between them I know is that **indexing of both array and pointer are similar**, that's it! – haccks Jun 22 '14 at 10:35
0

Why would you like to cast int **?

You use pointers to point to addresses.

So instead of type casting pointers, you have to point this pointer to some variable.

There is no meaning of type casting pointers because they themselves don't store values.