16

C 允许NULL定义为任何空指针常量,换句话说,任何计算结果为 0 的整数常量表达式,或这样的表达式转换为void *。我的问题涉及定义的选择是否真的很重要,即是否正确的程序可能取决于使用的定义。出于这个问题的目的,我想忽略诸如NULL传递给可变参数函数或缺少原型的函数之类的问题,因为我已经单独处理了它。让我们假设sizeof NULL == sizeof(void *)sizeof NULL == sizeof(T)对于一些整数类型T,所以这sizeof不足以回答是否NULL有指针类型的问题。

显然,C11 提供了一种区分类型NULL或任何其他表达式的方法:_Generic关键字。

C99 还提供了一种看似可靠的晦涩方式:

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

是否有任何其他方法NULL可以由符合标准的 C 程序确定类型?在 C89 中可以使用吗?

4

4 回答 4

6

通过问题、答案和评论,我认为我们确定了:

  1. C11 方式很简单(_Generic)。
  2. 由于编译器有问题,C99 方式相当不可靠。
  3. 由于typedef.
  4. 没有找到其他方法。

所以答案似乎是没有可靠的 pre-C11 方法,似乎也没有有效的 pre-C99 方法。

于 2013-06-19T10:20:24.420 回答
4

获取 NULL 的字符串定义,然后根据需要进行完整的检查。这是一个非常简单的想法:

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

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

但我不知道你将如何处理__null可以从中产生的内容。

于 2013-01-09T16:06:18.153 回答
3

您不能将宏字符串化并查看字符串吗?

# 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");
}

"integer"如果您添加它(通常NULL具有 pinter 类型),它会正确打印:

# undef NULL
# define NULL 0

NULL不能是这样(int) ((void *) 0)的,因为标准没有说明转换为整数类型的空指针常量仍然是空指针常量。

此外,该标准还对整数常量表达式(C11,6.6/6)这样说:

整数常量表达式117)应具有整数类型,并且应仅具有整数常量、枚举常量、字符常量、sizeof结果为整数常量的_Alignof表达式、表达式和作为强制转换的直接操作数的浮点常量的操作数。整数常量表达式中的强制转换运算符只能将算术类型转换为整数类型,但作为sizeofor_Alignof运算符的操作数的一部分除外。

编辑:实际上这不适用于以下内容:

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

(感谢您的注意)并且根据 OP 的需要,这不能以简单的方式检查,需要一些工作(简单的解析)。

编辑2:也有typedef正确指出的评论。

于 2013-01-09T16:07:12.410 回答
0

这是一种晦涩难懂的方式:如果程序使用表达式&*NULL,则它不会编译为NULL具有整数类型,但如果NULL具有指针类型则将编译。

C99 有针对这种特殊情况的措辞:

如果[&运算符] 的操作数是一元运算符的结果,则该运算* 符和该&运算符都不会被计算,并且结果就像两者都被省略了,除了对运算符的约束仍然适用并且结果不是左值.

没有违反对运算符的约束: 的操作数&是一元运算符的结果,而一元运算*符的操作数*具有指针类型(我们假设NULL是这样定义的)。

于 2016-03-10T04:55:11.917 回答