摘取以下片段:
#include <stddef.h>
_Bool
foo(char * p) {
return (p - 1) == NULL;
}
GCC 和 LLVM 将结果优化为 false。
标准中的哪些内容允许编译器假设
p
不是 1
?
这源于两件事:
因此,假设
p
是指向 char
的有效指针,p - 1
也将是指向 char
的有效指针,并且由于有效指针不能为 NULL,因此在该假设下,比较将始终为 false .
在 C 中,定义明确的指针只能指向一个有效对象,或者指向一个有效对象。
当整数类型的表达式与指针相加或相减时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向距原始元素的元素偏移量,使得结果数组元素和原始数组元素的下标之差等于整数表达式。换句话说,如果表达式 P 指向数组对象的第 i 个元素,则表达式 (P)+N (相当于 N+(P))和 (P)-N (其中 N 的值为 n)指向分别为数组对象的第 i+n 个和第 i−n 个元素(前提是它们存在)。此外,如果表达式 P 指向数组对象的最后一个元素,则表达式 (P)+1 指向数组对象的最后一个元素后一位,如果表达式 Q 指向数组对象的最后一个元素后一位,表达式 (Q)-1 指向数组对象的最后一个元素。如果指针操作数和结果都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则求值不会产生溢出;否则,行为是未定义的。如果结果指向数组对象的最后一个元素,则不得将其用作所计算的一元 * 运算符的操作数。
这意味着
p
指向字符数组/结构体等中的有效字符,这不是第一个元素,这意味着在该元素之前,将有一个地址不是NULL
的有效字符。
或者,您从
char
所在内存块边界之外的有效 char
的地址中减去 1。这会调用未定义的行为,因为减法产生的指针不是数组或指向最后一个元素 1 的指针。