结构体的介绍

1.结构体类型的声明

C语言提供了char、short、int、long等内置类型,但这些内置类型是不够用的,如我想要描述一本书,想要描述一本书单一的内置类型是不够用的,描述这本书需要作者、出版社、定价等。因此,C语言为了解决这一问题,就引入了结构体这种自定义的数据类型。

1.1结构的声明

struct tag
{
	member - list;
}variable-list;

 如描述一个学生:

struct student
{
	char name[20];//名字
	int age;//年龄
	int sex[5]//性别
};//结尾分号不能少

1.2结构体变量的定义和初始化

//变量的定义
struct Point
{
	int x;
	int y;
}p1;//声明类型的同时定义变量p1
//初始化
struct Stu
{
	char name[20];
	int age;
	char sex[5];
	char id[20];
};

int main()
{
	//按照结构体成员的顺序进行初始化
	struct Stu s = { "张三",20,"男","20230815648" };
	printf("name:%s\n", s.name);
	printf("age:%d\n", s.age);
	printf("sex:%s\n", s.sex);
	printf("id:%s\n", s.id);

	printf("\n");

	//按照指定的顺序初始化
	struct Stu s2 = { .age = 18,.name = "李四",.id = "21015120",.sex = "男" };
	printf("name:%s\n", s2.name);
	printf("age:%d\n", s2.age);
	printf("sex:%s\n", s2.sex);
	printf("id:%s\n", s2.id);
}

1.3结构的特殊声明

在声明结构的时候,可以不完全声明

如:

struct
{
	int a;
	char b;
	float c;
}x;

struct
{
	int a;
	char b;
	float c;
}a[20], *p;

上面两个结构在声明时省略了结构体的标签(tag)

注意:编译器会把上面的两个声明当成完全不相同的两个类型,故非法。

匿名的结构体类型,如果没有对结构体类型进行重命名的话,基本上只能使用一次。

2.结构成员访问操作符

2.1结构体成员的直接访问

结构体成员的直接访问是通过点操作符(.)访问的。如:

struct Point
{
	int x;
	int y;
}p = { 1,2 };
int main()
{
	printf("x=%d y=%d\n", p.x, p.y);
	return 0;
}

输出结果如下:

故使用方式:结构体变量.成员名

2.2结构体成员的间接访问

如果我们得到的不是一个结构体变量,而是得到一个指向结构体的指针,则我们就要用到结构体成员的间接访问。如下:

struct Point
{
	int x;
	int y;
};
int main()
{
	struct Point p = { 1,2 };
	struct Point* p1 = &p;
	printf("x=%d y=%d\n", p1->x, p1->y);
	p1->x = 10;
	p1->y = 20;
	printf("x=%d y=%d\n", p1->x, p1->y);
	return 0;
}

输出结果如下:

 使用方式:结构体指针->成员名

3.结构的自引用

错误示范:

struct po
{
	int a;
	struct po next;
};

在上述代码中,由于一个结构体再包含一个同类型的结构体变量,这样的结构体变量的大小是无穷大的,不合理。

正确的自引用方式:

struct po
{
	int a;
	struct po* next;
};

 需要用指针进行结构体的自引用。

4.结构体内存对齐

结构体的内存对齐就是计算结构体的大小。

4.1对齐规则

1.结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处。

2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数=编译器默认的一个对齐数与该成员变量大小的较小值

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

4.对于嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。

例:

struct s1
{
	char c1; //1字节
	int i;   //4字节
	char c2; //1字节
};
int main()
{
	printf("%zd\n", sizeof(struct s1));
	return 0;
}

输出结果:

首先,根据对齐规则第一条我们可以知道,结构体第一个成员的起始位置是偏移量为0的地址处 ,故第一个成员char c1放在偏移量为0的地址处。根据对齐规则第二条我们可以知道,其他成员变量要对齐到对齐数的整数倍的地址处,且对齐数=编译器默认的一个对齐数与该成员变量大小的较小值,由于vs中默认的对齐数是8,第二个成员的大小为4,小于8,因此对齐数是4,故第二个成员起始位置偏移量为4的倍数的地址处。由于第三个成员的对齐数是1,放在1的倍数处,故可以直接放在第二个成员的后面。因此这三个变量在内存中的存放如图:

我们可以看到,内存只占用了9个字节,但输出结果却是12。这是因为结构体总大小为最大对齐数的整数倍。在这个结构体中三个变量的对齐数依次是1、4、1,故最大对齐数是4,因此该结构体的大小是4的整数倍12。

结构体的内存对齐是拿空间来换取时间的做法。

4.2修改默认对齐数

在编译器中,默认的对齐数是可以通过#pragma这个指令进行修改。

 代码:

#pragma pack(1)//设置默认对齐数为1
struct s
{
	char c1;
	int i;
	char c2;
};
int main()
{
	printf("%zd\n", sizeof(struct s));
	return 0;
}

输出结构:

我们可以发现,改代码与上述例题相同,但输出的结构却不同,这是由于默认的对齐数被修改的缘故。当使用#pragma pack()这一语句时,可以取消设置的对齐数,还原为默认。

5.结构体实现位段

5.1什么是位段

1.位段的成员必须是int、unsigned int、signed int,在C99中位段的成员的类型也可以选择其他类型(常用的是char)

2.位段的成员名后面由一个冒号和一个数字

如:

struct A
{
	int a : 2;  //后面的数字表示占多大比特位
	int b : 5;
	int c : 10;
	int d : 30;
};
int main()
{
	printf("%zd\n", sizeof(struct A));
}

输出结果:

5.2位段的内存分配

1.位段的成员可以是int、unsigned int、signed int、char等类型

2.位段的空间上是按照需要以4个字节(int)或1个字节(char)的方式来开辟的

3.位段涉及很多不确定的因素,位段是不跨平台的,注重可移植的程序应避免使用位段

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值