clanguage-lawyer

Is behaviour of this function defined?


int *shiftRmemmove(int *arr, size_t size)
{
    if(arr && size)
    {
        memmove(arr + size - 1, arr + size - 2, (size - 1) *sizeof(*arr));
        arr[0] = 0;
    }
    return arr;
}

Is it defined if size == 1? It is calculating and passing reference to the elemet of the array one *before the first element. it is equivalent to: memove(arr, arr -1, 0); (©Jabberwocky)


Solution

  • arr is a pointer, but, from the comments, we are intended to assume it points to element 0 of some array A.

    Consider the expression arr + size - 2. arr + size is uncontroversial; it points to &A[1], even if A is an array of 1 element, so that &A[1] is the address “just beyond” the last element of the array.

    For arr + size - 2, effectively &A[1] - 2, we consider C 2018 6.5.6 8:

    If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i + n-th and in-th elements of the array object, provided they exist [emphasis added]…

    This passage does not define the result of &A[1] - 2 because the element of array A with index −1 does not exist. This alone suffices to render the behavior not defined, because nothing else in the standard defines it, and C 2018 4 2 tells us “Undefined behavior is otherwise indicated in this document… by the omission of any explicit definition of behavior.” However, 6.5.6 8 continues:

    … 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…

    Here the pointer operand (&A[1]) and the result (&(A[-1]) do not point to elements of the same array object or one past it, so the behavior is undefined.

    Evaluation of arr + size - 2 occurs before memmove is called, so whether memmove uses its corresponding parameter is irrelevant; the behavior is already undefined.