指针相减 p - (p - 1) 如何导致整数溢出?

3

以下是代码:

#include <stdio.h>
int main()
{
  int i = 3;
  int *p = &i;
  p - (p - 1);
  return 0;
}

编译器(gcc)警告外部减法存在整数溢出:
[user@comp c]$ gcc foo.c
foo.c: In function ‘main’:
foo.c:6:5: warning: integer overflow in expression [-Woverflow]
   p - (p - 1);
     ^

在我的机器上得到了正确的结果1。

为什么呢?

这是因为指针地址是无符号整数,而ptrdiff_t是带符号整数,不能处理那些大的数字。

我看到了这个信息。

p - (p);

并且

p - (p + 1);

不要引起溢出。

我正在努力理解这里的背景。这是我在stackoverflow上的第一个问题,请让我知道如果我的问题可以改进。


2
核心问题是 (p - 1),因为 p-1 越界了。 - R Sahu
你使用的编译器/标志是什么?即使我试图让gcc变得更加暴躁,它也没有抱怨。 - FatalError
1
你向我们展示了一个8行的程序和一个指向第12行的错误信息。请向我们展示整个(简短的)编译程序和精确的复制并粘贴的错误消息,以及您正在使用的精确命令行。 - Keith Thompson
@Keith Thompson:我修复了命令行引号的问题吗?你所说的确切命令行是什么意思?你想知道我正在使用哪个终端吗? - Lumbering Lummox
1
@LumberingLummox:是的,gcc foo.c 就是我所指的命令行。这个特定的信息只会出现在较新版本的 gcc 中,并且信息的措辞是不正确的(它不是整数溢出)。我已经更新了我的答案以反映这一点。 - Keith Thompson
显示剩余5条评论
1个回答

7
指针算术不是整数算术。它是根据数组元素的地址定义的。如果p指向数组的一个元素,则p-1指向同一数组的前一个元素。如果该元素不存在,则减法的结果未定义。为了进行指针算术,单个对象被视为一个包含1个元素的数组。指针可以指向数组的末尾,但这样的指针不能被解引用。
int i = 3;
int *p = &i;

到目前为止,一切都很好;p 指向 i
p - (p - 1);

评估

-1

的行为未定义,没有正确的结果。
通常编译器不会在运行时生成代码来检查指针算术的有效性。在典型的实现中,上述内容将产生“预期”的1结果。编译器甚至可能在编译时用文字1替换表达式,但在进行所需的分析以进行优化时,它可能会注意到行为是未定义的并警告您。
至于为什么会得到特定的消息,这是关于您的编译器的问题,它恰好是gcc。我在gcc 4.7.2中没有收到该消息,但在4.8.0和4.9.0中都收到了该消息。(命令
gcc --version

告诉您正在使用的版本。gcc打印了一些警告信息是正确的,但那个特定的消息是不正确的,因为没有进行整数算术运算。 "整数溢出"消息是gcc中的一个错误,它还会导致在有效代码中打印虚假警告。 我已经提交了一个错误报告,预计将在4.8.4版本中修复。

p - (p);

这是有效的(但括号是不必要的)。两个指针相减得到的是它们所指向的数组元素之间的距离(以元素为单位)。如果它们不指向同一个数组,或者刚好超过了它的末尾,那么行为是未定义的。p - p,假设p是一个有效的指针,就是0(类型为ptrdiff_t)。

p - (p + 1);

同样有效。 p + 1 指向超出 i 结尾的一个位置,这是被允许的。减法得到类型为 ptrdiff_t-1

推荐阅读:C语言FAQ 的第4节(指针)和第6节(数组和指针)。


1
@user2485710 1) 这不是一个C++问题。2) 如果你在某处读到在C++中,p - 1p指针一起产生一个int的说法,那么你应该更加谨慎地选择你的信息来源。或者,在这种特殊情况下,更加仔细地阅读它们。 - Pascal Cuoq
@Keith Thompson 谢谢,这非常有帮助。也非常感激 comp.lang.c 的参考资料。我很好奇在 i 的结尾之后能指向多远? - Lumbering Lummox
1
@LumberingLummox:指针可能指向数组的末尾(在这种情况下,i被视为一个1元素数组)。这样的指针是有效的,但不能被解引用。 - Keith Thompson
1
@KeithThompson 我刚刚删除了那些,因为它们没有用处,谢谢。 - user2485710
@PascalCuoq 这个问题也可以用于 C++,Keith 的答案仍然是正确的。 - M.M
@MattMcNabb:Pascal的评论是对早先(且不正确)的评论做出的回应,该评论已被删除。 - Keith Thompson

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