tex*_*ral 29 c null pointers undefined-behavior language-lawyer
我知道 C 编译器不需要对 的位表示使用全零NULL,但标准要求它们* *NULL在布尔上下文/比较中使评估为假。因此,下面程序中的第二个 printf将始终输出false.
但我想知道的是:在系统中NULL是*不*全零,将指针值*是*全部为零也值为false布尔上下文/比较?换句话说,将在第一个 printf在下面不断输出的程序true?
或者以稍微不同的方式提问:我可以依靠calloc生成一个在布尔上下文/比较中始终评估为 false 的指针值吗?这个问题的第一个答案用于memset清除long*named的位y,然后继续说这y==0是 UB 因为y可能是“陷阱表示”(无论是什么)。 calloc也只是清除位,所以也许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;
}
Run Code Online (Sandbox Code Playgroud)
chu*_*ica 12
typedef struct { void * p; } obj;
obj * o = calloc(sizeof(obj), 1);
assert(o); // Let us set aside the case of a failed allocation
printf("%s\n", o->p ? "true" : "false"); // 1st: could print "true" ?
Run Code Online (Sandbox Code Playgroud)
我可以依靠
calloc产生一个在布尔上下文/比较中总是评估为假的指针值吗?
无输出可能是"true"。*1 .
全零的位模式,作为指针,可能不是空指针。
7.22.3.2 calloc 函数
2 calloc 函数为nmemb对象数组分配空间,每个对象的大小都是size。空间被初始化为所有位为零。301)
脚注 301) 请注意,这不必与浮点零或空指针常量的表示相同。
示例:一个实现可能只有一个空指针编码,所有的位模式。(void *)0将全零位模式转换为全int 0一void *。 if (null_pointer)始终为 false,无论空指针的位模式如何。
*1但实际上是的,输出总是"false". 如今,不使用全零位模式作为空指针的实现并不常见。高度可移植的代码不会假设这种实用性。考虑一个旧的或新的系统可能使用零位模式作为非空指针- 可悲的是打破了许多假设全零位模式是空指针的代码库。
考虑以下使用表达式逻辑值的地方,全部取自 C18,我用粗斜体强调:
6.3.1.2(布尔型)p1:当任何标量值转换为 时_Bool,如果值比较等于0,则结果为0;否则,结果为 1。
6.5.3.3(一元算术运算符)p5:!如果其操作数的值比较不等于 0,则逻辑否定运算符的结果为 0,如果其操作数的值比较等于0,则结果为 1 。结果有类型int。表达式!E等价于(0==E)。
6.5.13(逻辑与运算符)p3:如果&&运算符的两个操作数比较不等于 0,则该运算符应产生 1 ;否则,结果为 0。结果的类型为int。
6.5.14(逻辑或运算符)p3:||如果其任一操作数比较不等于 0,则该运算符应产生 1 ;否则,结果为 0。结果的类型为int。
6.5.15(条件运算符)p4:评估第一个操作数;在它的求值和第二个或第三个操作数(以求值为准)的求值之间有一个序列点。仅当第一个操作数不等于 0时才计算第二个操作数;仅当第一个操作数等于 0时才计算第三个操作数;结果是第二个或第三个操作数(以求值者为准)的值,转换为下面描述的类型。
6.8.4.1(if语句) p2:在两种形式中,如果表达式比较不等于 0 ,则执行第一个子语句。在else表单中,如果表达式比较等于 0 ,则执行第二个子语句。如果通过标签到达第一个子语句,则不执行第二个子语句。
6.8.5(迭代语句) p4:迭代语句导致循环体语句被重复执行,直到控制表达式比较等于 0。无论循环体是从迭代语句还是通过跳转进入,都会发生重复。
“E 比较等于 0”相当于 C 表达式(E == 0),“E 比较不等于 0”相当于 C 表达式(E != 0)。等式运算符的约束由下式给出:
void;或者关于至少一个操作数是指针的相等运算符的语义:
6.5.9(等式运算符)p5:否则,至少有一个操作数是指针。如果一个操作数是指针,另一个是空指针常量,则将空指针常量转换为指针的类型。如果一个操作数是指向对象类型的指针,而另一个操作数是指向 的限定或非限定版本的指针void,则前者将转换为后者的类型。
p6:两个指针比较相等当且仅当它们都是空指针,都是指向同一个对象(包括指向对象的指针和它开头的子对象的指针)或函数的指针,都是指向最后一个元素的指针相同的数组对象,或者一个是指向一个数组对象末尾的指针,另一个是指向恰好紧跟地址空间中第一个数组对象的另一个数组对象的开头的指针。
关于空指针常量:
void *,称为空指针常量67)。如果将空指针常量转换为指针类型,则生成的指针(称为空指针)保证与指向任何对象或函数的指针不相等。但我想知道的是:在系统中
NULL是不是全零,将指针值是全零也值为false布尔上下文/比较?
旁白:NULL是一个空指针常量,不一定是一个空指针(参见上面的 6.3.2.3p3,它可以是一个整数常量表达式)。您真正的意思是一个系统,其中空指针的位表示不全为零。
注意:正如 Eric Postpischil 在下面的评论中指出的那样,一个系统可能有多个空指针值的位表示,因此我们假设对于这个问题,它们都不是全零位表示。
为了使指针值在布尔上下文/比较中评估为 false,它必须比较不等于 0。在这种情况下,它必须比较不等于空指针常量。在上面的 6.5.9p5 中,空指针常量将被转换为与之比较的指针的类型。根据上面的 6.5.9p6,空指针值不会与非空指针值相等。因此,在空指针值并非所有位为零的系统上,所有位为零的非空指针值将在布尔上下文中评估为真。
或者以稍微不同的方式提问:我可以依靠
calloc生成一个在布尔上下文/比较中始终评估为 false 的指针值吗?
不,您不能依赖calloc(或memset使用字节值 0)来生成在布尔上下文中将评估为 false 的指针值。如果具有全零位表示的指针值不是空指针值,它将在布尔上下文中评估为真。