Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Given that p is a pointer is "p > nullptr" well-formed?

Tags:

Given a pointer p:

char *p ; // Could be any type 

assuming p is properly initialized is the following well-formed:

if (p > 0) // or p > nullptr 

More generally is it well-formed to use a relational operator when one operand is a pointer and the other is a null pointer constant?

like image 633
Shafik Yaghmour Avatar asked Oct 27 '14 14:10

Shafik Yaghmour


People also ask

What happens when you set a pointer to nullptr?

C. Explanation: What happens here is that when a Null pointer is created, it points to null, without any doubt. But the variable of Null pointer takes some memory. Hence when a pointer to a null pointer is created, it points to an actual memory space, which in turn points to null.

Is nullptr a pointer?

nullptr is a pointer literal of type std::nullptr_t , and it's a prvalue (you cannot take the address of it using & ). 4.10 about pointer conversion says that a prvalue of type std::nullptr_t is a null pointer constant, and that an integral null pointer constant can be converted to std::nullptr_t .

Can this be a nullptr?

'this' is just another pointer. The glory of pointers is that they can be nullptr, and that's a useful condition. I know I can get around this by making static functions, passing as first parameter a pointer to the data structure (hellllo Days of C) and then check the pointer. I'm just surprised I'd need to.

Should I initialize a pointer to nullptr?

nullptr is a good value to initialize, to indicate that there is no memory pointed to by the pointer. Also, delete operation on nullptr is safe, whereas delete on arbitrary values (which is typically the case when you don't initialize the pointer) can cause Segmentation faults.


1 Answers

In C++14 this code is ill-formed but prior to the C++14 this was well-formed code(but the result is unspecified), as defect report 583: Relational pointer comparisons against the null pointer constant notes:

In C, this is ill-formed (cf C99 6.5.8):

void f(char* s) {     if (s < 0) { } } 

...but in C++, it's not. Why? Who would ever need to write (s > 0) when they could just as well write (s != 0)?

This has been in the language since the ARM (and possibly earlier); apparently it's because the pointer conversions (4.10 [conv.ptr]) need to be performed on both operands whenever one of the operands is of pointer type. So it looks like the "null-ptr-to-real-pointer-type" conversion is hitching a ride with the other pointer conversions.

In C++14 this was made ill-formed when N3624 was applied to the draft C++14 standard, which is a revision of N3478. The proposed resolution to 583 notes:

This issue is resolved by the resolution of issue 1512.

and issue 1512 proposed resolution is N3478(N3624 is a revision of N3478):

The proposed wording is found in document N3478.

Changes to section 5.9 from C++11 to C++14

Section 5.9 Relational operators changed a lot between the C++11 draft standard and the C++14 draft standard, the following highlights the most relevant differences (emphasis mine going forward), from paragraph 1:

The operands shall have arithmetic, enumeration, or pointer type, or type std::nullptr_t.

changes to:

The operands shall have arithmetic, enumeration, or pointer type

So the type std::nullptr_t is no longer a valid operand but that still leaves 0 which is a null pointer constant and therefore can be converted(section 4.10) to a pointer type.

This is covered by paragraph 2 which in C++11 says:

[...]Pointer conversions (4.10) and qualification conversions (4.4) are performed on pointer operands (or on a pointer operand and a null pointer constant, or on two null pointer constants, at least one of which is non-integral) to bring them to their composite pointer type. If one operand is a null pointer constant, the composite pointer type is std::nullptr_t if the other operand is also a null pointer constant or, if the other operand is a pointer, the type of the other operand.[...]

this explicitly provides an exception for a null pointer constant operand, changes to the following in C++14:

The usual arithmetic conversions are performed on operands of arithmetic or enumeration type. If both operands are pointers, pointer conversions (4.10) and qualification conversions (4.4) are performed to bring them to their composite pointer type (Clause 5). After conversions, the operands shall have the same type.

In which there is no case that allows 0 to be converted to a pointer type. Both operands must be pointers in order for pointer conversions to be applied and it is required that the operands have the same type after conversions. Which is not satisfied in the case where one operand is a pointer type and the other is a null pointer constant 0.

What if both operands are pointers but one is a null pointer value?

R Sahu asks, is the following code well-formed?:

char* p = ""; char* q = nullptr; if ( p > q ) {} 

Yes, in C++14 this code is well formed, both p and q are pointers but the result of the comparison is unspecified. The defined comparisons for two pointers is set out in paragraph 3 and says:

Comparing pointers to objects is defined as follows:

  • If two pointers point to different elements of the same array, or to subobjects thereof, the pointer to the element with the higher subscript compares greater.

  • If one pointer points to an element of an array, or to a subobject thereof, and another pointer points one past the last element of the array, the latter pointer compares greater.

  • If two pointers point to different non-static data members of the same object, or to subobjects of such members, recursively, the pointer to the later declared member compares greater provided the two members have the same access control (Clause 11) and provided their class is not a union.

Null pointers values are not defined here and later on in paragraph 4 it says:

[...]Otherwise, the result of each of the operators is unspecified.

In C++11 it specifically makes the results unspecified in paragraph 3:

If two pointers p and q of the same type point to different objects that are not members of the same object or elements of the same array or to different functions, or if only one of them is null, the results of p<q, p>q, p<=q, and p>=q are unspecified.

like image 178
Shafik Yaghmour Avatar answered Sep 23 '22 00:09

Shafik Yaghmour