我可以相信(uintptr_t)NULL等于零吗?

4

我知道NULL不要求是全零模式,但我想知道是否


(注:本文中的“NULL”指空指针)
uintptr_t x = (uintptr_t)NULL;
printf("%" PRIuPTR, x);

这段代码是否确保输出0?我怀疑不是,但只是想确认一下。

更重要的是,我能相信它吗?

if( ((uintptr_t)f(x) | (uintptr_t)f(y)) != 0)

假设 f 是一个返回指针的函数。我怀疑这段代码严重依赖于 NULL 是一种全 0 位模式。我的猜测正确吗?
我知道这被认为是一种不好的做法,而我自己也永远不会写这样的代码,但我想知道这是否被定义良好。
我在这个答案中遇到了这段代码,作者正在使用它。
while ( ( (uintptr_t)fgets(a,100,fp1) | (uintptr_t)fgets (b,100,fp) ) != 0 ) {
        printf("%s",a);
        printf("%s",b);
}

这里不是要偏离主题,但是有没有理由不直接写成 if (f(x) || f(y)) - WhozCraig
1
@WhozCraig 好吧,这是我对那段代码作者的问题之一 ;) - klutt
@WhozCraig 或许代码想要避免短路第二个函数调用,但是这仍然可以在不使用 (uintptr_t) 的技巧的情况下完成。 - Ian Abbott
也许一些来自1979年的编译器可能会将NULL定义为与0不同的内容,但我认为任何新的C编译器都不会冒险将其定义为~0(例如)。这样会破坏成千上万的程序。 - i486
@i486 当然可以,但既然您是代码的作者,能否解释一下首先将其转换为uintptr_t的目的是什么? - klutt
显示剩余4条评论
3个回答

3

不,没有这样的保证。根据7.19,NULL是一个宏定义,它扩展为实现定义的空指针常量。

但是,如果p不是NULL(其中p是一个指针),if(p)保证为真。

因此,如果指针为空,则printf("%d\n", !!p)保证打印0,如果非空则打印1

while ( fgets(a,100,fp1) || fgets (b,100,fp))

或者
while ( fgets(a,100,fp1) != NULL || fgets (b,100,fp) != NULL)

或者更加独特的方式。
while ( ((fgets(a,100,fp1) != NULL) | (fgets (b,100,fp) != NULL)) != 0)

while ( (!!fgets(a,100,fp1) | !!fgets (b,100,fp)) != 0)

更具体地说,它扩展为实现定义的空指针常量(并没有其他类型的指针常量!)。 - Ian Abbott
1
@IanAbbott 确实,已经修改了。标准的某些部分实际上写得像是经过一夜控制物质吸食后编写的 :) - 0___________

3

从理论上来说:不会。

但在实际操作中,可能会发生。

在相关标准中,对于空指针的检查是“特殊”的,因为它们不是与值0进行数字比较,因此从理论上讲,一些实现或平台可能会为空指针分配不同的值。


1
请注意,即使是所有零位的 _空指针_,也没有规定将其转换为值为0的整数。这可能只会在敌对编译器或旨在暴露代码弱点的编译器上发生。 - chux - Reinstate Monica
@chux-ReinstateMonica 这是一个非常好的观点。足够好以包含在答案中。 - klutt

1
但我想知道下面的代码是否保证输出0?
uintptr_t x = (uintptr_t)NULL;
printf("%" PRIuPTR, x);

不是的。虽然通常如此,但未指定要这样做。

  • (uintptr)NULL仅未指定转换为0。虽然很常见,但实践中很常见。 NULL可以是整数或指针。 NULL可能是无法转换为整数0的指针。即使NULL是所有零位模式,它仍未指定转换为整数0。

  • uintptr_tintptr_t是可选类型。它们经常存在,但不是必需的。考虑一个void *比64位更宽的系统,但最宽的整数类型为64位。


我能相信这个吗?
if( ((uintptr_t)f(x) | (uintptr_t)f(y)) != 0)

这种方法通常可行,但不太具有可移植性。
替代方案:

if( !!f(x) | !!f(y))

我怀疑这段代码严重依赖于NULL是一个全零模式。我正确吗?

几乎正确。它依赖于 (uintptr_t)NULL 转换为0。


除了转换问题外,我发现以下代码存在一个弱点,即 fgets() 由于输入错误或文件结束而返回 NULL 时,缓冲区也不一定是 _null 字符终止的(它可能也过时了),这会导致更多的 UB 或旧输出在打印时。
如果发生输入错误(流方向错误、文件未打开等),则 a[] 是不确定的,导致使用 printf("%s",a); 时出现 UB。
while ( ( (uintptr_t)fgets(a,100,fp1) | (uintptr_t)fgets (b,100,fp) ) != 0 ) {
    printf("%s",a);
    printf("%s",b);
}

fgets()的缓冲区内容最好不要在函数返回NULL时读取。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接