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

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

以下是各个区的作用:

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

(2)数据区

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

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

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

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

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

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

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

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

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

内存分配的问题:

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在静态区

1.test1

#include<iostream>
using namespace std;void test(int *p)
{int b=2;p=&b;cout<<p<<endl;
}int main(void)
{int a=10;int *p=&a;cout<<p<<endl;test(p);cout<<p<<endl;return 0;
}

第一行输出和第三行输出的结果相同,而第一行、第三行与第二行输出的结果不同。从这里可以看出,当指针作为参数进行传递时传递的也只是一个值,只不过该值只一个地址,因此对于形参的改变并不影响实参。

2.test2

#include<iostream>
using namespace std;char* test(void)
{char str[]="hello world!";return str;
}int main(void)
{char *p;p=test();cout<<p<<endl;return 0;
}

输出结果可能是hello world!,也可能是乱麻。

出现这种情况的原因在于:在test函数内部声明的str数组以及它的值"hello world”是在栈上保存的,当用return将str的值返回时,将str的值拷贝一份传回,当test函数执行结束后,会自动释放栈上的空间,即存放hello world的单元可能被重新写入数据,因此虽然main函数中的指针p是指向存放hello world的单元,但是无法保证test函数执行完后该存储单元里面存放的还是hello world,所以打印出的结果有时候是hello world,有时候是乱麻。

3.test3

#include<iostream>
using namespace std;int test(void)
{int a=1;return a;
}int main(void)
{int b;b=test();cout<<b<<endl;return 0;
}

输出结果为 1

有人会问为什么这里传回来的值可以正确打印出来,不是栈会被刷新内容么?是的,确实,在test函数执行完后,存放a值的单元是可能会被重写,但是在函数执行return时,会创建一个int型的零时变量,将a的值复制拷贝给该零时变量,因此返回后能够得到正确的值,即使存放a值的单元被重写数据,但是不会受到影响。

4.test4

#include<iostream>
using namespace std;char* test(void)
{char *p="hello world!";return p;
}int main(void)
{char *str;str=test();cout<<str<<endl;return 0;
}

执行结果是 hello world!

同样返回的是指针,为什么这里会正确地打印出hello world1?这是因为char *p="hello world!",指针p是存放在栈上的,但是"hello world!”是一个常量字符串,因此存放在常量区,而常量区的变量的生存期与整个程序执行的生命期是一样的,因此在test函数执行完后,str指向存放“hello world!”的单元,并且该单元里的内容在程序没有执行完是不会被修改的,因此可以正确输出结果。

5.test5

#include<iostream>
using namespace std;char* test(void)
{char *p=(char *)malloc(sizeof(char)*100);strcpy(p,"hello world");return p;
}int main(void)
{char *str;str=test();cout<<str<<endl;return 0;
}

运行结果 hello world

这种情况下同样可以输出正确的结果,是因为是用malloc在堆上申请的空间,这部分空间是由程序员自己管理的,如果程序员没有手动释放堆区的空间,那么存储单元里的内容是不会被重写的,因此可以正确输出结果。

6.test6

#include<iostream>
using namespace std;void test(void)
{char *p=(char *)malloc(sizeof(char)*100);strcpy(p,"hello world");free(p);if(p==NULL){cout<<"NULL"<<endl;}
}int main(void)
{test();return 0;
}

没有输出

在这里注意了,free()释放的是指针指向的内存!注意!释放的是内存,不是指针!这点非常非常重 要!指针是一个变量,只有程序结束时才被销毁。释放了内存空间后,原来指向这块空间的指针还是存在!只不过现在指针指向的内容的垃圾,是未定义的,所以说是垃圾。因此,释放内存后应把把指针指向NULL,防止指针在后面不小心又被使用,造成无法估计的后果。

