指针运算:C 程序中的奇怪结果

Pointer arithmetics: strange result in C programm

#include<cstdio>
int main(){
    int a=10,b=20;
    int *p=&a;
    *(p-1)=100;
    printf("%d, %d, %d\n",a,b,*(p-1));
//  printf("%d, %d, %d\n",&a,&b,(p-1));
    return 0;
}

为什么第一个 printf(第 6 行)显示的结果与第二个(第 7 行)是否在注释中不同? 我正在使用 C 编译器(TDM-GCC 4.8.1 64 位)

您的代码将 p 指向的内容递减并在其中存储 100。由于 p 被初始化为 a 的地址,您将 100 存储在某个随机内存地址。我想你想要:

*p = 100;

p 不指向数组,因此表达式 *(p-1) 具有 undefined behaviour。这意味着,一旦 *(p-1)=100 被执行,从技术上讲,程序可以以任何它喜欢的方式运行。

实践中可能发生的情况是您覆盖了堆栈中的一些内存,这可能会导致各种副作用。

如果您 Valgrind 您的代码,该工具将对此进行标记。

您正在尝试修改对象 a 之外的内存位置,指针 p 就是从该对象派生的。结果是未定义的行为,这意味着任何结果都是可能的,甚至是看似不可能的结果。

对您的问题给出明确答案的唯一方法是检查编译器的汇编程序输出(如果可以获取此类输出)或生成的机器代码。在这种情况下,对象 a 通常 分配在堆栈上,并且另一个 printf 的存在调用 可能 更改了编译器在同一代码块内分配堆栈项目的方式。

@valtah 和@NPE 的回复是正确的。如果你更详细地了解正在发生的事情,你就会明白为什么:

如果我们查看此执行的堆栈帧(或激活记录):

----------------------------------------------------------
   SP| saved state | return address |  a   |   b  |  p  |
-----------------------------------------------------|----
                                       ^             |
                                       |             |
                                       .-------------'
                                      &a

你会看到 &a 是 a 在堆栈上存储的地址,它也被分配给 p。当您使用值 (p-1) 时,它可能会计算堆栈中其他地方的位置。 (一个好的编译器会知道它不是数组指针并在此时给出语义错误。)许多编译器只会计算地址。当您为其分配一个值时,您可能正在将一项的值从 a 的地址中移开。这可能是 b,也可能是 return 地址 。这会改变程序的执行状态。

这正是代码注入攻击用来控制系统的机制。这是安全违规代码。

现在想到了这个问题。那个代码是从哪里来的?你是在写它,还是在某个地方读过它。它肯定会告诉专家您正在使用的代码类型...

:-)