在内存中(内部)创建结构时的操作顺序是什么?

问题描述 投票:0回答:1

说我在C语言中有一个简单的系统:

#include <cstddef>

typedef struct Point { 
  Point *a;
  Point *b;
  int x;
  int y;
} Point; 

int main() { 
  Point p1 = {NULL, NULL, 3, 5};
  return 0; 
}

Godbolt编译为:

main:
  push rbp
  mov  rbp, rsp
  mov  QWORD PTR [rbp-32], 0
  mov  QWORD PTR [rbp-24], 0
  mov  DWORD PTR [rbp-16], 3
  mov  DWORD PTR [rbp-12], 5
  mov  eax, 0
  pop  rbp
  ret

更进一步,我们有:

int main() { 
  Point v = {NULL, NULL, 3, 5};
  Point m = {NULL, NULL, 7, 9};
  Point s = {&v, &s, 11, 12};
  return 0; 
}

编译为:

main:
  push rbp                    ; save the base pointer to the stack.
  mov  rbp, rsp               ; put the previous stack pointer into the base pointer.
  mov  QWORD PTR [rbp-32], 0
  mov  QWORD PTR [rbp-24], 0
  mov  DWORD PTR [rbp-16], 3
  mov  DWORD PTR [rbp-12], 5
  mov  QWORD PTR [rbp-64], 0
  mov  QWORD PTR [rbp-56], 0
  mov  DWORD PTR [rbp-48], 7
  mov  DWORD PTR [rbp-44], 9
  mov  QWORD PTR [rbp-96], 0
  mov  QWORD PTR [rbp-88], 0
  mov  QWORD PTR [rbp-80], 0
  mov  DWORD PTR [rbp-80], 11
  mov  DWORD PTR [rbp-76], 12
  lea  rax, [rbp-32]
  mov  QWORD PTR [rbp-96], rax
  lea  rax, [rbp-96]
  mov  QWORD PTR [rbp-88], rax
  mov  eax, 0
  pop  rbp
  ret

我还不能确切地告诉发生了什么,但是this helps(有点)。能否解释一下上一个示例中发生了什么?我不太了解基本指针是什么,我知道堆栈指针是什么。我不确定QWORD PTR [...]的作用,但是它是四字大小和指针/地址。但是为什么要从QWORD PTR [...]中选择那些特定的偏移量呢?我不明白为什么选择了它。

然后第二部分是rbp。似乎正在处理我lea rax, [rbp-32]所做的部分。

所以我的问题是:

  1. 什么是QWORD / DWORD PTR加载into?这是加载到堆,堆栈还是其他东西中?
  2. 为什么选择选择lea rax, [rbp-32]的偏移量?
  3. 操作顺序是否总是从最小对象(最原始的对象)到最复杂的对象?还是可以想到一种情况,其中汇编代码首先构造复杂对象,然后then构造更原始的对象?

我很纳闷,因为我想把头放在如何在装配中创建树的周围。在函数式编程或JavaScript中,您有{&v, &s}。首先传递deepest函数的值,然后最后传递参数的值rbp的值。但是我很难想象它在装配中的外观。

[更具体地说,我试图在程序集中创建像简单的键/值存储区一样,以更深入地了解如何在此较低级别创建“对象”。使用JavaScript很容易:

a(b(c(), d(), e(f(g(), h()), ...)))

但是这是因为a在内存中已经存在somewhere。我的问题是,是否应该在键值存储中预先创建此direct?还是始终在内存中的任意空闲位置创建它(例如db[key] = value 的偏移量),然后再将它们移到正确的位置(或将它们指向正确的位置)?我一直认为我应该直接在树枝上创建树叶节点,就像我(在视觉上)在树枝上粘贴树叶一样。但是叶子已经存在!它在分支上之前在哪里存在?它可以存在于分支before上吗?我感到困惑。

所以,从叶子开始

value

将其粘贴在分支上。

rbp

首先在哪里创建叶子?这就是我试图通过装配示例看到的。

c assembly x86-64
1个回答
0
投票

大多数编译器将堆栈用于局部变量。

堆栈上的空间通常由两个指针管理:堆栈指针;和一个“基本”指针,指向堆栈上“已分配”内存的基础。

同样要注意的是,几乎所有系统上的堆栈都增长了[[downward,这就是为什么基指针(生成的代码中的寄存器🍁 )有负偏移的原因。


有点说明,它看起来像这样:

基本指针---> + --------------------- +|变量空间|| ... || ... || ... |堆栈指针-> + --------------------- +

© www.soinside.com 2019 - 2024. All rights reserved.