-2

我看到了一个我试图理解的令人费解的行为......

示例代码..请忽略我返回局部变量地址的事实..

编辑:我只是以这段代码为例来了解 gcc 的优化行为。我认为此示例代码中的未定义行为不会改变 gcc 的优化逻辑。如果你认为是,请解释。

    #include<stdio.h>
    char *foo() {
        char arr[] = "hello world is here..\n";
        return arr;
    }

    int main() {
        char *ptr;

        ptr = foo();
        printf("0x%x \n", ptr);
        printf("%s", ptr);
    }

在 linux/x86 机器上运行它, main() 中的第一个 printf 确实打印了一个地址。但第二个 printf 不打印任何东西。看起来 gcc 以某种方式优化了数组初始化。

如果我像下面这样更改 foo() ,那么字符串会被正确打印。我知道它是一个未定义的行为。但我只对了解这里的 gcc 优化感兴趣。

    char *foo() {
        char arr[] = "hello\n";
        printf("0x%x\n", arr);
        return arr;
    }

在原始代码中, foo 怎么可能返回一个地址,但初始化被优化了?这是汇编代码..我对 x86 汇编不太熟悉.. gcc 在这两种情况下到底在做什么?

            .LC0:
                    .string "hello\n"
                    .text
            .globl foo
                    .type   foo, @function
            foo:
            .LFB2:
                    pushq   %rbp
            .LCFI0:
                    movq    %rsp, %rbp
            .LCFI1:
                    movl    .LC0(%rip), %eax
                    movl    %eax, -16(%rbp)
                    movzwl  .LC0+4(%rip), %eax
                    movw    %ax, -12(%rbp)
                    movzbl  .LC0+6(%rip), %eax
                    movb    %al, -10(%rbp)
                    leaq    -16(%rbp), %rax
                    leave
                    ret
            .LFE2:

和 foo() 的汇编代码以及附加的 printf..

            .LC0:
                    .string "hello\n"
            .LC1:
                    .string "0x%x\n"
                    .text
            .globl foo
                    .type   foo, @function
            foo:
            .LFB2:
                    pushq   %rbp
            .LCFI0:
                    movq    %rsp, %rbp
            .LCFI1:
                    subq    $16, %rsp
            .LCFI2:
                    movl    .LC0(%rip), %eax
                    movl    %eax, -16(%rbp)
                    movzwl  .LC0+4(%rip), %eax
                    movw    %ax, -12(%rbp)
                    movzbl  .LC0+6(%rip), %eax
                    movb    %al, -10(%rbp)
                    leaq    -16(%rbp), %rsi
                    movl    $.LC1, %edi
                    movl    $0, %eax
                    call    printf
                    leaq    -16(%rbp), %rax
                    leave
                    ret
4

4 回答 4

6

Please ignore that fact that I am returning a local variable address..

好吧,由于您也在使用函数返回的值,我们不能忽略这一点。C 说你的程序调用了未定义的行为,并且实现有权做任何它想做的事情。

于 2012-06-06T07:53:34.273 回答
4

发生的事情很容易解释:您告诉编译器将一些数据放入堆栈,返回此地址并再次释放它。所以你有地址,但你没有关于内容的证据。

从函数返回后,您调用printf()了两次。首先是输出给定地址的任务,然后是它包含的内容。

在您到达输出点之前,堆栈内容可能(并且显然会)与其他内容混淆。我可以想象printf()内部需要一定数量的堆栈并使用它(=写入它),从而破坏您的原始内容。

尽管其他人完全正确地指出上述行为是未定义的,但想知道为什么编译器会以它的行为方式运行并没有什么坏处。


编辑:在这种情况下,仅查看汇编代码 fpr 是不够的foo()。优化还意味着函数调用可以被函数包含的代码替换。自己编译了代码后,这里好像是这样的。foo 的代码仍然包含在目标文件中,但也内联在main(). 这使得所述数组成为本地的,main()并且一直分配到最后。但正如所说,这只是巧合,没有什么可以依靠的。

汇编代码如下所示:

.LC1:
    .string "0x%x \n"
.LC2:
    .string "%s"
    .text
    .p2align 4,,15
.globl main
    .type   main, @function
main:
    pushl   %ebp
    movl    %esp, %ebp
    andl    $-16, %esp
    pushl   %ebx
    subl    $60, %esp
    leal    26(%esp), %ebx
    movl    %ebx, 4(%esp)
    movl    $1819043176, 26(%esp)
    movl    $1952784495, 30(%esp)
    movl    $1696607843, 34(%esp)
    movl    $539911028, 38(%esp)
    movl    $778269797, 42(%esp)
    movw    $10, 46(%esp)
    movl    $.LC0, (%esp)
    call    printf
    movl    %ebx, 4(%esp)
    movl    $.LC1, (%esp)
    call    printf
    movl    %ebx, 4(%esp)
    movl    $.LC2, (%esp)
    call    printf
    addl    $60, %esp
    popl    %ebx
    movl    %ebp, %esp
    popl    %ebp
    ret

call在这里你看 -你没有foo(); 数据以 4 个字节的块写入堆栈并立即提供给printf.

于 2012-06-06T08:17:40.403 回答
1

“未定义的行为”意味着编译器维护者不必为这种情况而烦恼。即,编译器在这种情况下所做的完全没有意义。除了你永远不应该调用未定义的行为之外,没有什么可以“理解”的。

如果您希望任何人将时间花在您的问题上,请创建一个定义明确的示例。

于 2012-06-06T08:10:30.447 回答
0

您如何检查给定代码中的优化。
return arr将返回数组的基地址。当这个地址到达 时main,所指向的内存arr被破坏。在此之后任何事情都可能发生,您可能会遇到段错误,您的程序可能会崩溃。使用未初始化指针时发生的任何事情都可能发生。此代码绝不是检查优化。它可以帮助您理解您在帖子中已经知道的未定义行为!!!!

于 2012-06-06T09:43:07.000 回答