我有下面的代码。
#include <iostream>
int * foo()
{
int a = 5;
return &a;
}
int main()
{
int* p = foo();
std::cout << *p;
*p = 8;
std::cout << *p;
}
并且代码正在运行,没有运行时异常!
输出是58
怎么可能呢?局部变量的内存在其函数之外不是不可访问的吗?
我有下面的代码。
#include <iostream>
int * foo()
{
int a = 5;
return &a;
}
int main()
{
int* p = foo();
std::cout << *p;
*p = 8;
std::cout << *p;
}
并且代码正在运行,没有运行时异常!
输出是58
怎么可能呢?局部变量的内存在其函数之外不是不可访问的吗?
当前回答
永远不要通过访问无效内存来抛出c++异常。您只是给出了一个关于引用任意内存位置的一般概念的示例。我也可以这样做:
unsigned int q = 123456;
*(double*)(q) = 1.2;
在这里,我简单地将123456作为double类型的地址,并对其进行写入。任何事情都可能发生:
Q实际上可能是double的有效地址,例如double p;Q = &p; q可能指向已分配内存中的某个地方,我只是在那里覆盖了8个字节。 Q指向分配的内存之外,操作系统的内存管理器向我的程序发送了一个分割错误信号,导致运行时终止它。 你中了彩票。
你设置它的方式是更合理的,返回的地址指向内存的有效区域,因为它可能只是在堆栈的下一点,但它仍然是一个无效的位置,你不能以确定的方式访问。
在正常的程序执行期间,没有人会自动检查内存地址的语义有效性。但是,像valgrind这样的内存调试器很乐意这样做,所以您应该通过它运行程序并观察错误。
其他回答
它之所以能工作,是因为自从a被放入堆栈以来,堆栈(还没有)被改变过。 在再次访问a之前调用一些其他函数(它们也调用其他函数),你可能不会再那么幸运了……: -)
从函数返回后,所有标识符都将被销毁,而不是将值保存在内存位置,如果没有标识符,则无法定位值。但该位置仍然包含前一个函数存储的值。
因此,这里函数foo()返回a的地址,a在返回地址后被销毁。您可以通过返回的地址访问修改后的值。
让我举一个真实的例子:
假设一个人把钱藏在一个地方,并告诉你这个地方。过了一段时间,告诉你钱的位置的人死了。但你仍然可以拿到那些隐藏的钱。
因为存储空间还没有被践踏。不要指望那种行为。
在典型的编译器实现中,您可以将代码视为“打印出内存块的值,该值带有曾经被a占用的地址”。此外,如果您将一个新的函数调用添加到一个约束局部整型的函数中,则a的值(或a所指向的内存地址)很有可能发生变化。这是因为堆栈将被包含不同数据的新帧覆盖。
然而,这是未定义的行为,你不应该依赖它来工作!
您实际上调用了未定义的行为。
返回临时作品的地址,但由于临时作品在函数的末尾被销毁,访问它们的结果将是未定义的。
因此,您没有修改a,而是修改了a曾经所在的内存位置。这种差异与崩溃和不崩溃之间的差异非常相似。