乐趣区

关于c:闲聊C语言结构体

构造体是 C 语言次要的自定义类型计划,这篇就来认识一下构造体。

一、构造体的状态

C 源程序(struct.c):

#include <stdio.h>

typedef struct{
    unsigned short int a;
    unsigned short int b;
}Data;

int main()
{
    Data c, d;
    
    c.a = 1;
    c.b = 2;
    d = c;
    
    printf("d.a:%d\nd.b:%d\n", d.a, d.b);
    return 0;
}

赋值局部翻译后:

    movw    $1, 28(%esp)    # c.a = 1
    movw    $2, 30(%esp)    # c.b = 2
    movl    28(%esp), %eax    #
    movl    %eax, 24(%esp)    # d = c

能够看出:

  • c.a 是在 28(%esp) 之后的 2 个字节
  • c.b 是在 30(%esp) 之后的 2 个字节
  • c 是 28(%esp) 之后的 4 个字节
  • d 是 24(%esp) 之后的 4 个字节

不得不感叹名字(构造体名字、子元素名字)再一次被抛弃了,子元素名代表的是绝对于构造体的偏移。

二、构造体的复制

很早就被告知:数组不能复制! 然而当发现上面这个程序失常运行后,我困惑了(block.c):

#include <stdio.h>

typedef struct{char data[1000];
}Block;

Block a={{'a','b','c',}};

int main()
{
    Block b;

    b=a;
    
    puts(b.data);
    return 0;
}

Block a={{‘a’,’b’,’c’,}} 是对 a 的局部初始化,’c’ 前面主动填 0,写成 Block a={{“abc”}} 也一样,C 语言对初始化还是很宽容的。

下面这个程序竟然失常的编译、运行了,这到底是怎么的逆天?
看看汇编局部:

leal    24(%esp), %edx
movl    $a, %ebx
movl    $250, %eax
movl    %edx, %edi    # edi = &b
movl    %ebx, %esi    # esi = &a
movl    %eax, %ecx    # ecx = 250
rep movsl

咱们发现程序的确通过 250 次 movsl 复制了一个 ” 数组 ”。其起因是:构造体是能够复制的,构造体又能够包含任意类型的子元素,数组也行,所以 ” 数组 ” 也被复制了。

那为什么纯正的数组就不能复制呢?

咱们能够这样去了解:一个变量能被复制的必要条件是咱们晓得它的大小。构造体做为自定义类型,在编译的时候编译器必然存储了它的子元素类型、个数等相干信息,构造体的大小也就晓得了;而数组个别只在乎它的类型和起始地址,元素个数总是被忽视的(例如:void func(char s[]) 可承受任何长度的字符数组做参数),而且元素个数也没有被当做数组的一部分存入内存,所以数组的复制是不好实现的。

三、构造体中的数组和指针

对于构造体中的指针,如果把构造体 a 赋值给构造体 b,那么对于指针来说,只是简略的拷贝指针地址,并不会重新分配一个内存空间,以及新的指针地址。也就是说构造体a 和构造体 b 中的指针,共享空间。
对于构造体中的数组,则恰恰相反,他们并不共享空间,而是重新分配一个内存空间,用以保留数组内容。
咱们能够这样去了解:一个变量能被复制的必要条件是咱们晓得它的大小。因而,在计算构造体大小时,构造体中的指针只占有了四个字节,而数组则占用数组自身的大小,因而,指针只是简略的拷贝指针地址,而数组却能够重新分配一个内存空间来保留数组内容。

四、小结

如果给构造体下一个切实点的定义话,那就是:有格局的字节数组。有了构造体后 C 语言的变量类型就丰盛多了,然而同时也要留神:

  1. 超过 4 字节的构造体不宜做参数(参数传递浪费时间、空间),换做指针更好。
  2. 超过 4 字节的构造体不宜做返回值类型(话说个别返回值都用 eax 来存,那么超过 4 字节的时候怎么存呢?本人去摸索吧!)。

五、关注 && 分割

gitee:https://gitee.com/cmcc-oneos/OneOS-Lite

docs:https://oneos-lite.com/

退出移动版