V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
gracehunter
V2EX  ›  问与答

C语言return数组的问题

  •  
  •   gracehunter · 2012-12-05 12:56:03 +08:00 · 5427 次点击
    这是一个创建于 4153 天前的主题,其中的信息可能已经有所发展或是发生改变。
    char* funA(){
    char A[] = "Hello, world.";
    return A;
    }

    这样写有什么问题吗?
    18 条回复    1970-01-01 08:00:00 +08:00
    yuelang85
        1
    yuelang85  
       2012-12-05 12:56:57 +08:00
    ??楼主想说啥?
    gracehunter
        2
    gracehunter  
    OP
       2012-12-05 13:00:07 +08:00
    @yuelang85 刚才不小心点了发布,现在改好了
    vampirekiss
        3
    vampirekiss  
       2012-12-05 13:10:23 +08:00
    返回了一个指向临时变量的指针,当函数执行完时,A会被销毁,指针指向的内容是未知的。
    laskuma
        4
    laskuma  
       2012-12-05 13:44:58 +08:00
    local variable的scope只到本function结尾。之后就失效。如果stack再次增长的话会被覆盖成奇怪的东西。
    改成
    char* funA()
    {
    char *b = "Hello, world.";
    char *a = (char *)malloc(sizeof(char) * strlen(b));
    strcpy(a, b);
    return a;
    }
    ShadowStar
        5
    ShadowStar  
       2012-12-05 14:11:57 +08:00
    局部变量存储于栈空间,函数调用返回后就会失效,被其他函数复用相同栈空间。

    但是!对于@gracehunter 以及@laskuma 的代码来说,是可以的。
    因为对于""中的字符串会被编码在.rodata段,是静态存储的,返回的是这个字符串的地址。
    raptium
        6
    raptium  
       2012-12-05 14:51:08 +08:00
    @laskuma malloc 的時候長度要加 1 吧,不然 a 最後的 '\0' 可能寫到外面去了
    laskuma
        7
    laskuma  
       2012-12-05 21:42:57 +08:00
    @ShadowStar LZ的方法不行
    raptium
        8
    raptium  
       2012-12-05 22:23:19 +08:00
    @ShadowStar 本來我以為你這麼說是對的
    但是看了一下 gcc -S 編譯出來的 asm 似乎不是這樣
    "hello world" 不是在 .rodata 段的,而是直接四句 mov 寫到 stack 上的(一句 mov 可以過去 4 個字符),所以返回是會出問題的
    於是我想可能是字符串太短了,嘗試把 hello world 改成一個很長很長的字符串,這樣編譯出來字符串的確保存在 .rodata 段了。但是,再看一下後面代碼很長,基本就是好不容易算出來了兩個指針地址,然後用 rep movsd 把 .rodata 裏的數據複製到了 A 的位置,所以還是在 stack 上。
    綜上,樓主怎麼寫是不行的。
    magicsilence
        9
    magicsilence  
       2012-12-06 01:07:46 +08:00
    @raptium 是在.rodata段中, gcc 4.6
    -----
    [lithium@ubuntu:~/code/gcc/test]
    > cat t.c
    #include <stdio.h>

    char* t() {
    char* p = "this is a test prog";
    return p;
    }

    int main(int argc, char *argv[]) {
    char* p = t();

    printf("Addr p: %x\n", p);
    return 0;
    }


    [lithium@ubuntu:~/code/gcc/test]
    > ./a.out
    Addr p: 40063c
    [lithium@ubuntu:~/code/gcc/test]
    > objdump -hs a.out | grep ".rodata" -3
    CONTENTS, ALLOC, LOAD, READONLY, CODE
    13 .fini 0000000e 0000000000400628 0000000000400628 00000628 2**2
    CONTENTS, ALLOC, LOAD, READONLY, CODE
    14 .rodata 00000024 0000000000400638 0000000000400638 00000638 2**2
    CONTENTS, ALLOC, LOAD, READONLY, DATA
    15 .eh_frame_hdr 00000034 000000000040065c 000000000040065c 0000065c 2**2
    CONTENTS, ALLOC, LOAD, READONLY, DATA
    --
    400620 83c4085b 5dc39090 ...[]...
    Contents of section .fini:
    400628 4883ec08 e82ffeff ff4883c4 08c3 H..../...H....
    Contents of section .rodata:
    400638 01000200 74686973 20697320 61207465 ....this is a te
    400648 73742070 726f6700 41646472 20703a20 st prog.Addr p:
    400658 25780a00 %x..
    dhysum
        10
    dhysum  
       2012-12-06 02:18:53 +08:00
    @raptium 分配的内存是在堆上的。
    timonwong
        11
    timonwong  
       2012-12-06 02:51:17 +08:00
    @magicsilence
    字符数组应该是不保证一定在.rodata上的,尤其A还定义的还是个可写的数组。

    你的代码展示的是 指向string literal的字符串指针,由于GCC默认就开了string pooling,应该是直接放到.rodata段中的,其它不默认开string pooling的编译器(比如MSVC),结果就很难说了。
    raptium
        12
    raptium  
       2012-12-06 11:05:57 +08:00
    @magicsilence 是在 rodata 上有 但是返回的时候会先整段复制到栈里 然后返回栈地址
    clowwindy
        13
    clowwindy  
       2012-12-06 11:11:15 +08:00
    养成好习惯,const char *
    raptium
        14
    raptium  
       2012-12-06 11:18:11 +08:00
    @magicsilence 不對,剛在車上沒細看
    你的程序和樓主的不一樣 char *p = "...." 和 char p[] = "...." 不是一個意思
    你這個直接返回指向 .rodata 的地址就行了
    chap p[] 會在棧上分配空間的
    raptium
        15
    raptium  
       2012-12-06 11:20:15 +08:00
    @dhysum malloc 的會在堆上,如果只是局部變量 char A[] 那就是在棧上
    ShadowStar
        16
    ShadowStar  
       2012-12-06 12:28:37 +08:00
    @raptium 抱歉,我确实有点想当然了。
    因为Mac自带的toolchain没有objdump等工具,所以我只能用mips64的toolchain验证一下。
    /tmp % cat c.c
    #include <stdio.h>
    #include <stdlib.h>
    char* funA(){
    char A[] = "Hello, world.";
    return A;
    }
    int main ( int argc, char *argv[] )
    {
    printf("%s\n", funA());
    return EXIT_SUCCESS;
    }
    /* ---- end of function main ---- */
    /tmp % mips64-octeon-linux-gnu-gcc -O2 -o c c.c
    c.c: In function 'funA':
    c.c:6: warning: function returns address of local variable
    /tmp % mips64-octeon-linux-gnu-objdump -s c | sed -n '/\.rodata/,/Contents/p'
    Contents of section .rodata:
    120000b40 00020001 00000000 48656c6c 6f2c2077 ........Hello, w
    120000b50 6f726c64 2e000000 orld....
    Contents of section .interp:
    /tmp %
    magicsilence
        17
    magicsilence  
       2012-12-06 13:50:51 +08:00
    @raptium 嗯,对, char* 和char []分配的不一样。我贴的代码和楼主的意思有差了。

    @ShadowStar 这个结果重现不出来,猜测是不是gcc版本导致的?
    timonwong
        18
    timonwong  
       2012-12-06 14:51:26 +08:00
    @magicsilence
    我是不加-O优化选项就能看到
    这种未定义行为,重现了也没有什么意义就是了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2814 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 13:13 · PVG 21:13 · LAX 06:13 · JFK 09:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.