函数中返回局部变量

我们都知道,函数的局部变量只能在声明它的函数中访问,超出作用域后的非法访问会得到不可预测的值,甚至导致程序崩溃。

这么说来返回值岂不是都要用全局变量?既然都使用了全局变量,那还有返回的必要吗?那么函数的返回值岂不是没有存在的意义了?

上一篇中,我们了解了函数调用栈,趁热打铁,这次我们就刚好利用学到的知识来解决眼前的问题。

测试用例

char* get_memory() {
    char p[] = "hello world";
    return p;
}

int main(int argc, const char * argv[]) {
    char* str = NULL;
    str = get_memory();
    printf("%s",str);
    return 0;
}

问题1. char p[] 真的是出了函数范围就访问不到正确内容了吗?

进入反汇编调试界面,断点在 get_memory 函数调用前:

2017-08-07-memory2-0.png
a. -18(%rbp) 是局部变量 char* str,用来存放 get_memory 函数的返回值;
b. -18(%rbp) 的值在 printf 函数调用时,作为第二个参数;

进入 get_memory 函数:

2017-08-07-memory2-1.png
c. get_memory 函数返回指针指向栈中内存地址 0x00007fff5fbff69c(-0x14(%rbp));
d. 第 9 到 12 行指令是将 "hello world" 这个字符串中的每个字符拷贝到 0x00007fff5fbff69c(-0x14(%rbp) 开始的连续地址中;

调用 printf 函数之前:

2017-08-07-memory2-5.png

可以看到,虽然已经结束了 get_memory 函数的调用,但是栈中分配给局部变量 char p[] 的内存地址,并没有被释放或者覆盖,此刻依然还是可以访问到正确的内容;

但是在函数调用栈里,我们说过,栈是由编译器自动分配和释放,是不可控的,这里只是为了理解过程。

2017-08-07-memory2-3.png

问题2. str 为什么不能被 printf 正确打印?

2017-08-07-memory2-4.png

通过 问题1. 我们分析得知,get_memory 中的局部变量 char p[] 在未调用 printf 函数之前,是可以正确访问的。

那么无法正确打印的问题,很明显就出在 printf 函数本身。

调试框输入 b printf 命令,可以在 printf 函数内部打上断点。

2017-08-07-memory2-6.png

printf 在更新了 rbp 之后,直接 push 了三个空内容寄存器,将 get_memory 函数中分配给 char p[] 的内存覆盖。

通过简单的栈图,更直观的感受一下:

2017-08-07-memory2-7.png

此刻的内存情况:

2017-08-07-memory2-8.png

如何正确返回局部变量?

我们在分析 问题1. 的时候,从 get_memory 函数的反汇编代码可以清楚的看到,局部变量 char p[] 实际上是在栈上分配了一段连续内存,再将 "hello world" 中的字符拷贝到其中,然后返回首地址;而栈是由编译器自动分配和释放的,是不可控的,那么返回的地址中的内容,当然也就是不可控的,这显然不是我们的初衷,所以 函数不能返回指向栈内存的指针

正确的返回姿势:

  • 声明为 static 后,变量将存储在静态存储区,分配一次后不会销毁,直到程序结束都有效,不再受函数作用域限制。
  • malloc 在堆上分配内存,返回指向堆内存的指针,需要手动释放内存,防止内存泄漏。
  • char *p = "hello world" 字符串分配在常量区,返回指向常量区的指针。

对于基本数据类型,编译器会直接进行值拷贝。

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

推荐阅读更多精彩内容

  • __block和__weak修饰符的区别其实是挺明显的:1.__block不管是ARC还是MRC模式下都可以使用,...
    LZM轮回阅读 3,292评论 0 6
  • C语言中内存分配 在任何程序设计环境及语言中,内存管理都十分重要。在目前的计算机系统或嵌入式系统中,内存资源仍然是...
    一生信仰阅读 1,151评论 0 2
  • iOS面试小贴士 ———————————————回答好下面的足够了------------------------...
    不言不爱阅读 1,966评论 0 7
  • ———————————————回答好下面的足够了---------------------------------...
    恒爱DE问候阅读 1,712评论 0 4
  • 多线程、特别是NSOperation 和 GCD 的内部原理。运行时机制的原理和运用场景。SDWebImage的原...
    LZM轮回阅读 2,004评论 0 12