C函数的返回值
首先看一段有问题的代码:
1 | #include <stdio.h> |
在编译时就给出了警告:
1 | In function 'string_copy': |
buffer是一个自动分配内存的数组,是该函数的局部变量。当控制流离开声明局部变量的范围时,自动变量便自动失效。这就意味着即使返回一个指向局部变量的指针,当函数结束时,由于该变量已被销毁,谁也不知道这个指针所指向的地址的内容是什么。
在C语言中,自动变量在堆栈中分配内存。当包含自动变量的函数或代码块退出时,它们所占用的内存便被回收,它们的内容肯定会被下一个所调用的函数覆盖。这一切取决于堆栈中先前的自动变量位于何处,活动函数声明了什么变量,写入了什么内容等。原先自动变量地址的内容可能被立即覆盖,也可能稍后才被覆盖,这就是问题难以被发现的原因。
然而,无论是编译器还是lint程序都无法检测到局部数组返回的所有情况(它有可能通过某一层间接形式存在躲过检查)。
解决这个问题有几种方案:
1.函数可以返回一个常量,或指向常量的指针。
例如:
1 | int func(){ |
这是最简单的解决方案,但是如果是其他需要返回变化的内容时,这就无能为力了。
2.使用全局声明的变量。
例如:
1 | char my_global_array[120]; |
这适用于自己创建字符串的情况,也很简单易用。它的缺点在于任何人都有可能在任何时候修改这个全局数组,而且该函数的下一次调用也会覆盖该数组的内容。
3.使用静态数组。
例如:
1 | char * func(){ |
这就可以防止任何人修改这个数组。只有拥有指向该数组的指针函数(通过参数传递给它)才能修改这个静态数组。但是,该函数的下一次调用将覆盖这个数组的内容,所以调用者必须在此之前使用或备份数组的内容。和全局数组一样,大型缓冲区如果闲置不用是非常浪费内存空间的。
4.显式分配一些内存,保存返回值。
例如:
1 | char * func(){ |
这个方法具有静态数组的优点,而且在每次调用时都创建一个新的缓冲区,所以该函数以后的调用不会覆盖以前的返回值。它适用于多线程的代码(在某一时刻具有超过一个的活动线程的程序)。它的缺点在于程序员必须承担内存管理的责任。根据程序的复杂程度,这项任务可能很容易,也可能很复杂.如果内存仍在使用就释放或者出现“内存泄漏”(不再使用的内存未回收),就会产生bug。人们非常容易忘记释放已分配的内存。
5.最好的解决方案就是要求调用者分配内存来保存函数的返回值。为了提高安全性,调用者应该同时指定缓冲区的大小。
例如:
1 | #include <stdio.h> |
如果可以在同一代码块中同时进行”malloc”和”free”操作,内存管理是最为轻松的。
本文标题:C函数的返回值
文章作者:Mr Bluyee
发布时间:2018-07-28
最后更新:2019-07-15
原始链接:https://www.mrbluyee.com/2018/07/28/C%E5%87%BD%E6%95%B0%E7%9A%84%E8%BF%94%E5%9B%9E%E5%80%BC/
版权声明:The author owns the copyright, please indicate the source reproduced.