C++ 中返回 *ptr 和 &x 的区别

Posted

技术标签:

【中文标题】C++ 中返回 *ptr 和 &x 的区别【英文标题】:Difference between returning *ptr and &x in C++ 【发布时间】:2017-10-07 16:23:27 【问题描述】:

我有一个简单的程序说:

#include <iostream>
using namespace std;

int* foo()
    int x=5;
    return &x;

int main() 
    int* p=foo();
    cout<<*p;

这个程序给出一个返回局部变量地址的警告。如果我尝试运行它,我会收到分段错误。到现在都还好。

现在这是另一个程序:

#include <iostream>
using namespace std;

int* foo()
    int x=5;
    int* ptr=&x;
    return ptr;

int main() 
    int* p=foo();
    cout<<*p;

在这种情况下,我构建它时没有警告。为什么?

另外,当我运行它时,没有分段错误。我得到 5 作为输出。为什么?

是什么让第二个程序完美运行而第一个程序却失败了?

【问题讨论】:

并不要求所有编译器都能为您找到所有错误。当您做明显非法的事情时,并不要求所有编译器都生成相同的行为。事实上,两者的情况正好相反,它被称为“未定义的行为”是有原因的。 这些都是故障代码。它们都应该崩溃,但如果一个没有崩溃,则可能是以下因素的组合:编译器在每种情况下生成的内容不同以及纯属运气。 不,你不能说他们应该崩溃。这就是 UB(未定义行为)的全部意义所在。如果你做非法的事情,你不知道会发生什么,因为 c++ 的设计目的不是为了保护你自己而浪费内存和 cpu 周期。 【参考方案1】:

在第一种情况下,编译器不仅检测到非法行为——返回一个局部变量的地址——它还认识到这是未定义的行为,并简单地将其优化掉。所以它故意返回了一个无效的指针。

在第二种情况下,编译器没有意识到这是未定义的行为并保留了它。您仍在使用此时已被释放的变量,只是它的工作是偶然的,因为它还没有被覆盖。

【讨论】:

@HarshBhardwaj 这种行为并非纯粹是随机的。如果这就是你所说的“偶然”。您选择了一个编译器,该编译器一旦确定就选择优化未定义的行为,这只是“偶然”。 答案中的“保留”是什么意思?如果我指向一个在函数结束后被释放的变量,我无法理解为什么它会打印 5。 @HarshBhardwaj “释放”时不会删除内存。重复使用它只是“免费”的。在这种情况下,这是堆栈上的内存,它将被下一个函数调用重新使用。在这个简单的例子中,returnpmain 范围内的引用之间没有函数调用,所以它还没有被覆盖。 这很好解释。非常感谢!【参考方案2】:

您需要了解指针和内存的工作原理。所有这些情况都是无效的,如果它们起作用,那是因为编译器的启发式以正确的方式编译了它。所有局部变量都在堆栈上声明,然后它们超出范围(即您的函数退出)堆栈内存被自动释放,并且在将新内容写入堆栈时将被覆盖(这就是您得到 seg-fault 的原因,因为它已检测到属于其他内容的内存的无效内存访问)。该函数起作用的原因是内存中存储 5 的位置尚未被覆盖。

如果你想返回一个指针,你必须使用 new 来声明 int。这将在堆上声明它,并且只有在您手动执行此操作时才会释放内存。然后,您必须使用 delete 来释放内存,以避免在完成后发生内存泄漏。

编译器选择一个无效案例而不是另一个的唯一原因是因为它是合法的 c 代码,但它可以告诉你可能的意图,而另一个它不能。

【讨论】:

以上是关于C++ 中返回 *ptr 和 &x 的区别的主要内容,如果未能解决你的问题,请参考以下文章

C 和 C++ 关于 ++ 运算符的区别

C++ 二叉搜索树的 删除算法

unique_ptr<T> 和 unique_ptr<T>&& 之间的区别 [重复]

C++ 函数返回与函数设置指针

C++ 中 make_shared 和普通 shared_ptr 的区别

C++ shared_ptr&&weak_ptr的简单介绍和仿写