【问题标题】:Where do macros such as global integers get stored in memory?诸如全局整数之类的宏在内存中存储在哪里?
【发布时间】:2020-01-30 21:36:12
【问题描述】:

我试图了解事物在内存中的存储位置,例如全局变量和静态变量(.data,如果未初始化为零)等。

我要查找/考虑的是一个宏,如下所示:

#define thisInteger 100

这可以使用 objdump 找到吗?

另外,如果我将它分配给一个新变量,如下所示,它会在哪里找到(在 .data 中猜测):

#define THIS_INTEGER 100

int newVariable = THIS_INTEGER;

【问题讨论】:

  • “可以使用 objdump 找到这个”。不,甚至在编译阶段之前就对宏进行了预处理。它本质上只是“复制和粘贴”到使用它的任何地方。第二个问题取决于该变量在何处以及如何声明 - 全局?在一个函数中?等
  • 宏不存储在内存中。所有使用宏的实例都会在预处理期间被宏的值完全覆盖。
  • 无论在何处使用宏,编译器都会获取值 100。它很可能出现在各种机器代码指令中,使用immediate mode addressing,例如在表达式语句中使用时,例如 a = a + 100f(100)

标签: c assembly system objdump


【解决方案1】:

宏不是变量,因此它们不会存储在任何地方。当您执行#define thisInteger 100 时,C 预处理器会运行源代码并将thisInteger 替换为整数文字100。询问thisInteger 的存储位置与询问100 的存储位置相同。要验证这一点,请尝试类似&thisInteger。它不会编译,因为&100 是非法的并且没有意义。

可以使用 objdump 找到吗?

没有。预处理是在编译之前完成的复制粘贴处理。

另外,如果我将它分配给一个新变量,如下所示,它会在哪里找到

取决于您定义变量的位置。

