4

I have two versions of a piece of code. The first is by using an int variable as part of an expression for a condition test, the second by using an integer constant for the same - both represent the same integral value - 24.

1. code:

#include <stdio.h>

int main()
{
    int var = 24;

    if((!var) == NULL)
    {
        printf("1");
    }
}

2. code:

#include <stdio.h>

int main()
{
    if((!24) == NULL)
    {
        printf("1");
    }
}

When I attempt to compile the first version, I get the warning:

warning: comparison between pointer and integer

from gcc, and

warning: comparison between pointer and integer ('int' and 'void *') [-Wpointer-integer-compare]

from clang.

When I compile the almost equivalent code by using the same value just as integer constant, everything is fine. Why?


My research so far:

I looked into C18 and found on section 6.4.4 "Constants":

First under Subsection /2 and /3:

"2 - Each constant shall have a type and the value of a constant shall be in the range of representable values for its type."

"3 - Each constant has a type, determined by its form and value, as detailed later.".

And second under Subsection /5:

"The type of an integer constant is the first of the corresponding list in which its value can be represented."

The following list:

list

Thus, an integer constant with no suffix and relative to the representable value of 24 should have type int.


I understand the warning itself and know that NULL usually on most implementations expands to (void*) 0. Thus, it is reasonable that this warning is thrown.

But why does the warning not occur when using the same value as integer constant?

Community
  • 1
  • 1
  • Use NULL for pointers — do not use NULL when not working with pointers. It is a null pointer constant. See C11 [§7.19 Common definitions ``](http://port70.net/~nsz/c/c11/n1570.html#7.19): _The macros are `NULL` which expands to an implementation-defined null pointer constant; and…_ – Jonathan Leffler Mar 10 '20 at 18:36
  • 4
    Your literal `(!24)` is seen by the compiler as `(0)` - which is a valid pointer constant (in fact, the *only* valid pointer constant). See [here](https://stackoverflow.com/a/24212940/10871073). – Adrian Mole Mar 10 '20 at 18:37
  • 3
    `!24` is zero, and zero can be compared to `NULL` (it can be converted to `void *` without further ado). – Jonathan Leffler Mar 10 '20 at 18:38
  • 2
    Note that `if((!0) == NULL)` should also raise a warning. – ad absurdum Mar 10 '20 at 18:39
  • Confirmed, Clang and GCC warn with `(!0) == NULL` but not `(!1) == NULL`. – Eric Postpischil Mar 10 '20 at 18:46

1 Answers1

6

From C11 6.3.2.3/3:

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

And from 6.5.9/2 regarding equality operators == and !=:

One of the following shall hold:
...
— one operand is a pointer and the other is a null pointer constant.

Note that integer constant expressions are not limited to integer constants (0, 42, etc.) but can also include operators acting on them like 5+5-10.

!24 is an integer constant expression with the value 0, and therefore counts as a null pointer constant. The == operator is allowed to act between a pointer and a null pointer constant, which makes (!24) == NULL a valid comparison.

In your first example, !var is not an integer constant expression because it includes a variable operand, and thus cannot be compared with a pointer.

Caveat: In most implementations, NULL is defined as (void*)0 and the above is correct. However, the standard allows it to be defined it as any null pointer constant such as 0. If this is the case, then the comparison (!var) == NULL will compile because it is the same as (!var) == 0.

interjay
  • 107,303
  • 21
  • 270
  • 254
  • 1
    Had the value in question been 0 instead of 24, the compiler would have generated a warning. – dbush Mar 10 '20 at 18:52