我有以下代码段:
#include <iostream>
#include <stdio.h>
void foo(int**p)
{
int y = 5; // memory for this is allocated, say at address 3000, and
// that position of memory is filled with the value 5
*p = &y; //we equal the value (contents) at address 2000 to 3000;
}
int main(int argc, char **argv)
{
int* p;
std::cout << &p << std::endl;
// p points to nowhere right now
foo(&p); //we pass p's address to foo()
printf("%d\n",*p);
std::cout << *p << std::endl;
}
当我运行此代码时,printf输出5,但std :: cout打印无效数据。谁能解释为什么?
答案 0 :(得分:2)
我认为值得深入了解关于C ++的基本误解:
// memory for this is allocated, say at address 3000, and
// that position of memory is filled with the value 5
在大多数编译器将解释代码的方式上,这是“ true”。但是,就C ++语言而言,这是实施细节。
从语言的虚拟机角度,执行以下操作:
*p = &y;
您正在说:“ p
指向的int指针对象现在指向对象y
”。就这样。通过内存寻址完成此操作的事实再次是实现细节。
现在,当y
超出范围时,p
成为不指向有效对象的指针。取消引用此类指针是未定义的行为。
顺便说一句,即使后来有一个新的int
对象被分配到相同的内存位置,也是如此。
关于未定义行为的有趣之处在于,它是编译器的空白。可以假定它永远不会发生,这往往会使您的程序执行似乎毫无意义的指令。
我想我要讲的是:除非您要处理指向char
,unsigned char
或std::byte
的指针,否则从内存地址的角度考虑指针实际上不是要走的路。
答案 1 :(得分:0)
是的,这绝对是指针问题,而不是cout / printf问题。
对于来自其他语言(例如Java)的人,这些人如果您不直接使用指针,可能会造成很多麻烦。您真的需要完全理解它们才能使用它们。在这种情况下,您不仅要处理指针,还要处理指向指针的指针。
在您的特定示例中,方法foo()使用一个指向指针的指针。很好,但是在执行此操作之前,您确实需要充分了解指针。
然后,它为变量y(在堆栈上)分配内存,并填充该内存。然后,将指针设置为指向y。
当您的方法返回时,堆栈将重置,并且堆栈中的所有操作都可能被丢弃。您不知道会发生什么。但是在这种情况下,您转过来调用另一个函数调用printf(),该函数在foo()所处的范围内在堆栈上分配空间,并将新数据放到那里。您的指针现在指向该新数据。
这就是您要打印的内容。
现在,如果您真正想要的是让foo()填写值,那么您真正需要的是:
P应该是一个int,而不是int *,并且它将更改main内部的其他大多数代码。
Foo应该采用int *,而不是int **。
您的最后一行应该是* p = y,而不是* p =&y。
换句话说,您将传递到foo中存储要返回的值的位置,而不是放置地址的位置。
当然,大多数人都会让foo返回您的int值:-)但是我认为您正在尝试更好地理解指针,这就是您这样做的原因。