1

I came across this question regarding sorting the first 2 lines of an array of integers, the obvious way that came to mind was to use std::sort so I proposed a solution like:

int mat[][3] = { {4, 5, 3},
                 {6, 8, 7},
                 {9, 5, 4},
                 {2, 1, 3} }; 


std::sort(std::begin(mat[0]), std::end(mat[1])); //sprting the first two rows

As you can see here it works without errors or warnings.

Meanwhile @Jarod42 pointed out that this is pedantically undefined behaviour in C++ because these are pointers of two different arrays.

I inclined towards this given that in C this would be a good way to do it, (without the std::sort, std::begin and std::end of course), using a similar method of accessing the array in line, given the way 2D arrays are stored in C.

We agreed that it would be undefined behaviour, but as @SergeBallesta remebered, pretty much all compilers accept this method, so should it be used?

And what about if one uses a int(*mat)[3] pointer to array, would it still be pedantic UB to use std::sort this way?

//...
srand(time(0));

int(*mat)[3] = (int(*)[3])malloc(sizeof(int) * 4 * 3);
//or int(*mat)[3] = new int[4][3];

for(int i = 0; i < 4 ; i++)
    for(int j = 0; j < 3; j++)
        mat[i][j] = rand() % 9 + 1;

std::sort(std::begin(mat[0]), std::end(mat[1])); //sorting the first two rows
//...
anastaciu
  • 23,467
  • 7
  • 28
  • 53
  • 2
    Related to [treating-2d-array-as-1d-array](https://stackoverflow.com/questions/50795498/treating-2d-array-as-1d-array). – Jarod42 May 25 '20 at 13:03
  • 1
    @anastaciu: It is detectable (at least in `constexpr` [here](https://godbolt.org/z/Vw-Hf8)). Badly, there are no good alternative. and compilers do the expected things (if compiler treats that really as UB (as removing code as wrong execution path), then warning would be really needed to fix the issue). – Jarod42 May 25 '20 at 13:23

1 Answers1

3

The problem comes from the way the standard defines an array type (8.3.4 [dcl.array]):

An object of array type contains a contiguously allocated non-empty set of N subobjects of type T.

but it does not explicitely says that a contiguous allocated set of objects of the same type can be used as an array.

For compatibility reasons all compilers I know accept that reciprocity, but on a pedantical point of view, it is not explicitely defined in the standard and is Undefined Behaviour.

The rationale behind the non reciprocity is that a program is expected to represent a model. And in the model, an object has no reason to be member or more than one array at the same time. So the standard does not allow it. In fact all the (real world) use cases I have ever encountered for handling a 2D array as if it was a 1D one were just low level optimization reasons. And in modern C++, the programmer should not care for low level optimization but let the compiler handle it.

The following it only my opinion.

When you find yourself processing a 2D array as if it was a 1D one, you should ask yourself for the reason. If you are using a legacy code, do not worry about it: compilers currently accept it, and even in the future will probably continue, even at the price of special options.

But if you are writing new code, you should try to move one step higher (or back) and wonder what it represents at the model level. Most of the time, you will find the the array is intrisically 1D or 2D but not both. Once this is done, if performance is not critical try to always handle it the conformant way. Or even better, try to use containers from the standard library instead of raw arrays.

If you are in a performance critical code where saying that any contiguous allocated set of objects is an array provides an important benefit, do it and document it for future maintainers. But only do that after profiling...

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