函数可以返回局部的指针数组

某次面试时,跟面试官说,函数可以返回局部变量,被指,不懂C++,不懂操作系统,事后写代码验证了一下。。后来与另一个面试官讨论到这个问题时,他说,访问肯定是行的,只是数据还是不是你想要的数据。觉得这话在理。。这个东西也不一定有什么用处,就当是了解吧~~

如果直接linux的gcc上执行下代码

gcc –o test test.c
./test

会直接输出(null)。如果将printf(“%s”,fun())换成printf(“%s\n”,fun())就直接报Segmentation fault (core dumped)。具体细节不是很清楚,不过加了换行的printf会被直接替换成puts(可以相应的汇编代码中查看到)


读汇编代码之前,说下栈帧(图片来源:Linux内核完全剖析),虽然这个结构的细节只对很多年前的编译器有用,,这个结构现在还是一样的调用调用函数之前,先按从到左的顺序放入参数然后保存一些东西,然后进入被调用函数开始执行。


这段代码转汇编代码是64位的,很多指令的后缀可能不常见

gcc –S –o test.s test.c

得到(代码有点长,其中其他的printf只是用来规律的,部分不重要内容已删除)

           .file           "test.c"
           .section           .rodata
.LC0:
           .string           "%s"                      为了方便查看输出结果,可以改成 .string           "%s\n"
           .text
           .globl           fun
           .type           fun, @function
fun:
.LFB0:
           .cfi_startproc
           pushq           %rbp
           .cfi_def_cfa_offset 16                     
#表示执行完上条指令后,当前栈指针与调用该函数时的栈指针相差了16字节计算hello在栈中地址时可以用到
详见http://sourceware.org/binutils/docs/as/CFI-directives.html http://blog.csdn.net/jtli_embeddedcv/article/details/9321253
           .cfi_offset 6, -16
           movq           %rsp, %rbp
           .cfi_def_cfa_register 6
           subq           $16, %rsp           #变量开辟空间
           movq           %fs:40, %rax
           movq           %rax, -8(%rbp)                     
           xorl           %eax, %eax                      # eax清零
           movl           $1819043176, -16(%rbp)           #放入字符串hello的地址值
           movw           $111, -12(%rbp)
           leaq           -16(%rbp), %rax                      把 -16(%rbp)中的量相对基地址的偏移量放入rax
           movq           %rax, %rsi                       把rax放入rsi寄存器中,rsi是printf调用的参数两个printf调用推断
           movl           $.LC0, %edi                      #.LC0是printf的格式串
           movl           $0, %eax                      # eax保存函数的返回值,调用函数先清零
           call           printf
           movl           $0, %eax
           movq           -8(%rbp), %rdx                      # dx寄存器,在输入输出指令中存放端口值
           xorq           %fs:40, %rdx
           je           .L3
           call           __stack_chk_fail
main:
.LFB1:
           .cfi_startproc
           pushq           %rbp
           .cfi_def_cfa_offset 16
           .cfi_offset 6, -16
           movq           %rsp, %rbp
           .cfi_def_cfa_register 6
           subq           $16, %rsp
           movq           %fs:40, %rax
           movq           %rax, -8(%rbp)
           xorl           %eax, %eax
           movl           $1819043144, -16(%rbp)
           movw           $111, -12(%rbp)
           leaq           -16(%rbp), %rax
           movq           %rax, %rsi
           movl           $.LC0, %edi
           movl           $0, %eax
           call           printf
           movl           $0, %eax
           call           fun
           movq           %rax, %rsi                      直接的汇编代码中,rax没有设置值,与上面两个printf相比少了一条语句
           movl           $.LC0, %edi
           movl           $0, %eax
           call           printf
           movl           $0, %eax
           movq           -8(%rbp), %rdx
           je           .L6
           call           __stack_chk_fail
           xorq           %fs:40, %rdx

最后一个printf加入一条与前两个printf一样leaq语句,就可以输出,还在栈中局部变量(buf)所指向的值。问题是,buf栈中地址偏移当前的rbp多少呢?两个函数中的

movq           %rsp, %rbp
subq           $16, %rsp

可知,每个函数的局部变量占了16字节并且偏移最远的4字节存放了buf,即局部变量的地址由fun函数的.cfi_def_cfa_offset   16 知,在fun存放16字节局部变量前,主函数的call fun语句相差16个字节。所以,buf与rbp相差16*3=48字节。在printf的红色语句前面,加入

leaq            -48(%rbp),%rax

再链接.s文件得到可执行文件

gcc  –o test  test.s

输出


所以,函数能返回局部变量的指针,栈中的内容能被访问到,只是,编译器不允许。

至于堆,还没想到要怎么弄才可以。数据当然是存在的,能不能绕过操作系统的保护访问到,才是问题的根本。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值