编译过程和变量地址

时间:2018-06-29 16:43:49

标签: c embedded

在技术面试中有人问我这个问题 “ c语言中的编译过程是什么?”

我回答:

  1. 预处理器
  2. 编译器
  3. 汇编器
  4. 链接器

然后他继续

“”然后是这些编译过程中的一个,程序中的所有变量都位于并具有地址....如果有2个变量A和B ....之后,过程A和B将进行在内存中有地址”

(我认为他的意思是每个过程后生成了哪个文件)

我最后回答说,是在链接器之后,因为需要定义外部值,但是我不知道我说的是对还是错。

所以希望有人可以帮助我理解这个问题

2 个答案:

答案 0 :(得分:2)

我只想对user3386109注释添加一些说明:

  1. 对于裸机编译器,将分配确定的地址 在链接时。
  2. 对于要在OS(Linux,Windows,RT-Linux等)上运行的程序,链接器将分配可重定位的地址,并在程序加载时给出确定的地址。但是我不认为加载实际上不是编译过程的一部分,我宁愿说它是程序初始化过程的一部分。

希望有帮助。

答案 1 :(得分:-1)

没有人回答地址问题。根据平台的不同,您的变量可能有多个地址。

根据变量进行编译时,要么为堆栈上的堆栈指针分配了一个偏移量,但是直到该函数运行时(通常)才知道堆栈指针。对于.data和.bss,编译器将保留一种机制,具体取决于编译器和目标如何到达变量。

unsigned int x = 5;
unsigned int y;
unsigned int more_fun ( unsigned int );
unsigned int fun ( unsigned int z )
{
    unsigned int a;
    a = x + 1;
    a = a + more_fun(y) + y + z;
    return(a);   
}

00000000 <fun>:
   0:   e92d4070    push    {r4, r5, r6, lr}
   4:   e1a06000    mov r6, r0
   8:   e59f3028    ldr r3, [pc, #40]   ; 38 <fun+0x38>
   c:   e5934000    ldr r4, [r3]
  10:   e59f5024    ldr r5, [pc, #36]   ; 3c <fun+0x3c>
  14:   e5950000    ldr r0, [r5]
  18:   ebfffffe    bl  0 <more_fun>
  1c:   e5953000    ldr r3, [r5]
  20:   e0844003    add r4, r4, r3
  24:   e2844001    add r4, r4, #1
  28:   e0844006    add r4, r4, r6
  2c:   e0840000    add r0, r4, r0
  30:   e8bd4070    pop {r4, r5, r6, lr}
  34:   e12fff1e    bx  lr

在这种情况下,z不存储在堆栈中,而是将寄存器保存在堆栈中,并且z存储在该寄存器中,因此它没有相对或其他地址。 x和y确实有地址供链接器稍后填充,这是此编译器和目标如何解决该问题的方法。这显然是优化的。 a没有地址,也没有在寄存器中处理。如果我没有优化,那么a和z将具有堆栈指针相对存储,并且全局变量保持全局状态。

但是一旦链接。

00200008 <more_fun>:
  200008:   e12fff1e    bx  lr

0020000c <fun>:
  20000c:   e92d4070    push    {r4, r5, r6, lr}
  200010:   e1a06000    mov r6, r0
  200014:   e59f3028    ldr r3, [pc, #40]   ; 200044 <fun+0x38>
  200018:   e5934000    ldr r4, [r3]
  20001c:   e59f5024    ldr r5, [pc, #36]   ; 200048 <fun+0x3c>
  200020:   e5950000    ldr r0, [r5]
  200024:   ebfffff7    bl  200008 <more_fun>
  200028:   e5953000    ldr r3, [r5]
  20002c:   e0844003    add r4, r4, r3
  200030:   e2844001    add r4, r4, #1
  200034:   e0844006    add r4, r4, r6
  200038:   e0840000    add r0, r4, r0
  20003c:   e8bd4070    pop {r4, r5, r6, lr}
  200040:   e12fff1e    bx  lr
  200044:   0021004c    eoreq   r0, r1, r12, asr #32
  200048:   00210050    eoreq   r0, r1, r0, asr r0

Disassembly of section .data:

0021004c <x>:
  21004c:   00000005    andeq   r0, r0, r5

Disassembly of section .bss:

00210050 <y>:
  210050:   00000000    andeq   r0, r0, r0

x和y具有已知/固定地址。因此,当您在此处看到答案或评论时,他们会说的是链接时间。在这种情况下,编译器最终不需要任何基于堆栈的变量,从技术上讲,这些变量将是运行时的,尽管使用了一个简单的程序,并说只能对函数进行一次调用,并且可以预先确定和/或最终将其固定下来。链接时间确定了它们的结局,但不要假设,非静态本地变量是在运行时从技术上确定的。

现在,我已经使用-fPIC进行了构建,对x和y的访问将是双重间接的,将读取全局偏移表,然后在其中读取变量本身的地址。初始地址在链接时确定,但可以在加载时修改为其他位置。

然后是虚拟的还是物理的,如果您在操作系统上运行,可以这样说,但不一定,但是可能使用mmu允许程序认为它在某个基于零的内存空间中(程序加载在偏移处) (对于程序和工具链而言,则说0x8000),但是有一个物理地址会因每次负载而有所不同,甚至更糟糕的是,如果换出了程序,只要正确处理了虚拟空间,它就有可能返回其他地方如果换出,则物理在加载时或运行时可能会有所不同。

当您在面试或大学考试中看到这样的问题时,就是这个问题。有时,询问者正在寻找特定的答案,例如链接器,虽然在很多情况下都适用,但也有例外。或者也许被问到的人知道的不仅仅是危险,而且正在寻找加载时间,链接时间或运行时间,或者正在寻找更长的解释。

到目前为止,这些答案还有其他例外。因此,被询问的人很可能对这个问题有一个特定的答案或原因,这很可能是您无法理解他们的想法并使其正确。所以这是一个不公平/不好的问题,我会为在这样一个糟糕的问题上工作的地方而犹豫。除非是后者,否则他们知道所有细微差别,并试图查看您是否出于某种原因知道所有细微差别。看看谁跌倒了,可能与他们的产品或开发无关,这可能是一个淘汰的问题。

我建议您为一些不同的gnu支持的目标(例如pdp-11,而不是在开玩笑,arm,x86等)构建/构建一些交叉编译器,尝试上述类似的不同实验,或者反汇编您的实际项目正在研究该工具的工作原理。如果在面试中有自由,您可以说,让我展示给您,然后带上笔记本电脑,敲出一个简单的例子,如果他们没有跟着您而感到困惑,请说声谢谢并寻找其他雇主。同时,我们整天进行面试,其中几个人与候选人一一轮换,当我们在岗位上聆听时,我问这个问题,这就是他们的答案。房间里的其他人说:“我什至都不知道你想在那儿问什么”,所以有时候这只是一个不好的问题。

我无法想象什么样的工作真正关心这样的事情,为什么这是一个相关的面试问题?这是一个工具链开发人员吗?

编辑

简短答案:有多个正确答案,同时这意味着答案可能彼此矛盾。

确定本地项目的编译时堆栈指针相对偏移量。但是堆栈指针本身以及偏移量是该函数的运行时事物。

链接时间地址应用于包括变量在内的其余项目。因此,链接时间是正确的答案。

可以更改加载时间,例如与位置无关的代码,因此加载时间是正确的答案。

当然还有虚拟地址和物理地址,mmu后面的物理地址在加载时,并且有可能在运行时更改。