C函数的返回值

首先看一段有问题的代码:

#include <stdio.h>
#include <string.h>

char * string_copy(char * source_str){
    char buffer[120];
    strncpy(buffer, source_str, 119);
    return buffer;
}

int main(void){
    char *destination_str;
    char source_str[] = "hello world";
    destination_str = string_copy(source_str);
    printf("%s\n",destination_str);
    return 0;
}

在编译时就给出了警告:

In function 'string_copy':
 warning: function returns address of local variable [-Wreturn-local-addr]
return buffer;

buffer是一个自动分配内存的数组,是该函数的局部变量。当控制流离开声明局部变量的范围时,自动变量便自动失效。这就意味着即使返回一个指向局部变量的指针,当函数结束时,由于该变量已被销毁,谁也不知道这个指针所指向的地址的内容是什么。

在C语言中,自动变量在堆栈中分配内存。当包含自动变量的函数或代码块退出时,它们所占用的内存便被回收,它们的内容肯定会被下一个所调用的函数覆盖。这一切取决于堆栈中先前的自动变量位于何处,活动函数声明了什么变量,写入了什么内容等。原先自动变量地址的内容可能被立即覆盖,也可能稍后才被覆盖,这就是问题难以被发现的原因。

然而,无论是编译器还是lint程序都无法检测到局部数组返回的所有情况(它有可能通过某一层间接形式存在躲过检查)。

解决这个问题有几种方案:

1.函数可以返回一个常量,或指向常量的指针。
例如:

int func(){
  return 0;
} 

这是最简单的解决方案,但是如果是其他需要返回变化的内容时,这就无能为力了。

2.使用全局声明的变量。
例如:

char my_global_array[120];
char *fun(){
  ...
  my_global_array[i] = ...;
  ...
  return my_global_array;
}

这适用于自己创建字符串的情况,也很简单易用。它的缺点在于任何人都有可能在任何时候修改这个全局数组,而且该函数的下一次调用也会覆盖该数组的内容。

3.使用静态数组。
例如:

char * func(){
  static char buffer[120];
  ...
  return buffer;
}

这就可以防止任何人修改这个数组。只有拥有指向该数组的指针函数(通过参数传递给它)才能修改这个静态数组。但是,该函数的下一次调用将覆盖这个数组的内容,所以调用者必须在此之前使用或备份数组的内容。和全局数组一样,大型缓冲区如果闲置不用是非常浪费内存空间的。

4.显式分配一些内存,保存返回值。
例如:

char * func(){
  char *s = (char *)malloc(120 * sizeof(char));
  ...
  return s;
}

这个方法具有静态数组的优点,而且在每次调用时都创建一个新的缓冲区,所以该函数以后的调用不会覆盖以前的返回值。它适用于多线程的代码(在某一时刻具有超过一个的活动线程的程序)。它的缺点在于程序员必须承担内存管理的责任。根据程序的复杂程度,这项任务可能很容易,也可能很复杂.如果内存仍在使用就释放或者出现“内存泄漏”(不再使用的内存未回收),就会产生bug。人们非常容易忘记释放已分配的内存。

5.最好的解决方案就是要求调用者分配内存来保存函数的返回值。为了提高安全性,调用者应该同时指定缓冲区的大小。
例如:

#include <stdio.h>
#include <string.h>
#include <malloc.h>

void string_copy(char * destination_str,char * source_str,int size){
    strncpy(destination_str, source_str, size);
}

int main(void){
    char source_str[] = "hello world";
    int str_size = 120;
    char *destination_str = (char *)malloc(str_size * sizeof(char));
    string_copy(destination_str,source_str,str_size);
    printf("%s\n",destination_str);
    free(destination_str);
    return 0;
}

如果可以在同一代码块中同时进行"malloc"和"free"操作,内存管理是最为轻松的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,373评论 8 265
  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 3,440评论 3 44
  • 一个人有没有文化,就看这四点:根植于内心的修养;无需提醒的自觉;以约束为前提的自由;为他人着想的善良。------...
    张庆玲阅读 504评论 0 4
  • A健康 家庭亲子 财务 人际 个人提升 美丽 工作 有趣 今日:健康 亲子 财务 人际 美丽 有趣 工作 ...
    是Penny阅读 48评论 0 0
  • 参数格式UserParameter=key[*],command 首先去配置文件中,开启includeInclud...
    J书越来越垃圾了阅读 1,188评论 0 0