Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shall a pointer past the end of the last array element compare equal to a pointer past the end of the whole array?

Compilers diverge when compiling the code

int main()
{
    constexpr int arr[3] = {};
    static_assert((void*)(arr + 3) == (void*)(&arr + 1));
}

In GCC and Clang, the static_assert doesn't fire, MSVC thinks that the static_assert fails https://godbolt.org/z/dHgmEN

Shall (void*)(arr + 3) == (void*)(&arr + 1) evaluate to true and why?

like image 320
Language Lawyer Avatar asked Oct 15 '22 07:10

Language Lawyer


1 Answers

Relevant rules from latest standard draft:

[intro.object]

Objects can contain other objects, called subobjects. A subobject can be a member subobject ([class.mem]), a base class subobject ([class.derived]), or an array element. An object that is not a subobject of any other object is called a complete object.

So, array elements are subobjects. The array in the example is a complete object.


[expr.eq]

... Comparing pointers is defined as follows:

  • If one pointer represents the address of a complete object, and another pointer represents the address one past the last element of a different complete object,79 the result of the comparison is unspecified.

  • Otherwise, if ... both represent the same address, they compare equal.

79) As specified in [basic.compound], an object that is not an array element is considered to belong to a single-element array for this purpose

The first case seems to match almost, but not quite. Both are pointers to one past the last element of a different complete object - one complete object is the array arr, the other is the hypothetical single element array whose element is arr. And neither is a pointer to the unrelated complete object that might exist after their respective array as clarified by the note in the following [basic.compound] quote.

So, the other case should apply assuming they represent the same address.


[basic.compound]

A value of a pointer type that is a pointer to or past the end of an object represents the address of the first byte in memory ([intro.memory]) occupied by the object43 or the first byte in memory after the end of the storage occupied by the object, respectively. [ Note: A pointer past the end of an object ([expr.add]) is not considered to point to an unrelated object of the object's type that might be located at that address. A pointer value becomes invalid when the storage it denotes reaches the end of its storage duration; see [basic.stc]. — end note ]

For purposes of ... comparison ([expr.rel], [expr.eq]), a pointer past the end of the last element of an array x of n elements is considered to be equivalent to a pointer to a hypothetical array element n of x and an object of type T that is not an array element is considered to belong to an array with one element of type T. ...

43) For an object that is not within its lifetime, this is the first byte in memory that it will occupy or used to occupy.

Both arr, and the hypothetical array containing just arr have the same address and the same size. Therefore one element past both arrays is considered to be equivalent to a pointer at the same address outside the bounds of the array.


Just to be clear that an array cannot contain padding:

[expr.sizeof]

... When applied to a class, the result is the number of bytes in an object of that class including any padding required for placing objects of that type in an array. The result of applying sizeof to a potentially-overlapping subobject is the size of the type, not the size of the subobject. When applied to an array, the result is the total number of bytes in the array. This implies that the size of an array of n elements is n times the size of an element.


Let us clarify that conversion to void* should not change pointer value and thus the equality.

[conv.ptr]

A prvalue of type “pointer to cv T”, where T is an object type, can be converted to a prvalue of type “pointer to cv void”. The pointer value ([basic.compound]) is unchanged by this conversion.

like image 67
eerorika Avatar answered Nov 03 '22 23:11

eerorika