R.. GitHub STOP HELPING ICE
R.. GitHub STOP HELPING ICE

Reputation: 215287

Ways a program might detect whether NULL is defined with integer or pointer type?

C allows NULL to be defined to any null pointer constant, in other words, any integer constant expression that evaluates to 0, or such an expression cast to void *. My question concerns whether the choice of definition really matters, i.e. whether an otherwise-correct program might depend on which definition is used. For the purpose of this question, I'd like to ignore issues like NULL being passed to variadic functions or functions lacking prototypes, since I've already dealt with it separately. Let's assume sizeof NULL == sizeof(void *) and sizeof NULL == sizeof(T) for some integer type T, so that sizeof is not sufficient to answer the question of whether NULL has pointer type.

Obviously, C11 provides a way to distinguish the type of NULL or any other expression: the _Generic keyword.

C99 also provides one obscure way that seems to be reliable:

int null_has_ptr_type()
{
    char s[1][1+(int)NULL];
    int i = 0;
    return sizeof s[i++], i;
}

Are there any other methods by which the type of NULL may be determined by a conforming C program? Any that work in C89?

Upvotes: 17

Views: 1371

Answers (4)

caf
caf

Reputation: 239081

Here's one obscure way: If the program uses the expression &*NULL, this won't compile with NULL having integer type but will if NULL has pointer type.

C99 has wording for this special case:

If the operand [of the & operator] is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue.

The constraints on the operators aren't violated: the operand of & is the result of the unary * operator, and the operand of the unary * operator has pointer type (as we are assuming NULL is defined that way).

Upvotes: 1

R.. GitHub STOP HELPING ICE
R.. GitHub STOP HELPING ICE

Reputation: 215287

Through the question, answers, and comments, I think we established:

  1. The C11 way is easy (_Generic).
  2. The C99 way is fairly unreliable due to buggy compilers.
  3. Stringifying approaches are a dead-end due to typedef.
  4. No other approaches were found.

So the answer seems to be that there's no reliable pre-C11 method, and seemingly no valid pre-C99 method.

Upvotes: 6

effeffe
effeffe

Reputation: 2881

Couldn't you stringify the macro and look at the string?

# include <stddef.h>
# include <stdio.h>
# include <string.h>

# define STRINGIFY(x) STRINGIFY_AUX(x)
# define STRINGIFY_AUX(x) #x

int main(void)
{
  const char *NULL_MACRO = STRINGIFY(NULL);

  if (strstr("void", NULL_MACRO) != NULL)
    puts("pointer");
  else
    puts("integer");
}

It correctly prints "integer" if you add this (usually NULL has pinter type):

# undef NULL
# define NULL 0

NULL cannot be something like (int) ((void *) 0) because the standards doesn't state that a null pointer constant converted to an integer type is still a null pointer constant.

Furthermore, the standard also say this about integer constant expressions (C11, 6.6/6):

An integer constant expression117) shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, _Alignof expressions, and floating constants that are the immediate operands of casts. Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof or _Alignof operator.

EDIT: actually this doesn't work with things like:

# define NULL (sizeof(void *) - sizeof(void *))

(thanks for noticing) and this cannot be checked in a trivial way as the OP needs, a little bit of work (simple parsing) is required.

EDIT 2: and there are also typedef as comment correctly pointed out.

Upvotes: 3

AProgrammer
AProgrammer

Reputation: 52294

Get the string definition of NULL and then do an as complete check as you want. Here is a very simple minded one:

#define XSTR(x) #x
#define STR(x) XSTR(x)

if (STR(NULL)[0] == '(') {
   ...
}

But I don't know how you'll handle a __null which can come out from that.

Upvotes: 4

Related Questions