C语言学习记录——사십일 自定义数据类型结构体(1)

目录

一、结构体声明

一些特殊声明

二、自引用

三、定义和初始化

四、内存对齐(计算结构体大小)

为什么要存在内存对齐?

修改默认对齐数


一、结构体声明
 

struct Stu//结构体关键字,标签(名字)
{
    char name[20];
    char tele[12];
    char sex[10];
    int age;
}s4, s5, s6;//全局变量

struct Stu s3;//全局变量

int main()
{
    struct Stu s1;//局部
    struct Stu s2;//变量
    return 0;
}

一些特殊声明

struct
{
    char name[20];
    char tele[12];
    char sex[10];
    int age;
}x;

没有了名字,是一种匿名类型。如果不在下面紧接着声明x这个变量,那么就没有别的方法去声明变量。

struct
{
    char name[20];
    char tele[12];
    char sex[10];
    int age;
}sa;


struct
{
    char name[20];
    char tele[12];
    char sex[10];
    int age;
}*psa;

完全一样,如果这时候psa = &sa,编译器会报错。因为编译器会把他们当成两个独立的个体,不合法。

匿名结构体不要用。

二、自引用

参考数据结构中的链表类型,12345五个数字,都是结点,通过1能连接到2,2连到3,依次类推。结构体中也可以实现

struct Node
{
    int data;
    struct Node n;
};

这样的代码,data代表着1,n就是下一个结构体。但是结构体就太大了,Node n里面还放着一个数据,一个结构体,这一个结构体还放着一个数据,一个结构体,所以说无法计算大小。既然目的是通过一个结点找到另一个结点,那么只要存放地址即可,所以:

struct Node
{
    int data;
    struct Node* next;
};

可以用typedf来重命名

typedef struct Node
{
    int data;
    struct Node* next;
}Node;


但是必须原本就有结构体的名字,不能去重命名匿名结构体。

三、定义和初始化

之前也写过,里面也可以嵌套初始化,比如初始化一个结构体

四、内存对齐(计算结构体大小)

规则:

第一个成员在与结构体变量偏移量为0的地址处存放

其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

对齐数=编译器默认的一个对齐数 与 该成员大小的较小值。

结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍

 gcc编译器没有默认对齐数,所以对齐数就是成员大小

如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

#include <stdio.h>

struct S1
{
    char c1;
    int a;
    char c2;
};

struct S2
{
    char c1;
    char c2;
    int a;
};

int main()
{
    struct S1 s1 = { 0 };
    printf("%d\n", sizeof(s1));
    struct S2 s2 = { 0 };
    printf("%d\n", sizeof(s2));
    return 0;
}

S1,char占1个字节,int需要挪到到第5个字节处存储,前面有4个字节,3个空着,第一个位置存储char,这时候4 + 4,就已经占据8个字节了,char自然是接着占1个字节,这时候就是9,但这不是4的倍数,把char放到第12个字节处,所以大小就是12。

S2,char占1个字节,char占1个字节,这时候是2,int需要到第5个字节处存储,所以是4 + 4,8是4的倍数,所以大小就是8个字节。

嵌套

struct S3
{
    double d;
    char c;
    int i;
};

struct S4
{
    char c1;
    struct S3 s3;
    double d;
};


为什么要存在内存对齐?

没有官方原因,很多资料是这样的:

平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。(有些平台,访问int类型的数据,只能去访问4的整数倍的地址处取得)

性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问仅需要一次访问。

总结:结构体的内存对齐是拿空间换时间的做法

假设一个32位的机器,每次读取都是4个字节。如果不对齐,对于一个char和int类型的数据,想要读取完全这个int类型的数据,那么第一次读取4个字节,读到了char数据和部分int数据的空间,需要第二次读取才能读到完整的int数据。如果对齐了,char后面空3个字节再放上int数据,也需要读两次,但是对于int数据,第二次读就可以读完。

在设计结构体的时候,我们既要满足对齐,又要节省空间,那么:

让占用空间小的成员尽量集中在一起。


修改默认对齐数

#pragma预处理指令可以改变

#pragma pack(4)//设置默认对齐数为4
struct S
{
    char c1;
    double d1;
};
#pragma pack()//取消设置的默认对齐数

另外一个关键字offsetof(structName, memberName)

offset就是计算偏移量,of 就是指哪个元素

#include <stdio.h>

void Init(struct S* ps)
{
    ps->a = 100;
    ps->c = 'w';
    ps->d = 3.14;
}

void Print1(struct S tmp)
{
    printf("%d %c %lf\n", tmp.a, tmp.c, tmp.d);
}

void Print2(const struct S* ps)
{
    printf("%d %c %lf\n", ps->a, ps->c, ps->d);
}

int main()
{
    struct S s = { 0 };
    Init(&s);
    Print1(s);
    Print(&s);
    return 0;
}

void Print1(struct S tmp)

传值和传址调用。

如果传过去的参数过大,那么就会出问题,压栈。

之前写过

第二篇写位段

结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值