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

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

一、内存分配

    在谈述函数调用和返回值问题之前,先来看看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、传值调用函数。在函数中的对形参的修改,不会在函数外体现。

class Student
{
public:
	char Name[10];
	int Age;
};

void Edit(Student stu)
{
	stu.Age = 30;
}
void CMFCTestDlg::OnBnClickedButton3()
{
	Student s;
	sprintf(s.Name, "tom");
	s.Age = 29;

	Edit(s);
	//此处s.Age仍然为29
}


 

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

class Student
{
public:
	char Name[10];
	int Age;
};

void Edit(Student* stu)
{
	Stu->Age = 30;
//或者,下面这种写法也会影响函数外
	Student s;
	sprintf(s.Name, "jerry");
	s.Age = 33;
	*stu = s;

}
void CMFCTestDlg::OnBnClickedButton3()
{
	Student s;
	sprintf(s.Name, "tom");
	s.Age = 29;

	Edit(&s);
	//此处s.Age仍然为30
}


 

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

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);
}


 

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

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);
}


 

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

char *GetString(void)
{
 char p[] = "hello world";
 return p; // 编译器将提出警告
}
void Test4(void)
{
 char *str = NULL;
 str = GetString(); // str 的内容是垃圾
 cout<< str << endl;
}


 

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

char *GetString2(void)
{
 char *p = "hello world";
 return p;
}
void Test5(void)
{
 char *str = NULL;
 str = GetString2();
 cout<< str << endl;
}


 

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

 

 

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值