Treating 2D array as 1D array

后端 未结 4 1930
余生分开走
余生分开走 2020-12-10 12:31

Let\'s say we have a 2D int array:

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

Is it legal and valid to take its

4条回答
  •  死守一世寂寞
    2020-12-10 12:52

    Thank you all for replying and commenting, but I think the correct answer is - as it stands the code exhibits technical UB, though correctable. I have looked through some of those questions [1, 2] @xskxzr linked and it led me to this quote from the standard:

    If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_­cast. [ Note: An array object and its first element are not pointer-interconvertible, even though they have the same address. — end note ]

    Then on reinterpret_cast page there is the following note with an example:

    Assuming that alignment requirements are met, a reinterpret_cast does not change the value of a pointer outside of a few limited cases dealing with pointer-interconvertible objects:

    int arr[2];
    int* p5 = reinterpret_cast(&arr); // value of p5 is unchanged by reinterpret_cast and
                                            // is "pointer to arr"
    

    Even though this compiles without warning and runs, this is technically a UB because p5 is technically still a pointer to arr and not to arr[0]. So basically the use of reinterpret_cast the way I used it leads to UB. Taking the above into account, if I were to create int * directly to the 1st int (and this is ok according to the answer by @codekaizer), then this should be valid, right?:

    template
    void sort2(T(&arr)[X][Y])
    {
        T *p = &arr[0][0]; // or T *p = arr[0];
        std::sort(p, p + X * Y);
    }
    

    But it probably is UB as well since the pointer p is pointing to the first T of the first array of Ts which has Y elements. p + X*Y therefore will be pointing out of range of this 1st array of Ts, hence UB (thanks again to @xskxzr for the link and comment).

    If the expression P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i+j] if 0≤i+j≤n; otherwise, the behavior is undefined.

    So here is my final attempt before I give up:

    template
    void sort2(T(&arr)[X][Y])
    {
        T(&a)[X * Y] = reinterpret_cast(arr);
        std::sort(a, a + X * Y);
    }
    

    Here T arr[X][Y] is first converted to T a[X*Y] with, again, reinterpret_cast, which I think is now valid. The reinterpreted array a happily decays to a pointer to 1st element of array a[X*Y] (a + X * Y is also within the range) and gets converted to an iterator in std::sort.

    TL;DR version

    Behaviour in the OP is Undefined because of improper use of reinterpret_cast. The correct way to convert 2D array to 1D array would be:

    //-- T arr2d[X][Y]
    T(&arr1d)[X*Y] = reinterpret_cast(arr2d);
    

    An lvalue expression of type T1 can be converted to reference to another type T2. The result is an lvalue or xvalue referring to the same object as the original lvalue, but with a different type. No temporary is created, no copy is made, no constructors or conversion functions are called. The resulting reference can only be accessed safely if allowed by the type aliasing rules

    Aliasing rules:

    Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:

    • AliasedType and DynamicType are similar.

    Type similarity:

    Informally, two types are similar if, ignoring top-level cv-qualification

    • they are both arrays of the same size or both arrays of unknown bound, and the array element types are similar.

    Array element type:

    In a declaration T D where D has the form

    D1 [ constant-expression opt ] attribute-specifier-seq opt

    and the type of the identifier in the declaration T D1 is “derived-declarator-type-list T”, then the type of the identifier of D is an array type; if the type of the identifier of D contains the auto type-specifier, the program is ill-formed. T is called the array element type;

提交回复
热议问题