Pointers into multidimensional arrays

1,314 views
Skip to first unread message

ndkr...@gmail.com

unread,
Apr 8, 2019, 6:47:31 AM4/8/19
to ISO C++ Standard - Discussion
This is a topic that has caused much confusion over the years, and still appears shrouded in obscurity (with likely differences between various C and C++ standards). See [1], [2] and related/duplicate questions on StackOverflow, for example. [3] also has some relevance. [4] and [5] are two examples of the many C tutorials recommending (possibly erroneously) a naive approach.

Consider the following program:

#include <iostream>

int a[2][2] = {{1, 2}, {3, 4}};

int main() {

  int* p0 = &a[0][0] + 3;                                                       // line 7
  std::cout << *p0 << std::endl;                                                // line 8

  unsigned char* p2 = reinterpret_cast<unsigned char*>(&a) + 3 * sizeof(int);   // line 10
  std::cout << +*p2 << std::endl;                                               // line 11
  int* p3 = reinterpret_cast<int*>(p2);                                         // line 12
  std::cout << *p3 << std::endl;                                                // line 13

  int* p1 = reinterpret_cast<int*>(&a) + 3;                                     // line 15
  std::cout << *p1 << std::endl;                                                // line 16

}

Which lines cause undefined or implementation-defined behavior? I'm most interested in C++17 and later drafts, but information about earlier versions and C are also welcome (as the C++ rules are based on C, but with some changes).

Here are my best guesses (references to draft N4810):

Line 7: Pointer addition is undefined behavior according to 7.6.6¶4.3.
Line 8: (Irrelevant as depends on line 7.)

Line 10: Naively seems like the pointer addition is undefined behavior by 7.6.6¶4 (unless there is an implicit unsigned char array representing the storage), but it "ought" to be the case that a trivially copyable type (like int[2][2]) can be treated as an array of unsigned char, as suggested by 6.7¶2. Perhaps the only way to do this is using std::memcpy, but it would be surprising that a manual loop using unsigned char* is not possible. An analogous property for structs seems to be necessary to make "offsetof" useful.
Line 11: Expect to be defined if line 10 was OK: it's reading the representation of a trivially copyable type.
Line 12: This conversion is defined. The reinterpret_cast here is defined in terms of static_cast from void* to int* (7.6.1.8¶13). The alignment requirement is satisfied. What is not clear is "if the original pointer value points to an
object a" for a = int. But even if not, it is defined as "the pointer value is unchanged by the conversion".
Line 13: The not clear part from line 12 is what determines whether this dereference is defined behavior or not.

(The wording on trivially copyable types, 6.7¶9, "Scalar types, trivially copyable class types, arrays of such types, ... are collectively called trivially copyable types" is perhaps unclear on whether it means "arrays of (scalar types, trivially copyable class types)" or "arrays of trivially copyable types" (recursively), but gcc and clang agree that std::is_trivially_copyable_v<int[2][2]> is true, as do various non-normative references.)

Line 15: Not sure. Seems like should be equivalent to combined effect of lines 10+12, but maybe there's some further subtlety.
Line 16: Not sure. If above equivalence holds, this should be equivalent to line 13.


--

Nick

ndkr...@gmail.com

unread,
Apr 8, 2019, 7:31:28 AM4/8/19
to ISO C++ Standard - Discussion, ndkr...@gmail.com
If the conclusion of this thread [https://groups.google.com/a/isocpp.org/d/msg/std-discussion/JNp8xsgRbW4/HbzPHs75AwAJ] is accurate, the unsigned char* pointer addition on line 10 is indeed undefined behavior, and it seems like the special privileges of std::memcpy would have to be used to access representation (similarly when trying to use "offsetof" to access a field of a struct).

In that case, is there any way to treat a multidimensional array as a 1-dimensional array (e.g. to pass to STL algorithms)?

--

Nick
Reply all
Reply to author
Forward
Message has been deleted
Message has been deleted
0 new messages