4

我有几个关于 C++ 中的内存处理的问题。

  1. Mystruct *s = new Mystruct和有什么不同Mystruct s?记忆中发生了什么?

  2. 看这段代码:

    struct MyStruct{
        int i;
        float f;
    };
    
    MyStruct *create(){
        MyStruct tmp;
        tmp.i = 1337;
        tmp.j = .5f;
        return &tmp;
    }
    
    int main(){
        MyStruct *s = create();
        cout << s->i;
    
        return 0;
    }
    

什么时候MyStruct tmp免费?为什么最后没有MyStruct tmp自动释放create()

谢谢!

4

9 回答 9

8

当您使用new关键字获取指针时,您的结构被分配在堆上,以确保它将在您的应用程序的生命周期内持续存在(或直到它被删除)。

如果不这样做,则该结构将在堆栈上分配,并且在分配它的范围终止时将被销毁。

我对您的示例的理解(如果我错了,请随时通知我,任何人):

tmp确实会在函数结束时被“释放”(不是堆栈变量的最佳词选择),因为它是在堆栈上分配的并且该堆栈帧已经丢失。您返回的指针/内存地址不再具有任何意义,如果代码有效,您基本上只是幸运(还没有覆盖旧数据)。

于 2010-02-04T08:15:18.550 回答
3

对于问题 1,您正在查看堆内存和堆栈内存。简而言之,

Mystruct S;

在堆栈上创建 S。当 S 超出范围时,它将被销毁。因此,如果 S 在函数内部,则当函数返回时,S 被销毁。

然而

MyStruct *S = new MyStruct();

在堆上。它是一块留给程序存储变量的内存块,S 将存储一个指向新 MyStruct 的起始内存块的指针。在你释放它之前,它总是在堆中;如果你在程序结束时不释放它,你就会得到严重的内存泄漏。

关于问题 2 - 本地 MyStruct 在函数退出时被销毁;指向其返回值的 MyStruct 指针指向未定义的区域。它可能仍然有效,因为操作系统尚未回收内存,但这绝对不是正确的行为 - 或者是安全的事情。

于 2010-02-04T08:14:48.187 回答
1

第一的:

Mystruct* s = new Mystruct;

new Mystryct部分在堆上为该类型的对象分配内存。在 C++ 中,它还将执行该类型的默认构造函数。该Mystruct* s部分声明了一个指针变量,指向新分配的对象内存的第一个字节的地址。

第二:

Mystruct s;

它将与第一个相同,但有两个不同之处,可以简化为:为对象分配的内存在堆栈上,并且没有指向内存的指针变量,而是s 那个对象。该对象的地址是&s,因此指向该对象的指针s应被赋值&s

为什么 MyStruct tmp 在 create() 结束时不会自动释放?

确实如此。tmp析构函数在语句之后运行,因此函数return返回的地址将存储到很快会被其他内容覆盖的内存中,这充其量会导致分段错误(或同等情况),最坏的情况是破坏您的数据。

于 2010-02-04T08:17:56.383 回答
1

您的两个问题都涉及存储持续时间和范围。

首先,当您动态分配一个对象时,该对象和指向它的指针在您释放它之前都是有效的。如果它是一个自动变量(即,不是由new,malloc等动态分配的,并且没有声明static),则该变量会在对象的范围结束时立即超出范围(通常}与对象是定义的)。它还具有“自动存储持续时间”,这意味着当对象不在范围内时,它的存储也会消失。

对于您的第二个问题,tmp范围}create. 它也具有相同的存储期限。指向的指针tmp仅在该存储期限内有效。一旦create()退出,指向的指针tmp就失效了,无法使用。

于 2010-02-04T08:36:18.080 回答
0

Mystruct *s = new Mystruct;

在堆上动态分配 s。它不会被自动释放。(另外,s 是一个指针,而不是直接的 Mystruct)。

我的结构;

在堆栈上静态分配 s。当它超出范围时,它将被“释放”。*

您的代码无效。当您在 create 之外引用 tmp 时,您使用的是野指针来访问死内存。这会导致未定义的行为。

  • 差不多。超出范围使用它是未定义的行为,即使该值仍在内存中。
于 2010-02-04T08:15:51.857 回答
0

Q1:

Mystruct *s = new Mystryct;

在堆上创建结构变量,该变量由变量 s 指向。

Mystruct s;

这里的结构是在堆栈上创建的。

Q2:

MyStruct *create(){ MyStruct tmp; tmp.i = 1337; return &tmp; }

是错的!!您正在堆栈上创建一个局部结构变量,该变量在函数返回时消失并且对它的任何引用都将无效。您应该动态分配变量,并且稍后在 main 中手动释放它。

于 2010-02-04T08:16:25.333 回答
0

Mystruct *s = new Mystryct在堆上分配。
需要明确删除它。
Mystruct s在堆栈上分配结构。
当堆栈展开时它会自动被释放(变量超出范围),因此对于案例 2,一旦 create() 退出,tmp 就会被释放。所以你将返回的是一个dangling pointer which is very dangerous.

如果太难,请遵循这个经验法则,
For every new operator called, delete must be called in the end.
For every new[] operator called, delete[] must be called in the end.

使用智能指针自动删除分配的内存而不是普通指针。如果您从 create 中的函数返回对象,请确保使用 new 运算符分配对象,而不是像示例中所做的那样在堆栈上分配对象。确保调用者在完成后调用指针上的 delete。

于 2010-02-04T08:18:37.667 回答
0
  1. 其中Mystruct *s = new Mystruct有一个静态变量,指针,它是在堆栈上的函数调用开始时分配的。当这条线运行时,它在堆上分配结构。它在Mystruct s堆栈上分配结构,当函数运行时“静态”(构造函数,如果有的话,将在 decleration 行中运行)。

  2. “为什么 MyStruct tmp 在 create() 结束时不会自动释放?” - 嗯,是的。但是,内存仍然存在,因此您可以访问它,它可能包含旧值。

于 2010-02-04T08:18:52.967 回答
0
struct MyStruct{ int i; };

MyStruct create(){ MyStruct tmp; tmp.i = 1337; return tmp; }

int main(){

  MyStruct s = create();
  cout << s.i;

  return 0;
}

或者

struct MyStruct{ int i; };

MyStruct* create(){ MyStruct* tmp = new MyStruct; tmp->i = 1337; return tmp; }

int main(){

  MyStruct* s = create();
  cout << s->i;
  delete s;    

  return 0;
}

会工作。因为在第一种情况下,复制构造函数会在将结构分配给 s 时创建结构的副本。整个新/删除的东西(动态内存分配)属于 C++ 的基础。您不必使用任何 new 或 delete 来实现基本算法。复制构造函数等将始终完成工作并使代码更易于理解。

如果您想使用 new ,您还应该阅读有关自动指针等的内容。C++ 中没有内存垃圾回收。我认为Thinking C++很好地解释了动态内存的概念(第 13 章)。

于 2010-02-04T08:23:06.983 回答