【C语言】结构体与位段

在这里插入图片描述


前言

当我们设计程序时,其中有一个非常重要的步骤是选择表示数据的方式。
之前我们用数组存储数据,但这远远不够,比如存储一个人的数据:姓名,年龄,电话。因此c语言提供了结构体变量,让你自己创造新的形式,来表示不同数据。接下来,让我们开始结构体的学习。


一、声明结构体

结构体声明举例:创建一个结构体用来描述 书的相关信息
在这里插入图片描述

对于结构体的名称:命名标准和其他标识符一样。但有一个特别的是结构体可以没有名称。而没有名称的结构体称为匿名结构体在这里插入图片描述

对于结构体成员:可以是变量,数组,指针,甚至是结构体。
其中结构体成员是结构体的情况,在下文结构体变量中有解释。

——————————————————————————————————————————

二、结构体的运用

2.1、结构体变量与结构体指针

在这里插入图片描述
注:结构体变量的初始化

仿造上面的s2的初始化形式:要求一一对应。

当 结构体中包含结构体,如下

struct B
{
	char a;
	short b;
	int c;
};
struct Student
{
	struct B sb;
	char name[20];
	int age;
	char id[20];
};
struct Student s = { {'w',3,5},"sfw",20,"20256161" };

注:结构体的自引用

上面结构体中含有结构体中情况描述的是含有其他结构体,而结构体的自引用则是自己含有自己。

如下:

struct book
{
	int price;
	struct book* p;
};

注意结构体的自引用中的结构体成员必需是结构体指针。
如果是结构体变量:struct book p那将出错。因为此时的结构体大小无法确定。
结构体自引用的作用:在数据结构中用来建立链表

——————————————————————————————————————————

2.2、访问结构成员

当我们创建了结构体变量或结构体指针之后如何去使用它们呢?这时就要用到结构体访问操作符。

结构体变量:使用 操作符 - (.)
结构体指针:使用 操作符 - (->)

如下

struct book
{
	//结构体的成员,用.来提取
	char name[20];
	char id[20];
	int price;
};
int main(void)
{
	int num = 10;
	struct book b = { "c语言", "c20220826", 55 };
	printf("书名:%s\n书号:%s\n定价:%d\n", b.name, b.id, b.price);
	struct book* pb = &b;
    printf("书名:%s\n书号:%s\n定价:%d\n", pb->name, pb->id, pb->price);//也可以(*pb).name 
	return 0;
}

注:结构的初始化器

C99和C11为结构提供了指定初始化器,使得结构体变量初始化时不必要按顺序来赋值
struct book s = {.price = 123, .name="Asdbha", .id="c54542525"};

——————————————————————————————————————————

三、结构体内存对齐

struct s
{
	char c1;
	int i;
	char c2;
}s1;

问:sizeof(s1)的结果是多少?(int 为 4字节)
你可能认为结果为 1 + 4 + 1 = 6 即结构体成员的大小全部加起来。
但它的结果为 12
一个结构体变量的大小,并不是成员变量的简单相加,而是有一定规则。

结构体内存对齐的规则

C语言结构体对齐规则:
结构体(struct)的数据成员, 第一个数据成员存放的地址为结构体变量偏移量为0的地址处.
其他结构体成员自身对齐时, 存放的地址为min{ 有效对齐值为自身对齐值, 指定对齐值 } 的最小整数倍的地址处.
注 : 自身对齐值 : 结构体变量里每个成员的自身大小
注 : 指定对齐值:有宏 #pragma pack(N)指定的值, 这里面的 N一定是2的幂次方.如1, 2, 4, 8, 16等.
如果没有通过宏

  1. 在32位Linux主机上默认指定对齐值为4, 64位的默认对齐值为8,
  2. AMR CPU默认指定对齐值为8;
  3. vs-8

注:有效对齐值:结构体成员自身对齐时有效对齐值为自身对齐值与指定对齐值中 较小的一个.
总体对齐时, 字节大小是min{ 所有成员中自身对齐值最大的, 指定对齐值 } 的整数倍.


看上去并不好理解,下面我们举实际例子。

在这里插入图片描述

上面规则说到可以用改变默认对齐数

在这里插入图片描述

内存对齐的意义:
许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,
它们会要求这些数据的起始地址的值是某个数k的倍数,这就是所谓的内存对齐,
注:k被称为该数据类型的对齐模数(alignment modulus)。
这种强制的要求

  1. 简化了处理器与内存之间传输系统的设计
  2. 可以提升读取数据的速度。

总结:结构体内存对齐是以 空间 换 时间
怎样即节省空间又节省时间呢? - 调整变量顺序/修改默认对齐数

——————————————————————————————————————————

四、结构体传参

结构体传参一般传地址
因为结构体一般比较大,传值时消耗大,而传地址可以很好解决这个问题

struct s
{
	int date[1000];
	int num;
};
struct s s = { {1,2,3,4}, 1000 };
void print1(struct s s)
{
	printf("%d", s.num);
}
void print2(struct s* ps)
{
	printf("%d", ps->num);
}
int main(void)
{
	struct s a = { 0 };
	print1(s);//传结构体 - 数据太大,临时拷贝数据也太大
	print2(&s);//传地址 - 指针大小,更节省空间
	//因此一般用地址
	return 0;
}

——————————————————————————————————————————

五、位段

5.1、位段的声明

位段的声明和结构是类似的,有两个不同

  1. 位段的成员必须可以是 int , unsigned int, signed int, char
  2. 位段的成员名后边有一个冒号和一个数字

声明如下:

struct A
{
	int a : 2;// 表示 a 占2bit位
	int b : 5;// 表示 b 占5bit位
	int c : 10;
	int d : 30;
	由上可以看出,它操作的是比特位,比字节更小的单位。
};

那么它的作用是什么呢?
假如我们要表示性别:男,女,不确定。用2个比特位就够了。00 - 男,01 - 女, 10 - 不确定。能节省空间。


5.2、位段的内存问题

位段的内存分配

  1. 位段的成员可以是int , unsigned int, signed int, char(属于整型家族)
  2. 位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的
  3. 位段涉及很多不确定因素,位段是不跨平台的,注意可移植程序一个避免使用位段。
    在这里插入图片描述在这里插入图片描述

位段的跨平台问题

  1. int位段被看作有符号还是无符号是不确定的
  2. 位段中最大位的数码不确定(16位 - int - 2字节,32位-int-32,如int b:17,16位机器超了)
  3. 位段中成员在内存从左向右还是从右向左,不确定
  4. 当一个结构体包含两个位段,第二个位段成员比较大,无法容纳第一个位段剩余的位是,是舍弃剩余的位还是利用,不确定

总结:与结构相比,位段可以达到同样效果,但有跨平台问题

在这里插入图片描述

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值