4_结构的存储

结构的存储

结构的存储-1

结构

结构的定义, 一块连续分配的内存区域, 内部的元素通过名字去访问, 而且内部元素可以是不同的数据类型.

里面元素是连续存放的

下面是访问结构的 C 语言代码 :

struct rec{
    int i;
    int a[3];
    int *p;
}

void set_i(struct rec *r, int val)
{
    r->i = val;
}

汇编代码如下 :

; %eax = val
; %edx = r
movl %eax, (%edx)   ; Mem[r] = val

结构中元素地址的计算

还是上面那个结构, 我们要计算 里面第二个元素 a 里面某个下标为 idx 的元素的地址

int *find_a(struct rec *r, int idx)
{
    return &r->a[idx];
}

我们先画出 layout :

看图就知道, 针对这个 idx 的访问, 就是它的首地址, 加上一个 4, 因为 i 占了 4 个字节, 这就相当于访问了一个数组, 数组首地址是 r + 4, 找到数组之后就根据索引和每个元素的大小来访问我们的目标元素

汇编代码如下 :

; %edx = idx
; %edx = r
leal 0(, %ecx, 4), %eax     ; 4 * idx
leal 4(%eax, %edx), %eax    ; r + 4 * idx + 4

每个元素在结构中的相对地址, 编译时就已经确定了.

续前

再来一个复杂点的, 这么个故意构造出来的运算, 把三个元素的访问都涉及到了

还是上面那个结构

void set_p(struct rec *r)
{
    r->p = &r->a[r->i];
}

把 r + i 作为 index 去访问 a 里面的这个元素, 然后把这个元素地址算出来, 赋值给 p

; %edx = r 结构首地址
movl (%edx), %ecx           ; r->i  首先把 i 元素的值取出来, 放到 ecx 里面去
leal 0(, %ecx, 4), %eax     ; 4 * (r->i) 计算 r 里面 a 的这个元素的地址
leal 4(%edx, %eax), %eax    ; r + 4 + 4 * (r->i)
movl %eax, 16(%edx)         ; Update r->p

结构的存储-2

数据存储位置对齐

现在计算机系统, 对这些元素的存储都有一个位置对齐的要求

位置对齐就是, 假设你这个数据类型的 size 为 k, 那么它的存储地址必须是 k 的整数倍.

当然, 不同的系统的要求都略有不同

为什么要这样呢, 因为计算机访问内存一般是以内存块为单位的, 块的大小是地址对齐的.

如果你不要求基本数据位置上的对齐, 数据访问地址跨越 "块" 的边界会引起额外的内存访问. 比如说一个 int, 它位于 2 个内存块之间, 你要取这个 int, 你就要两次访问内存

C 语言编译器会在各个元素之间插入额外的空间, 来满足不同元素的对齐要求

x86-32 下不同元素的对其要求

基本数据类型 :

如果大小为 1byte, 比如 char, 就没有任何对齐要求

如果大小为 2 byte, 比如 short, 就需要 2 字节对齐, 地址最后一位为 0

如果大小为 4 byte, 比如 int float char*, 要求 4 字节对齐, 地址最后两位为 0

如果是 8 byte, double 类型, 看情况. windows 系统地址后三位为 0, 8 字节对齐. linux 系统为 4 字节对齐, 地址后两位为 0.

-86-64 下不同元素的对齐要求

1 byte, 2 byte, 4 byte 同 x86-32

8 byte 大小比如 double, windows&linux 都是要求 8 字节对齐

16 byte 大小的 long double, linux 为 8 对齐, 地址最后三位为 0.

结构的存储-3

结构的对齐存储要求

  • 必须满足结构中各个元素的对齐要求

首先一个结构给你了, 结构当中每个元素该怎么存就怎么存, 每个元素该怎么对齐还是怎么对齐.

  • 结构自身的对齐要求, 等同于其各个元素中对齐要求最高的那个, 设为 k 字节

  • 结构的起始地址和结构长度必须是 k 的整数倍

结构的存储-4

结构自身的对齐要求

刚才说过, 结构里面的元素就是应该怎么对齐就是怎么对齐

结构本身也有对齐要求, 结构自身的对齐要求, 就参照它里面各个元素对齐要求最严格的那一个

结构内元素不同的先后顺序

我们声明一个结构, 结构里元素不变, 但是这次我们改一下它们在里面的顺序, 这就会对结构在内存里面的布局产生一些影响.

结构数组的存储

结构数组

首先计算数组元素 (即结构) 的地址

然后访问该结构中的元素

小结

结构起始地址的对齐要求, 等同于该结构各个元素中对齐要求最高的那个

结构当中的元素, 该怎么对齐就怎么对齐

结构的长度必须是 K 的整数倍

联合

联合和结构有点像, 区别是结构元素是一个挨着一个放的

联合是里面的元素共享同一块内存, 假设有个联合, 里面有 3 个元素, 你可以理解为这 3 个元素是叠在一起放的

所以说联合的尺寸, 完全是里面最大的层面的大小来决定的

联合成员共享同一块大小的内存, 一次只能使用其中的一个成员

小结_

  • C 语言数组的汇编访问

    • 连续存储

    • 访问代码优化

    • 无边界检查

  • 结构

    • 对齐要求

    • 以及相应的汇编代码

  • 联合