C++中内存分配、函数调用和返回值问题相关推荐

  1. 浅谈C++中内存分配、函数调用和返回值问题

    在谈述函数调用和返回值问题之前,先来看看C++中内存分配的问题. C++编译器将计算机内存分为代码区和数据区,很显然,代码区就是存放程序代码,而数据区则是存放程序编译和执行过程出现的变量和常量.数据区 ...

  2. C++中内存分配方式、空指针及野指针的区别

    一.C++中内存分配方式可以分为三种: (1)从静态存储区域分配: 内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在.速度快.不容易出错,因为有系统会善后.例如全局变量,static变 ...

  3. python中func函数可以没有返回值吗_python让函数不返回结果的方法

    1.简单介绍print和return的区别,print仅仅是打印在控制台,而return则是将return后面的部分作为返回值:作为函数的输出,可以用变量接走,继续使用该返回值做其它事. 2.函数需要 ...

  4. TabHost中使用startActivityForResult无法接收返回值的解决方案

    TabHost中使用startActivityForResult无法接收返回值的解决方案 参考文章: (1)TabHost中使用startActivityForResult无法接收返回值的解决方案 ( ...

  5. 【kernel 中内存分配那点事】

    首先呢作为车载bsp开发人员,写大量的内核代码是不现实的事情,多数都是修修改改,但是要有内核代码阅读浏览理解的能力,毕竟linux kernel 还是很nb 的,所有技术人员深入研究内核代码是必须的, ...

  6. php mysql返回行数_PHP中mysqli_affected_rows作用行数返回值分析

    本文实例分析了PHP中mysqli_affected_rows作用行数返回值.分享给大家供大家参考.具体分析如下: mysqli中关于update操作影响的行数可以有两种返回形式: 1. 返回匹配的行 ...

  7. matlab .m 返回值,MATLAB一个M文件的function返回值怎么在另一个M文件中的函数调用这个返回值?...

    答:1.首先要在笔记本电脑上安装好指定版本的matlab软件并双击打开. 2.然后双击matlab图标打开其主页面,可以看到这个软件的按钮都是全英文单词的. 3.那么创建m文件的方法就是鼠标移动到Ne ...

  8. Linux内核中内存分配函数

    1.原理说明 Linux内核 中采 用了一种同时适用于32位和64位系统的内 存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系 统中,用到了四级页表,如图2-1所示.四级页表分别为 ...

  9. python 函数变量_Python函数中的变量和函数返回值

    1.函数的变量 局部变量和全局变量: Python中的任何变量都有特定的作用域 在函数中定义的变量一般只能在该函数内部使用,这些只能在程序的特定部分使用的变量我们称之为局部变量 在一个文件顶部定义的变 ...

最新文章

  1. Asp.Net下的DataGrid的多层表头
  2. MIT 的新型开源系统 Taco 将数据分析速度提升 100 倍 !(附论文)
  3. node命令错误--nodemon : 无法将“nodemon”项识别
  4. mysql 主从同步 阻塞_如何解决主从数据库同步延迟问题?
  5. 阿里巴巴宣布成立半导体公司,要自主研发芯片
  6. sql游标循环结果集
  7. python 批量修改文件夹和子文件夹的名称
  8. PowerPoint 消除所有动画VBA指令
  9. 服务器raw格式硬盘,硬盘分区格式变为RAW
  10. 2019全球人工智能技术大会在宁举办
  11. 期货投资盈亏比怎么算(期货盈亏怎么算的)
  12. 【布局优化】基于Memetic算法求解集群无线传感器网络中节能覆盖控制优化问题含Matlab源码
  13. cocos creator 2.4.6 加载json文件 初始化游戏 - 初学者
  14. [收藏]《观察与思考》:相信中国,寻找下一个比尔·盖茨
  15. PROTOTEX: Explaining Model Decisions with Prototype Tensors论文解读
  16. 因为有了老狼,老虎,我们不在是兔子.
  17. 程序员的鄙视链-------哈哈哈,真逗
  18. vite+vue3打包后图片404问题:已解决
  19. 来自Gartner 中国分析师的最新见解
  20. 在Linux中利用backtrace信息解决程序崩溃问题

热门文章

  1. Redhat、CentOS进单用户模式进行维护
  2. WINCE之“系统事件”——System/Events
  3. Camera 涉及的文件70
  4. WINCE6.0文件系统及存储管理器
  5. Android6.0以上的权限问题
  6. 用mansard对cell的子控件设置约束,并且自动计算cell高度的问题,ios7警告
  7. Openstack Tempest测试入门
  8. libpython2.7.so.1.0 cannot open的解决方法
  9. UCenter实现同步登陆原理
  10. RHEL 6 关闭ThinkPad 触摸板