c - 在 C 中从堆栈中复制数据

标签 c linux macos stack memcpy

我正在为我的几个 friend 整理一个 C 谜语,当一个 friend 提请我注意以下片段(恰好是我一直在写的谜语的一部分)时) 在 OSX 上编译和运行时运行不同

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

int main()
{
        int a = 10;
        volatile int b = 20;
        volatile int c = 30;

        int data[3];

        memcpy(&data, &a, sizeof(data));

        printf("%d %d %d\n", data[0], data[1], data[2]);
}

您期望的输出是 10 20 30Linux 下恰好是这种情况,但是当代码是在 OSX 下构建的,您会得到 10 后跟两个随机数。在调试并查看编译器生成的 assembly 之后,我得出结论,这是由于堆栈的构建方式所致。我绝不是 assembly 专家,但是在 Linux 上生成的汇编代码似乎很容易理解,而在 OSX 上生成的代码让我很困惑关闭一点。也许我可以从这里得到一些帮助。

这是在 Linux 上生成的代码:

        .file   "code.c"
        .section        .text.unlikely,"ax",@progbits
.LCOLDB0:
        .section        .text.startup,"ax",@progbits
.LHOTB0:
        .p2align 4,,15
        .globl  main
        .type   main, @function
main:
.LFB23:
        .cfi_startproc
        movl    $10, -12(%rsp)
        xorl    %eax, %eax
        movl    $20, -8(%rsp)
        movl    $30, -4(%rsp)
        ret
        .cfi_endproc
.LFE23:
        .size   main, .-main
        .section        .text.unlikely
.LCOLDE0:
        .section        .text.startup
.LHOTE0:
        .ident  "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609"
        .section        .note.GNU-stack,"",@progbits

这是在 OSX 上生成的代码:

        .section        __TEXT,__text,regular,pure_instructions
        .macosx_version_min 10, 12
        .globl  _main
        .p2align        4, 0x90
_main:                                  ## @main
        .cfi_startproc
## BB#0:
        pushq   %rbp
Ltmp0:
        .cfi_def_cfa_offset 16
Ltmp1:
        .cfi_offset %rbp, -16
        movq    %rsp, %rbp
Ltmp2:
        .cfi_def_cfa_register %rbp
        subq    $16, %rsp
        movl    $20, -8(%rbp)
        movl    $30, -4(%rbp)
        leaq    L_.str(%rip), %rdi
        movl    $10, %esi
        xorl    %eax, %eax
        callq   _printf
        xorl    %eax, %eax
        addq    $16, %rsp
        popq    %rbp
        retq
        .cfi_endproc

        .section        __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
        .asciz  "%d %d %d\n"


.subsections_via_symbols

我真的只对这里的两个问题感兴趣。

  1. Why is this happening?

  2. Are there any get-arounds to this issue?

我知道这不是利用堆栈的实用方法,因为我是一名专业的 C 开发人员,这确实是我发现这个问题很有趣并投入一些时间的唯一原因。

最佳答案

访问声明变量末尾之后的内存是未定义的行为——无法保证当您尝试这样做时会发生什么。由于编译器在 Linux 下生成程序集的方式,您碰巧直接在堆栈的一行中获取了 3 个变量,但是这种行为只是巧合 - 编译器可以合法地在堆栈变量之间添加额外数据,或者实际上做任何事情——结果不是由语言标准定义的。所以在回答你的第一个问题时,它正在发生,因为你正在做的不是设计语言的一部分。在回答你的第二个问题时,没有办法从多个编译器可靠地获得相同的结果,因为编译器没有被编程为可靠地重现未定义的行为。

关于c - 在 C 中从堆栈中复制数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45340497/

相关文章:

c - 隐藏 shell 中打印的回溯

c++ - 移植到Linux错误: expected initializer before ‘:’ token

macos - 在特定文件夹中添加菜单项?

macos - 在 Mac OSX 上打印 Firemonkey

linux - 循环直到连接到 SSH

xcode - 从核心数据生成报告

c - 大O小澄清

C、乘法、位运算或*

创建一个 128 字节的随机数

linux - vxworks 中的任务