【发布时间】:2020-12-15 23:42:19
【问题描述】:
我知道 C 编译器不需要对 NULL 的位表示使用全零,但它们*是*标准要求使 NULL 在布尔值中计算为 false上下文/比较。因此下面程序中的2nd printf 将始终输出false。
但我想知道的是:在 NULL 是 *not* 全零的系统上,是否会有一个指针值 *is* 全零也计算在布尔上下文/比较中为假?换句话说,下面程序中的1stprintf会输出true吗?
或者以稍微不同的方式问:我可以依靠calloc 生成一个在布尔上下文/比较中总是评估为假的指针值吗? this 问题的第一个答案使用memset 清除名为y 的long* 的位,然后继续说y==0 是UB,因为y 可能是“陷阱表示”(无论那是)。 calloc 也只是清除位,所以可能 1st 中的 o->p printf 也是 UB?
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef struct { void * p; } obj;
int main() {
obj * o = calloc(sizeof(obj), 1);
assert(o); // assume successful allocation
printf("%s\n", o->p ? "true" : "false"); // 1st: could print "true"? Is o->p UB?
o->p = NULL;
printf("%s\n", o->p ? "true" : "false"); // 2nd: always prints "false"
return 0;
}
【问题讨论】:
-
您是否知道空指针并非全为零的架构?我想我从未见过,其中包括像分段 16 位 x86 这样的怪人。
-
请注意,C 中的指针不一定是真正的数字,它只是在某些情况下表现得像一个(你可以做算术等)。这就是为什么 C 也没有为 printf 定义
%p的原因,因为不需要“指针”的一致表示。 FWIW,指针实际上可以是指向某个“对象”的箭头,C 在这方面非常抽象。因此,询问“位表示”对于严格阅读标准通常没有真正意义。 -
@textral 你如何从这里总结出程序员的意图?我只能说程序员的意图是在那里写一个全为零的值。如果意图是有一个空指针,我不知道。有疑问,我不得不假设程序员知道他的目标平台,知道什么是全零模式,什么不是。
-
@MarkRansom 我为 CDC Cyber 180 系列开发了一个 C 编译器。这些机器旨在运行类似于 Multics 的操作系统,因此它的 48 位指针包括一个 4 位环号。只有在环 0 中运行的代码才能创建将环号设置为 0 的指针。因此,我们有不全为 0 的空指针。如果在分支中使用了指针,我们会将其移至整数寄存器并屏蔽掉环号,然后再对其进行全零测试;第二个 println 语句将打印“false”。第一个 println 访问一个未初始化的位置,因此是未定义的行为。
-
@TheodoreNorvell 感谢您提供的具体示例。我在 CDC Cyber 6400 上学习了汇编程序,当然它的工作方式完全不同——地址寄存器只有 18 位,并且没有环的概念。每个进程都有自己的地址空间,监控功能由一组单独的外围处理器处理。
标签: c pointers null language-lawyer undefined-behavior