同步博客:My Love
在写C程序的时候,我们很多时候都需要在一个函数的内部和外部进行数据交换,通常我们使用C语言基本类型比如int、char、short、long等时,直接返回函数内创建的这些变量就行啦。但是函数内局部指针和引用变量我们不能直接返回,严重时有可能引起程序崩溃或者内存泄露。这是为什么?
获取函数计算结果的方法
之所以出现上面的问题是因为我们访问到了非法地址导致的程序崩溃,为什么基本类型就可以直接访问但是指针或引用就不行呢,这是因为基本类型在函数返回时进行了副本拷贝,创建了一块内存区域存放这个副本,实际上局部变量的作用域就是函数内部,出了函数就销毁了,正是有了副本拷贝的操作我们才能在函数外部使用返回的那些C语言基本类型的变量。
既然这样为什么指针就不能再函数外继续使用呢?难道指针没有进行副本拷贝?其实指针也是会进行副本拷贝的,跟基本类型一样,只不过由于指针存储地址的特性决定了它不能在函数外继续被正确访问。在返回指针后,函数也会拷贝一个指针的副本返回,然后把函数内的指针指向的内存空间销毁(这里假设指针指向的是数组类型或者用户自己分配后也正确地销毁了),我们捕获到这个返回的指针后,取访问该指针指向的内存区域,但是这块内存区域已经在函数内被释放掉了,这时我们访问的这个内存区域是未初始化的,所以会出现访问非法内存地址的错误,这个错误很严重,直接会导致程序崩溃。
那么我们如果想要得到函数内部的非基本类型的结果要怎么做呢?有三种方法:
函数内分配内存空间
可以在函数内部申请一块内存空间(手动申请的内存空间存在于堆上),然后将计算结果存储到这个内存空间内,返回该内存空间的地址。在函数外获取到函数的返回值后就可以得到该指针指向的内存地址存放的数据。
不过需要注意的一点是在函数外使用完该数据后要记得释放掉该指针指向的内存空间,否则就会引起内存泄露的问题。还有就是在函数外会有多次释放该内存地址的问题,结果就会导致程序崩溃,所以这种方法不推荐使用。
函数内分配静态内存空间
还可以利用static的特性,在函数内部申请一个static标示的地址空间,因为static生命的数据都是存放的全局数据区的,不会因为函数的结束而被释放,所以在函数内给该内存区域赋值后在函数外也是可以直接访问到的。
不过使用这种方法有一个隐患,在多次调用这个函数的情况下,只有最后一次会起作用(排除函数内进行数据算法运算),因为每次调用该函数后申请的那个全局静态区域都会重新赋值,那就会把之前该地址空间存放的内容覆盖掉,这是个需要注意的问题。
函数外分配内存空间,然后作为函数参数传递进函数内部
还有一种方法是在函数外分配好地址空间后,把该地址空间的首地址也就是指针作为函数参数传递到函数内部,在函数内对该地址空间进行赋值操作。这样的话函数结束后该地址空间的内容就可以保留下来供用户使用。
这个方法的不好之处就是比较麻烦,但是优点是降低了错误率,把函数内可能出现的问题规避到了函数外,更容易让用户发现问题。推荐这种方!
总结
返回函数计算结果的方法有三种:
- 函数内分配内存空间, 该方法容易导致内存泄露和程序崩溃,不推荐使用。
- 函数内分配静态内存空间, 在多次调用该方法的情况下需要注意变量内容被覆盖的问题。
- 函数外分配内存空间,然后作为函数参数传递进函数内部,该方法虽然麻烦点,但是错误率比较低,推荐使用。
上述内容参考了左耳朵耗子(陈皓)的文章C/C++返回内部静态成员的陷阱。
本来写过一遍了,放在coding上的虚拟机上,后来就打不开了,再后来就丢了……丢了……了……,所以果断本地搭建一个博客平台,不用coding了。另外,时刻要记住备份,备份,备份!!!
2018-09-19 北京 多云转晴
网友评论