【讨论】:

    【解决方案2】:

    宏只是编译时(它们在编译前被预处理)

    如果您使用gcc 编译器,您可以使用 -E gcc 选项查看预处理的 C 文件。这个预处理后的文件会在实际编译中用到。

    Your preprocessed example

    1. 如果newVariable 具有静态或线程存储持续时间,则在调用main 函数之前将其初始化为该值
    2. 如果 newVariable 具有自动存储持续时间,则在调用函数时将其初始化为该值。

    【讨论】:

      【解决方案3】:

      无论在何处使用宏,编译器都会获取值100。它很可能出现在各种机器代码指令中,使用immediate mode addressing,例如在表达式语句中使用时,例如 a = a + 100f(100)

      编译器很可能会根据需要在上述计算表达式所涉及的指令中嵌入这样的小常量,所以如果我们执行a = a + thisInteger;f(thisInteger),可能会有两个不同的机器代码指令嵌入常量100 直接用于每个此类用途。全局数据比嵌入小的立即数更需要处理,因此编译器不会尝试在两个用途之间共享100 作为全局或静态数据。

      所以,是的,您可以在 objdump 中看到 100,但对于许多用法,您可能需要查看代码 (.text) 部分以查找使用 #100 作为立即操作数(或 @ 987654332@ 如果以十六进制打印)。在反汇编中,您正在寻找诸如add [rbp+24], #100move rdi, #100 之类的指令。

      你说得对,如果你声明一个可变全局变量 int x = thisInteger;,你可以在数据 (.data) 部分找到 100objdump。但是同一声明的局部变量将在运行时使用机器代码指令进行初始化,例如mov ??, #100

      【讨论】:

        【解决方案4】:

        自己试试看

        起点:so.c

        #define THIS_INTEGER 100
        
        int newVariable = THIS_INTEGER;
        
        void fun0 ( void )
        {
            static int hello;
            hello = 100;
        }
        int fun1 ( void )
        {
            int hello;
            hello = 100;
            return(hello);
        }
        

        预处理器对定义进行搜索和替换

        arm-none-eabi-gcc -save-temps -O2 -c so.c -o so.o
        

        所以.i

        # 1 "so.c"
        # 1 "<built-in>"
        # 1 "<command-line>"
        # 1 "so.c"
        
        
        
        int newVariable = 100;
        
        void fun0 ( void )
        {
            static int hello;
            hello = 100;
        }
        int fun1 ( void )
        {
            int hello;
            hello = 100;
            return(hello);
        }
        

        您可以看到 THIS_INTEGER 不再存在,它只是一个宏/定义它的目的是在这种情况下保持一个常量,以便如果您想更改它,您可以更改它的所有相关实例。但是编译器需要它可以实际编译的东西。

        然后将预处理器输出 so.i 馈送到实际的编译器并生成汇编:so.s

            .cpu arm7tdmi
            .eabi_attribute 20, 1
            .eabi_attribute 21, 1
            .eabi_attribute 23, 3
            .eabi_attribute 24, 1
            .eabi_attribute 25, 1
            .eabi_attribute 26, 1
            .eabi_attribute 30, 2
            .eabi_attribute 34, 0
            .eabi_attribute 18, 4
            .file   "so.c"
            .text
            .align  2
            .global fun0
            .arch armv4t
            .syntax unified
            .arm
            .fpu softvfp
            .type   fun0, %function
        fun0:
            @ Function supports interworking.
            @ args = 0, pretend = 0, frame = 0
            @ frame_needed = 0, uses_anonymous_args = 0
            @ link register save eliminated.
            bx  lr
            .size   fun0, .-fun0
            .align  2
            .global fun1
            .syntax unified
            .arm
            .fpu softvfp
            .type   fun1, %function
        fun1:
            @ Function supports interworking.
            @ args = 0, pretend = 0, frame = 0
            @ frame_needed = 0, uses_anonymous_args = 0
            @ link register save eliminated.
            mov r0, #100
            bx  lr
            .size   fun1, .-fun1
            .global newVariable
            .data
            .align  2
            .type   newVariable, %object
            .size   newVariable, 4
        newVariable:
            .word   100
            .ident  "GCC: (GNU) 9.2.0"
        

        它被输入到汇编器中,然后如果你反汇编,你会得到:

        Disassembly of section .text:
        
        00000000 <fun0>:
           0:   e12fff1e    bx  lr
        
        00000004 <fun1>:
           4:   e3a00064    mov r0, #100    ; 0x64
           8:   e12fff1e    bx  lr
        
        Disassembly of section .data:
        
        00000000 <newVariable>:
           0:   00000064 
        

        嗯,我曾希望静电能把它留在那里。对于正在初始化的全局变量,如果它不是.data,它将是.bss。然后在 .data 你 可以看到 100 (0x64)。但它与宏无关/定义宏/定义只是将实际值100放在实际编译的代码中。

        对于另一种情况,在这里进行优化,堆栈上没有变量或任何类似的值被放置在返回寄存器中,所以在这种情况下它短暂地存在于寄存器中。

        静态是否按预期工作,事后看来,它没有起作用。我希望我称之为本地全球。它是一个局部变量,但添加静态将其放入 .bss 或 .data 而不是堆栈,然后希望看到生成的代码然后将 100 放入变量中,然后将其放入 .data/.bss 区域,该区域当然未优化但更难阅读:

        Disassembly of section .text:
        
        00000000 <fun0>:
           0:   e52db004    push    {r11}       ; (str r11, [sp, #-4]!)
           4:   e28db000    add r11, sp, #0
           8:   e59f3018    ldr r3, [pc, #24]   ; 28 <fun0+0x28>
           c:   e3a02064    mov r2, #100    ; 0x64
          10:   e5832000    str r2, [r3]
          14:   e1a00000    nop         ; (mov r0, r0)
          18:   e1a00003    mov r0, r3
          1c:   e28bd000    add sp, r11, #0
          20:   e49db004    pop {r11}       ; (ldr r11, [sp], #4)
          24:   e12fff1e    bx  lr
          28:   00000000    andeq   r0, r0, r0
        
        0000002c <fun1>:
          2c:   e52db004    push    {r11}       ; (str r11, [sp, #-4]!)
          30:   e28db000    add r11, sp, #0
          34:   e24dd00c    sub sp, sp, #12
          38:   e3a03064    mov r3, #100    ; 0x64
          3c:   e50b3008    str r3, [r11, #-8]
          40:   e51b3008    ldr r3, [r11, #-8]
          44:   e1a00003    mov r0, r3
          48:   e28bd000    add sp, r11, #0
          4c:   e49db004    pop {r11}       ; (ldr r11, [sp], #4)
          50:   e12fff1e    bx  lr
        
        Disassembly of section .data:
        
        00000000 <newVariable>:
           0:   00000064    andeq   r0, r0, r4, rrx
        
        Disassembly of section .bss:
        
        00000000 <hello.4142>:
           0:   00000000    andeq   r0, r0, r0
        

        具体来说:

           c:   e3a02064    mov r2, #100    ; 0x64
          10:   e5832000    str r2, [r3]
        

        100 被放入一个寄存器,然后该寄存器值被写入内存中,来自 fun0 的本地全局 hello 位于 .bss 中。

        宏/定义只是搜索和替换,预处理器将根据需要对宏的各个级别/层进行多次迭代,直到它们全部被替换,它们都不存在于预处理代码中。然后将其发送到编译器。

        在这种情况下,VALUE 100 在最终输出中可见,但这取决于您如何使用它,以及它的表示方式或存储位置。

        【讨论】:

          猜你喜欢
          • 2021-10-18
          • 2023-04-08
          • 2023-04-03
          • 2012-06-26
          • 2011-02-06
          • 1970-01-01
          • 2011-02-27
          • 2011-01-08
          • 1970-01-01
          相关资源
          最近更新 更多