C++内存管理

1.C++中,内存分为五个区域,堆,栈,自由存储区,全局/静态存储区,常量存储区;

栈,函数调用时,函数内部局部变量的存储单元都可以在栈上创建,函数调用结束时,存储单元被自动释放;由编译器负责;

堆,使用new分配的存储单元,由程序员自己负责delete释放;如果忘记了回收,当程序运行结束后由系统回收这部分内存;

自由存储区,由malloc分配的存储单元,使用free释放;

全局/静态存储区,它们共占一部分内存;

常量存储区,存放常量,内容不允许修改;

2.嵌入式环境中,内存经常成为瓶颈;为了防止程序员不定时的new不定大小的空间,有必要重载new/delete操作符,实现人为的高效的内存管理;

可以重载全局的new/delete操作符;

也可以重载某一个类的new/delete操作符;

如果使用对象数组的话,需要重载new[]/delete[]操作符;

3.几种情况;

1.如果程序中对象的调用关系非常复杂,比如在某个地方做了一个new操作,而在很远的地方也会使用到这个对象,之后很容易忘记该对象是否被释放。这种情况最好重新设计数据结构,从根本上改变;

2.return语句,写错,不要返回指向栈内存的指针以及引用,因为它们会在函数执行结束之后被自动释放;(下面还会详细讨论)

3.使用指针的习惯要好,用前先申请分配,只有判断是否申请成功,成功了则赋初值,使用完了释放,释放后再赋空值;

4.指针与数组的比较

1.数组一般只存在于静态存储区(全局数组)、或者栈中;数组名对应着一块内存,而不是指向一块内存,数组名的地址与容量在生命周期内不变;只有数组的内容可以改变;

2.指针可以随时指向不同类型的内存块;

3.修改内容比较:

char a[]="hello";
char *p="hello";
a[2]='a'; //正确
p[1]='a' //错误,因为p指向的常量字符串,内容不可以被修改;


4.内容复制与比较

指针与数组都不可以直接复制,比如两个数组,a,b,a=b是错误的;复制需要通过strcpy(),比较需要使用strcmp(),这点数组与指针一样;

5.计算大小

sizeof可以计算数组的大小,但是无法计算指针指向地址的大小,C/C++还没有提供这种机制,只能在申请时知道大小;当数组作为函数的参数时,大小自动退化为同类型的指针的大小;

int function(char a[100])
{
cout<<sizeof(a)<<endl;//返回的是4,而非100;(32位系统)
}


5.变量的生存周期

不要返回指向栈内存的指针或者引用,因为它们会在函数执行结束之后被自动释放;

例1:

#include <stdio.h> 
char *returnStr() 
{ 
    char *p="hello world!"; 
    return p; 
} 
int main() 
{ 
    char *str; 
    str=returnStr(); 
    printf("%s\n", str); 
    return 0; 
}

这样是没问题的,因为p指向的是内存中的常量存储区,函数返回的是指向常量存储区的指针;当函数执行结束后,字符串所在的常量字符串的内容不会被回收,因此能够访问;

例2:

#include <stdio.h> 
char *returnStr() 
{ 
    char p[]="hello world!"; 
    return p; 
} 
int main() 
{ 
    char *str; 
    str=returnStr(); 
    printf("%s\n", str); 
    return 0; 
} 

执行错误;函数returnStr中,p是一个数组,而数组一般只存在于全局存储区或者栈中,很明显,函数中的数组属于局部变量,不可能存在于全局存储区中,只能是在内存中的栈中分配的空间;而当函数执行结束后,栈内存是要被回收的,这时候返回的是一个已经被释放了的地址;打印出来的可能是乱码;

例3:

int* func( void )
{
    static int a[10];//如果去掉,static,失败
    ........
    return a;
} 
同上,函数中的数组是在栈上分配的,如果不加static关键字的话,当函数执行结束后,栈空间会被释放,返回的是一个指向被释放了的地址空间;如果加上static关键字的话,数组在静态存储区分配,在程序执行结束后静态存储区的空间才会被释放,因此以上代码是可行的。

例4:

void GetMemory(char *p, int num)
{
 p = (char *)malloc(sizeof(char) *num);
}
void Test(void)
{
 char *str = NULL;
 GetMemory(str, 100); // str 仍然为 NULL
 strcpy(str, "hello"); // 运行错误
}
上面这段代码,函数中p指向的内存空间是在堆上,当函数调用执行结束后申请的空间并没有被释放(实际上,每次执行这个函数都会造成内存泄露,大小就是malloc出来的内存块的大小);出现错误的原因是,在函数内部修改的形参,并没有真正的改变传入的形参的值;

毛病出在函数GetMemory中。编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使 _p = p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p申请了新的内存,只是把_p所指的内存地址改变了,但是p丝毫未变。

实际上,

p=str;
p=(char *)malloc(sizeof(char) * num);
//str并没有改变

如果实在喜欢这种内存分配方式,可以传入二重指针,

void GetMemory2(char **p, int num)
{
 *p = (char *)malloc(sizeof(char) * num);
}
void Test2(void)
{
 char *str = NULL;
 GetMemory2(&str, 100); // 注意参数是 &str,而不是str
 strcpy(str, "hello");
 cout<< str << endl;
 free(str);
}
或者:

char *GetMemory3(int num)
{
 char *p = (char *)malloc(sizeof(char) * num);
 return p;
}
void Test3(void)
{
 char *str = NULL;
 str = GetMemory3(100);
 strcpy(str, "hello");
 cout<< str << endl;
 free(str);
}
例1,例2,例3主要涉及的都是变量的生存周期以及存储方式,例4是由于在函数内部修改的形参,并不能真正的修改传入的形参。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值