-1

I have recently read Can I use NULL as substitution for the value of 0?

In short, in answers it was mentioned that using NULL as a substitution for the value of 0 is not suggested and will to lead to UB.

But in Is it safe to assume that the NULL constant is zero?, in short it was said that assuming if(!ptr)//ptr is a pointer is not completely wrong.

I know the question contents are different, but how could this be explained that using NULL as substitution for 0 is wrong , while if(!ptr) is true? Because if(!ptr) is equivalent to if(ptr==0) (I assume this is right, not sure).

Also, I have used if(ptr==0) and it never worked wrong for me (to check if ptr is NULL), and I have assigned 0 to pointer ptr and when I debugged my code ptr was NULL. Are these two experiences safe?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
hanie
  • 1,863
  • 3
  • 9
  • 19

3 Answers3

2

From this NULL pointer reference:

To initialize a pointer to null or to assign the null value to an existing pointer, a null pointer constant (NULL, or any other integer constant with the value zero) may be used.

[Emphasis mine]

So the integer constant 0 is a valid null pointer constant.

But note that it doesn't mean that the actual null value on the hardware platform used is equal to 0, it only means that the compiler accepts 0 as an alias for the system-dependent null pointer constant.

Also, a null pointer is always "false" and a non-null pointer is always "true", which is why a condition like if (ptr) or if (!ptr) works well.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
1

I know the question contents are different, but how could this be explained that using NULL as substitution for 0 is wrong , while if(!ptr) is true? Because if(!ptr) is equivalent to if(ptr==0) (I assume this is right, not sure).

if(!ptr) is equivalent to if (!ptr != 0) by the semantics of if statements, which is equivalent to if (ptr == 0) by the semantics of the !, !=, and == operators with pointer operands. This is nicely consistent, but it does not follow from anything you know about operations on integers. Operations on pointers have their own set of rules.

And that is exactly the take home. Because an integer constant with value zero -- which is a source code construct -- is, among other things, a null pointer constant, it by definition compares equal to all null pointer values. This says nothing at all about the representation of null pointer values of any type, about the type of the expression to which the NULL macro expands, or about the value produced by converting to integer any particular null pointer value that is not represented in source code as an integer constant with value zero.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
1

NULL is a macro. It is an "implementation-defined null pointer constant;" C17dr § 7.19 3.

An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant. C17dr § 6.3.2.3 3

So NULL may have the type of void *, int, long, unsigned, long long, etc.

0 is an int constant.

When its OK.

Assignment: Both below assign p,q to some null pointer.

void *p = 0;
void *q = NULL; 

Code compare: p==q is true as all null pointers equate. All null pointers do not equate to the address of any object. !p and !q are both 1.

When its not OK.

Function argument

The type and its size of NULL is implementation defined.

printf("%d\n", 0);            // OK - %d expects an int
printf("%d\n", NULL);         // Not OK, NULL could be long, void *, etc.
printf("%p\n", NULL);         // Not OK, NULL could be int, long, long long
printf("%p\n", (void*) NULL); // OK - %p expects a void*

_Generic()

The result of the below is implementation defined.

_Generic((NULL), \
  void *: "void *", \
  int: "int", \
  long: "long", \
  default: "TBD")

Macro compare

The below resulted in "error: operator '*' has no right operand" for me. #if !0 was fine.

#if !NULL
#error foo
#endif
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • is this `ptr=0` ok? I mean , is this initialization instead of `ptr=NULL `right to be used? – hanie Apr 03 '20 at 19:50
  • Implementations may or may not guarantee anything about all of the `printf` examples, but those which specify that they define `NULL` in certain ways would be required to process either the second or third "not ok" line in meaningful fashion, depending upon how precisely they specify `NULL`. – supercat Apr 03 '20 at 19:53
  • The first convert the `int` 0 to a pointer, a _null pointer_ and then assigns. Well defined. The 2nd converts whatever type `NULL` is to a pointer, a _null pointer_ and then assigns. Also well defined. Use either, best to code to your group's coding standard/style. – chux - Reinstate Monica Apr 03 '20 at 19:54
  • @supercat "Implementations may or may not guarantee anything about all of the printf examples," --> What is not guaranteed about `printf("%d\n", 0);`? – chux - Reinstate Monica Apr 03 '20 at 19:55
  • @chux-ReinstateMonica: They would be required to offer guarantees about *some*, but not *all*. – supercat Apr 03 '20 at 19:56
  • @supercat I hope we agree the 2nd and 3rd are potential UB given the possibility the specifier and argument type may not match. – chux - Reinstate Monica Apr 03 '20 at 20:00
  • @chux-ReinstateMonica: Each would be UB on some implementations. The Standard lacks terminology to describe actions which may be UB or not *based upon implementation-defined traits*; if it classifies such actions as all, it tends to classify them as UB without regard for whether some implementations should define them [For example, that C99 likely only intended to eliminate C89's behavioral requirements for left-shifting negative numbers in sign-magnitude implementations, which would have made sense, but it takes behavior that was defined on all platforms into UB on all platforms. – supercat Apr 03 '20 at 20:10