我对C ++中的内存处理有几个问题。
Mystruct *s = new Mystruct
和Mystruct s
的不同之处是什么?记忆中会发生什么?
查看此代码:
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()
结束时{{1}}自动免费?
谢谢!
答案 0 :(得分:8)
当您使用new
关键字获取指针时,您的结构将在堆上分配,以确保它将在应用程序的生命周期内持续存在(或直到它被删除)。
如果不这样做,结构将在堆栈上分配,并在分配范围终止时销毁。
我对你的例子的理解(如果我错了,请不要犹豫,告诉我),
tmp
确实会在函数末尾被“释放”(不是堆栈变量的最佳单词选择),因为它是在堆栈上分配的并且堆栈帧已经丢失。你返回的指针/内存地址已经没有任何意义了,如果代码有效,你基本上就幸运了(还没有覆盖旧数据)。
答案 1 :(得分:3)
对于问题1,您正在查看堆内存和堆栈内存。简而言之,
Mystruct S;
在堆栈上创建S.当S超出范围时,它将被销毁。因此,如果S在函数内,当函数返回时,S被销毁。
尽管
MyStruct *S = new MyStruct();
在堆上。它是为程序存储变量而预留的一块内存,S将存储指向新MyStruct的起始内存块的指针。它会一直在堆里,直到你释放它;如果你的程序结束时没有释放它,你会得到恶意的内存泄漏。
问题2 - 当函数退出时,本地MyStruct被销毁;指向其返回值的MyStruct指针指向未定义的区域。它可能仍然有效,因为操作系统尚未回收内存,但它绝对不是正确的行为 - 或者是安全的事情。
答案 2 :(得分:1)
首先:
Mystruct* s = new Mystruct;
new Mystryct
部分在堆上为该类型的对象分配内存。在C ++中,它还将执行该类型的默认构造函数。 Mystruct* s
部分声明一个指针变量,该变量指向新分配的对象内存的第一个字节的地址。
第二
Mystruct s;
它的作用与第一个有两个不同,可以简化为:对象的已分配内存在堆栈上,并且没有指向内存的指针变量,而是s
是那个对象。该对象的地址为&s
,因此指向对象s
的指针应分配值&s
。
为什么MyStruct tmp在create()结束时不会自动释放?
确实如此。 tmp
析构函数在 return
语句之后运行,因此函数返回的地址将是一个很快被其他东西覆盖的内存,这最多会导致分段错误(或等效的),最坏的情况是破坏您的数据。
答案 3 :(得分:1)
您的两个问题都涉及存储时间和范围。
首先,当您动态分配对象时,该对象及其指针在您释放之前一直有效。如果它是一个自动变量(即,不是由new
,malloc
等动态分配,而不是声明static
),那么一旦对象的范围,变量就会超出范围结束(通常是}
与定义对象的“级别”相同的“级别”。它还具有“自动存储持续时间”,这意味着当对象不在范围内时,它的存储也会消失。
对于第二个问题,tmp
的范围以}
的结尾create
结尾。它也具有相同的存储持续时间。指向tmp
的指针仅在该存储持续时间内有效。退出create()
后,指向tmp
的指针将变为无效,无法使用。
答案 4 :(得分:0)
Mystruct * s = new Mystruct;
在堆上动态分配s。它不会自动释放。 (另外,s是指针,不是直接的Mystruct)。
Mystruct s;
在堆栈上静态分配s。当它超出范围时,它将被“释放”。*
您的代码无效。当你在create之外引用tmp时,你正在使用一个野指针访问死记忆。这会导致未定义的行为。
答案 5 :(得分:0)
<强> Q1:强>
Mystruct *s = new Mystryct;
在堆上创建结构变量,该变量由变量s指向。
Mystruct s;
这里的结构是在堆栈上创建的。
<强> Q2:强>
MyStruct *create(){ MyStruct tmp; tmp.i = 1337; return &tmp; }
错了!!您正在堆栈上创建一个本地结构变量,当函数返回并且对它的任何引用都无效时,它会消失。你应该动态地分配变量,并且必须在以后用main手动解除分配。
答案 6 :(得分: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。
答案 7 :(得分:0)
在Mystruct *s = new Mystruct
中,有一个静态变量,指针,在堆栈上的函数调用开始时分配。当这一行运行时,它在堆上分配struct。在Mystruct s
中,它在堆栈上分配结构,“静态地”在函数运行时(构造函数,如果有的话,将在decleration行中运行)。
“为什么MyStruct tmp在create()结束时不会自动释放?” - 好吧,是的。但是,内存仍然存在,因此您可以访问它,可能包含旧值。
答案 8 :(得分: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,你还应该阅读有关autopointer等内容。 C ++中没有内存的垃圾收集。 我认为Thinking C++很好地解释了动态记忆的概念(第13章)。