C++中函数调用中的内存分配

转自:http://blog.csdn.net/zztfj/article/details/7987645

一、内存分配

    在谈述函数调用和返回值问题之前,先来看看C++中内存分配的问题。

    C++编译器将计算机内存分为代码区和数据区。很显然,代码区就是存放程序代码,而数据区则是存放程序编译和执行过程出现的变量和常量。数据区又分为静态数据区、动态数据区,动态数据区包括堆区和栈区。

以下是各个区的作用:

(1)代码区:存放程序代码;

(2)数据区

   a.静态数据区: 在编译器进行编译的时候就为该变量分配的内存,存放在这个区的数据在程序全部执行结束后系统自动释放,生命周期贯穿于整个程序执行过程。

   b.动态数据区:包括堆区和栈区

    堆区:这部分存储空间完全由程序员自己负责管理,它的分配和释放都由程序员自己负责。这个区是唯一一个可以由程序员自己决定变量生存期的区间。可以用malloc,new申请对内存,并通过free和delete释放空间。如果程序员自己在堆区申请了空间,又忘记将这片内存释放掉,就会造成内存泄露的问题,导致后面一直无法访问这片存储区域。

    栈区:存放函数的形式参数和局部变量,由编译器分配和自动释放,函数执行完后,局部变量和形参占用的空间会自动被释放。效率比较高,但是分配的容量很有限。

 

注意:1)全局变量以及静态变量存放在静态数据区;

2)注意常量的存放区域,通常情况下,常量存放在程序区(程序区是只读的,因此任何修改常量的行为都是非法的),而不是数据区。有的系统,也将部分常量分配到静态数据区,比如字符串常量(有的系统也将其分配在程序区)。但是要记住一点,常量所在的内存空间都是受系统保护的,不能修改。对常量空间的修改将造成访问内存出错,一般系统都会提示。常量的生命周期一直到程序执行结束为止。

 

内存分配的举例:

int a=1;        //a在栈区

char s[]="123";     //s在栈区,“123”在栈区,其值可以被修改

char *s="123";     //s在栈区,“123”在常量区,其值不能被修改

int *p=new int;    //p在栈区,申请的空间在堆区(p指向的区域)

int *p=(int *)malloc(sizeof(int)); //p在栈区,p指向的空间在堆区

static int b=0;     //b在静态区

 

二、函数调用

   在弄懂内存分配的问题过后,来看看函数调用的过程:

执行某个函数时,如果有参数,则在栈上为形式参数分配空间(形参是引用类型的参数则除外;如果形参是对象则调用其“拷贝构造函数”给形参分配空间和赋值),继续进入到函数体内部,如果遇到变量,则按情况为变量在不同的存储区域分配空间(如果是static类型的变量,则是在进行编译的过程中已经就分配了空间),函数内的语句执行完后,如果函数没有返回值,则直接返回调用该函数的地方(即执行远点),如果存在返回值,则先将返回值进行拷贝(如果是对象则调用其“拷贝构造函数”)传回,再返回执行远点,函数全部执行完毕后,进行退栈操作,将刚才函数内部在栈上申请的内存空间释放掉。

 

下面通过几个例子来谈谈内存分配和函数返回值的问题:

1、传值调用函数。在函数中的对形参的修改,不会在函数外体现。

[cpp]  view plain copy
  1. class Student  
  2. {  
  3. public:  
  4.     char Name[10];  
  5.     int Age;  
  6. };  
  7.   
  8. void Edit(Student stu)  
  9. {  
  10.     stu.Age = 30;  
  11. }  
  12. void CMFCTestDlg::OnBnClickedButton3()  
  13. {  
  14.     Student s;  
  15.     sprintf(s.Name, "tom");  
  16.     s.Age = 29;  
  17.   
  18.     Edit(s);  
  19.     //此处s.Age仍然为29  
  20. }  


 

2、传址调用函数。在函数中的对形参的修改,会在函数外体现。

[cpp]  view plain copy
  1. class Student  
  2. {  
  3. public:  
  4.     char Name[10];  
  5.     int Age;  
  6. };  
  7.   
  8. void Edit(Student* stu)  
  9. {  
  10.     Stu->Age = 30;  
  11. //或者,下面这种写法也会影响函数外  
  12.     Student s;  
  13.     sprintf(s.Name, "jerry");  
  14.     s.Age = 33;  
  15.     *stu = s;  
  16.   
  17. }  
  18. void CMFCTestDlg::OnBnClickedButton3()  
  19. {  
  20.     Student s;  
  21.     sprintf(s.Name, "tom");  
  22.     s.Age = 29;  
  23.   
  24.     Edit(&s);  
  25.     //此处s.Age仍然为30  
  26. }  


 

3、用指针参数去申请内存,那么应该改用“指向指针的指针”

[cpp]  view plain copy
  1. void GetMemory2(char **p, int num)  
  2. {  
  3.  *p = (char *)malloc(sizeof(char) * num);  
  4. }  
  5. void Test2(void)  
  6. {  
  7.  char *str = NULL;  
  8.  GetMemory2(&str, 100); // 注意参数是 &str,而不是str  
  9.  strcpy(str, "hello");  
  10.  cout<< str << endl;  
  11.  free(str);  
  12. }  


 

4、用函数返回值来传递动态内存。这种方法更加简单和容易理解

[cpp]  view plain copy
  1. char *GetMemory3(int num)  
  2. {  
  3.  char *p = (char *)malloc(sizeof(char) * num);  
  4.  return p;  
  5. }  
  6. void Test3(void)  
  7. {  
  8.  char *str = NULL;  
  9.  str = GetMemory3(100);  
  10.  strcpy(str, "hello");  
  11.  cout<< str << endl;  
  12.  free(str);  
  13. }  


 

5、用函数返回值来传递动态内存这种方法虽然好用,但是常常有人把return语句用错了。这里强调不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡。

[cpp]  view plain copy
  1. char *GetString(void)  
  2. {  
  3.  char p[] = "hello world";  
  4.  return p; // 编译器将提出警告  
  5. }  
  6. void Test4(void)  
  7. {  
  8.  char *str = NULL;  
  9.  str = GetString(); // str 的内容是垃圾  
  10.  cout<< str << endl;  
  11. }  


 

6、用函数返回值来返回“静态存储区”的指针。

[cpp]  view plain copy
  1. char *GetString2(void)  
  2. {  
  3.  char *p = "hello world";  
  4.  return p;  
  5. }  
  6. void Test5(void)  
  7. {  
  8.  char *str = NULL;  
  9.  str = GetString2();  
  10.  cout<< str << endl;  
  11. }  


 

上面示例6运行虽然不会出错,但是函数GetString2的设计概念却是错误的。因为GetString2内的“hello world”是常量字符串,位于静态存储区,它在程序生命期内恒定不变。无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值