8

Is it safe to cast a 2D array of type T to T* and dereference the elements?

Since the memory layout of 2D array is linear the base pointer should be equal to pointer to the first element. Since the final type they are pointing to is also the same, there shouldn't be any alignment difference issue.

Or there is some aspect that can cause Undefined Behaviour?

Just to be clear I mean something like this -

int arr[10][10];
int p = *((int*) arr);

Also, the same question if I access elements beyond the first array i.e. (int*) arr + 13. Would it come under the clause of out of bounds access? Since I am accessing outside the bounds of the first array.

Ajay Brahmakshatriya
  • 8,993
  • 3
  • 26
  • 49
  • @dasblinkenlight Yes I am also dereferencing in the same statement to get `int`. It works, I am just curious if this invokes any kind of UB. – Ajay Brahmakshatriya May 08 '17 at 15:27
  • 1
    I'm unclear why you would even *want* to. `int *p = *arr;` is valid, and by further indirecftion, `int val = **arr;` is as well. Why muddy any of that up with casts? Are you *really* asking if the address of the first element of an array of arrays is equivalent to the address of the first element of the first array in an array of arrays, and whether the standard guarantees that? – WhozCraig May 08 '17 at 15:29
  • I would not hesitate to use such code in my private as well as productive code **but** I wouldn't dare to ask this here. I'm looking forward a good answer which cites the respective standard paragraphs... – Scheff's Cat May 08 '17 at 15:29
  • There have been a few arguments here about whether you can calculate `(int*) arr + 13` or if it becomes undefined once you go past the end of the “first” array (although the ones I’ve seen have been for C++, not C, and the relevant parts of the standard might be different). I don’t think there’s any reason to think you get UB if you stay within the first array, though. – Daniel H May 08 '17 at 15:31
  • @WhozCraig the intent of the question is not to know how to access the elements. I was wondering if this cast is valid by the C standards. – Ajay Brahmakshatriya May 08 '17 at 15:31
  • @DanielH I also intend to access beyond. I will edit the question to clarify the same. – Ajay Brahmakshatriya May 08 '17 at 15:32
  • @WhozCraig Yes, I want to know if address of `arr[i][j]` is equivalent to `(int*) arr + i*10 + j` and also if it is okay by the standard to perform the cast and dereference? – Ajay Brahmakshatriya May 08 '17 at 15:41
  • 1
    The consensus I’ve seen is that it probably isn’t allowed in C++, but it will work on practically any system in practice I wouldn’t be surprised if we missed something and it actually is allowed. I’m also sure that C is more permissive in some ways so even if it is UB in C++, there’s a good chance it’s allowed in C. I can’t wait to see what the answer is here. – Daniel H May 08 '17 at 15:42
  • Does this answer your question? [One-dimensional access to a multidimensional array: is it well-defined behaviour?](https://stackoverflow.com/questions/6290956/one-dimensional-access-to-a-multidimensional-array-is-it-well-defined-behaviour) – Andreas Wenzel Dec 10 '22 at 00:43

1 Answers1

7

The cast itself is fine. What might indeed be questionable would be using a pointer to an element within one subarray to access elements in a different one: While the operation is clearly well-defined at a lower level (everything's aligned properly, no padding, the types match, ...), my impression of the C standard has been that it is worded in a way that allows bounds-checking implementations to be standards-conforming.

Note, however, that linearly traversing a multi-dimensional array might nevertheless still be permissible as a pointer pointing past a subarray (which normally must not be dereferenced) also happens to be a pointer to the first element of the next subarray. What this thought leads to is pointer arithmetics being non-associative:

It is my understanding that an expression such as (int *)arr + 13 involves undefined behaviour1, but might become well-defined if you split it into two steps ((int *)arr + 10) + 3.

If you do want to do it in a single step, there's of course also the option of dropping to the byte level, ie (int *)((char *)arr + 13 * sizeof (int)), which should be unproblematic as in contrast to other pointer types, character pointers are bounded by the outermost enclosing object.

I've had discussions about this before, but I don't remember if there ever was a definitive conclusion resolving this particular ambiguity.


1 C11, section 6.5.6 §8

[...] If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. [...]

Christoph
  • 164,997
  • 36
  • 182
  • 240
  • I added the cast since I wanted the first element after a single dereference. `*arr` would return the address of the first element which would be the same as `arr`. – Ajay Brahmakshatriya May 08 '17 at 15:51
  • Also, would it be possible for you to point to any discussions on the topic if they are public. I would like to go through the arguments of both sides. – Ajay Brahmakshatriya May 08 '17 at 15:52
  • @AjayBrahmakshatriya: You're right, the cast was fine - I was a bit too quick when writing my answer. I've now also added a bit about the idea of non-associative pointer arithmetics.Aas to links to such discussions: They happened in person, though I think I also talked about it here on stackoverflow. I'll see what I can dig up... – Christoph May 08 '17 at 16:10
  • I see. I think the thing that you mention in the first para would be different case of doing something like `&arr[0][0] + 13`. – Ajay Brahmakshatriya May 08 '17 at 16:14
  • About the para you sorted, would it be okay to assume the elements of the subarrays to be a part of the same "array object" (a two dimensional array object)? – Ajay Brahmakshatriya May 08 '17 at 16:15
  • 1
    One case I think where this can go wrong if the compiler decides to reserve more space for each subarray. i.e. say it reserves 16 bytes for each subarray instead of 10 (and generates proper code for `arr[i][j]`). Does the standard specify anywhere that the total size of the object should be `100 * sizeof(int)` when I declare `int arr[10][10]`? – Ajay Brahmakshatriya May 08 '17 at 16:19
  • 2
    Yes, we always have `sizeof (int [10][10]) == 100 * sizeof (int)`. I don't think that's explicitly stated anywhere, but eg example 2 of 6.5.3.4 is the well-known idiom `sizeof array / sizeof array[0]`, which only works if arrays can't add padding. – Christoph May 08 '17 at 16:42
  • 1
    Yes, that is a strong evidence but `sizeof arr` could be resolved to return 100 irrespective of how much space is actually allocated – Ajay Brahmakshatriya May 08 '17 at 16:49
  • 2
    @AjayBrahmakshatriya: The Standard expressly requires compilers to treat the stride of an array as equal to the element size, and the size of an array as equal the element size times the declared number of elements. Thus, given `int foo[4][5]`, the Standard guarantees that `foo[0][4]` and `foo[0][1]` will be stored consecutively. These requirements would be rather silly and useless were there not a way to obtain a pointer that could be used to access all the elements of an array sequentially. Unfortunately, the Standard isn't exactly clear on what is required to do that. – supercat May 08 '17 at 18:49
  • 3
    @supercat I’ve seen that said before, but I never actually seen where the standard says that arrays can’t have padding on the end. It’s at least a *de facto* standard because no compiler writer wants to break `sizeof(arr) / sizeof(arr[0])`, but I’m not sure where it’s actually stated. – Daniel H May 09 '17 at 13:40
  • 2
    @DanielH: Searching through it, the only mention I see is a statement that that `sizeof` expression will report the size of an array. Perhaps I saw the more explicit guarantee in K&R2 which isn't the Standard, but the Standard makes no provision that would allow an `int foo[7][3]` declaration to create an array where `sizeof foo[0] != 3* sizeof foo[0][0]`. – supercat May 09 '17 at 15:57