(char *)NULL - (char *)NULL 是未定义的行为吗?

P__*_*J__ -2 c null pointers pointer-arithmetic language-lawyer

((char *)NULL - (char *)NULL)UB吗?

海事组织的答案在这里不是微不足道的。有什么想法吗?

Godbolt 实验链接https://godbolt.org/z/zgVGk9

聚苯乙烯

我不是在问添加一些空指针的东西(就像在提议的欺骗中一样),而只是关于一种特殊情况

Kei*_*son 8

该表达式具有未定义的行为。

(此问题已作为此问题的副本关闭,但仅讨论指针+整数算术,而不是此问题所询问的指针-指针算术。如果存在特定的现有问题,请随时将问题关闭为副本询问指针减法。)

N1570是 2011 ISO C 标准的草案。第 6.5.6 节第 9 段,讨论减法,说:

两个指针相减时,都指向同一个数组对象的元素,或者指向数组对象最后一个元素后的一个;结果是两个数组元素的下标之差。

(单个非数组对象被视为 1 元素数组的元素,但这不适用于此处。)

表达式产生的指针(char*)NULL不指向数组对象的元素,也不指向任何其他对象(6.3.2.3 第 3 段),因此((char *)NULL - (char *)NULL)违反了should。违反约束或运行时约束之外的应会导致具有未定义的行为(第 4 节第 2 段)。

  • @P__J__ 我引用了标准中与您的主张相矛盾的措辞。空指针与自身比较,但与指向对象的指针不同。空指针不指向对象。您关于“任何相等指针减法结果为零”的断言是没有根据的。一种特殊情况是“(void*)0 - (void*)0”无效,因为没有为指向“void”的指针定义指针算术。但更一般地说,指针减法的标准定义明确要求两个操作数都指向对象。你认为“NULL”指向什么对象? (4认同)
  • @P__J__ 它指出 `(void*)0` 是一个空指针常量,并且评估它会产生一个空指针。“((void*)0)”是“NULL”宏的一种可能定义。是的,“NULL == NULL”。您能澄清一下这有什么关系吗?两个值相等这一事实并不意味着将它们相减是明确定义的。减法和等式是分开定义的并且有不同的规则。 (2认同)
  • @P__J__:根据定义,`NULL` 不引用任何对象! (2认同)
  • @P__J__: **6.3.2.3 指针**: *[...] 如果将空指针常量转换为指针类型,则所得指针(称为空指针)保证与指向任何对象的指针比较不相等或函数。* (2认同)
  • @P__J__:“如果`NULL == NULL`则`NULL - NULL = 0`” - 这并不遵循。指针算术不是数值算术。它遵循 C 标准中指定的规则。这些规则明确没有定义空指针的算术行为。重述您的主张并不会使其变得更加有效。如果“NULL”定义为“((void*)0)”,则“NULL - NULL”违反约束。 (2认同)

chq*_*lie 5

简短的回答是肯定的,行为是未定义的:

C17 J.2 未定义的行为(信息性)

在以下情况下,该行为是未定义的:

  • 不指向或超出同一数组对象的指针将被减去 (6.5.6)。

长(规范)答案是这样的:

C17 6.5.6 加法运算符

...

当两个指针相减时,两个指针都应指向同一个数组对象的元素,或者指向数组对象最后一个元素之后的一个;结果是两个数组元素的下标之差。结果的大小是实现定义的,其类型(有符号整数类型)ptrdiff_t<stddef.h>标头中定义。如果结果无法用该类型的对象表示,则行为未定义。换句话说,如果表达式PQ分别指向数组对象的第i-th 和j-th 元素,则表达式(P)-(Q)具有该值i - j,前提是该值适合类型 的对象ptrdiff_t。此外,如果表达式P指向数组对象的一个​​元素或数组对象最后一个元素之后的一个元素,并且表达式Q指向同一数组对象的最后一个元素,则表达式((Q)+1)-(P)的值与((Q)-(P))+1和 相同-((P)-((Q)+1)),并且如果表达式P指向数组对象的最后一个元素之后,则其值为零,即使表达式(Q)+1不指向数组对象的元素。

由于(char *)NULL不指向数组,因此该表达式具有未定义的行为,但正如您在各种编译器上进行的测试一样,您很可能会获得在编译时确定的(char *)NULL - (char *)NULL值。0C 标准不保证这一点,但需要不正当的编译器才能产生其他任何东西。

  • @P__J__:标准是明确的:*当两个指针相减时,两个指针都应指向同一数组对象的元素*...“NULL”不指向对象,因此它违反了此约束。 (3认同)
  • @chqrlie这不是一个*约束*,它是一个约束之外的“应该”,这使得它成为未定义的行为。(必须诊断约束违规。) (3认同)
  • 当然,`NULL == NULL`:比较指向不同对象或非对象的指针对于`==`和`!=`有效,但对于`&lt;`、`&lt;=`、`&gt;`和`&gt;无效=`。如果 2 个有效指针比较相等,则它们指向同一个数组或对象,被视为大小为 1 的数组,但这并不意味着无效指针有任何意义。常识告诉你该值为“0”,但 C 标准并不保证它。严格的验证套件将检测代码并在“pq”表达式上插入断言,检查“p”和“q”的有效性,并验证它们指向同一数组。发布的代码将失败。 (2认同)
  • 约束很明确,“NULL”**不**指向对象,因此违反了约束。 (2认同)
  • 归谬法:如果存在这样一个对象,则对该对象的引用将等于“NULL”,这与假设相矛盾。 (2认同)