指针运算: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 地址 。这会改变程序的执行状态。
这正是代码注入攻击用来控制系统的机制。这是安全违规代码。
现在想到了这个问题。那个代码是从哪里来的?你是在写它,还是在某个地方读过它。它肯定会告诉专家您正在使用的代码类型...
:-)
#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 地址 。这会改变程序的执行状态。
这正是代码注入攻击用来控制系统的机制。这是安全违规代码。
现在想到了这个问题。那个代码是从哪里来的?你是在写它,还是在某个地方读过它。它肯定会告诉专家您正在使用的代码类型...
:-)