我现在一直在使用C ++。我只是不确定内存管理是如何工作的,所以在这里:
我首先不确定如何在函数中分配内存,例如:
int addTwo(int num)
{
int temp = 2;
num += temp;
return num;
}
所以在这个例子中,函数结束后是否会从内存中删除?如果没有,这是怎么做的。在C#中,一旦变量用完,变量就会被删除。还有其他我应该了解的案例吗?
由于
答案 0 :(得分:16)
在C ++中有一个非常简单的经验法则:
除非已手动分配,否则所有内存在超出范围时都会自动释放。
手动分配:
C ++中一个非常有用的设计模式叫做 RAII (Resource Acquisition Is Initialization),它将动态分配绑定到一个作用域对象,从而释放析构函数中的分配。
在RAII代码中,您不必再担心调用delete()或free(),因为只要“锚点对象”超出范围,就会自动调用它们。
答案 1 :(得分:11)
这里,temp
被分配在堆栈上,当函数退出时,它自动释放它使用的内存。但是,您可以像这样在堆上分配它:
int *temp = new int(2);
要释放它,你必须这样做
delete temp;
如果在堆栈上分配变量,通常会发生这种情况:
当你调用你的函数时,它会增加这个称为“堆栈指针”的东西 - 一个数字,说明内存中的哪些地址被“保护”以供其局部变量使用。当函数返回时,它会将堆栈指针递减到其原始值。实际上没有对你在该函数中分配的变量进行任何操作,除了它们所在的内存不再受“保护” - 其他任何东西都可以(并最终将)覆盖它们。所以你不应该再访问它们了。
如果在退出函数后需要分配的内存保持不变,请使用堆。
答案 2 :(得分:7)
局部变量temp
在函数开头的“堆栈”上被“推”,并在函数退出时“弹出”堆栈。
以下是非优化版本的反汇编:
int addTwo(int num)
{
00411380 push ebp
00411381 mov ebp,esp //Store current stack pointer
00411383 sub esp,0CCh //Reserve space on stack for locals etc
00411389 push ebx
0041138A push esi
0041138B push edi
0041138C lea edi,[ebp-0CCh]
00411392 mov ecx,33h
00411397 mov eax,0CCCCCCCCh
0041139C rep stos dword ptr es:[edi]
int temp = 2;
0041139E mov dword ptr [temp],2
num += temp;
004113A5 mov eax,dword ptr [num]
004113A8 add eax,dword ptr [temp]
004113AB mov dword ptr [num],eax
return num;
004113AE mov eax,dword ptr [num]
}
004113B1 pop edi
004113B2 pop esi
004113B3 pop ebx
004113B4 mov esp,ebp //Restore stack pointer
004113B6 pop ebp
004113B7 ret
“推”和“弹出”这两个词仅仅是一个类比。从汇编输出中可以看出,编译器通过从堆栈指针中减去一个合适的值,一次性保留局部变量的所有内存。
答案 3 :(得分:6)
一旦函数退出,它就不会从内存中删除。
它保留在内存中,在addTwo的堆栈帧中,直到某个其他进程(或相同的)重新使用该部分内存。
在此之前,访问temp是未定义的行为。
答案 4 :(得分:4)
temp
。所以当函数返回时,它就消失了。
C ++范围规则类似于C#。
答案 5 :(得分:1)
请参阅我对这个问题的回答。这可能会为oyu清理很多东西。
How does automatic memory allocation actually work in C++?
我不只是发布咯咯笑的链接。我的答案是深入了解(在介绍性层面)内存管理的工作原理。
答案 6 :(得分:1)
通常,内存管理在
创建的动态内存环境中使用new
malloc
在普通代码中,C ++的行为与其他语言类似。如果您创建变量或将其返回,则会在目标端复制并访问该变量。
int a = addTwo(3);
获取您返回值的副本。如果返回的值是一个调用的类复制操作符。 因此,只要您不使用new和malloc,就不必那么关心内存管理。
另外一个重要的评论
void func(std::string abc)
{
// method gets a copy of abc
}
void func(std::string& abc)
{
// method gets the original string object which can be modified without having to return it
}
void func(const std::string& abc)
{
// method gets the original string object abc but is not able to modify it
}
这三行的区别非常重要,因为您的程序可能会花费大量时间创建通常不想创建的输入参数副本。
e.g。
bool CmpString(std::string a, std::string b)
{
return a.compare(b);
}
非常昂贵,因为字符串a和b总是被复制。 使用
bool CmpString(const std::string& a, const std::string& b)
代替。
这很重要,因为默认情况下不使用refcounted对象。
答案 7 :(得分:0)
变量temp是堆栈分配的。这意味着它在函数返回时被释放。
参见例如:
答案 8 :(得分:0)
在这种情况下,num和temp都是此函数的本地。调用该函数时,传入num的数字将从调用者复制到堆栈上的变量。然后在堆栈上创建Temp。当你返回时,num的值被复制回调用者,并且函数中使用的temp和num变量被删除。
答案 9 :(得分:0)
在C中,C ++ 局部变量具有自动存储类,并存储在堆栈中。
当函数返回时,堆栈被解开并且本地不再可访问,但它们仍然存在于内存中,这就是当你在函数中定义变量时它可能包含垃圾值的原因。
它只是堆栈中的堆栈指针操作,实际上删除了本地的内存。
答案 10 :(得分:0)
在C ++中,任何作用域中声明的任何对象都将在作用域退出时被删除。在下面的示例中,在声明对象时调用默认构造函数,并在退出时调用析构函数,即~MyClass。
void foo() {
MyClass object;
object.makeWonders();
}
如果在函数中声明指针,那么当范围退出时,指针本身(32位系统的4个字节)会被回收,但是您可能使用operator new或malloc分配的内存将会延迟 - 这通常是称为内存泄漏。
void foo() {
MyClass* object = new MyClass;
object->makeWonders();
// leaking sizeof(MyClass) bytes.
}
如果你真的必须通过new分配一个对象,当它退出范围时需要删除,那么你应该使用boost :: scoped_ptr,如下所示:
void foo() {
boost::scoped_ptr<MyClass> object(new MyClass);
object->makeWonders();
// memory allocated by new gets automatically deleted here.
}