自定义类型:结构体

一、结构体

1.1什么是结构体

C语言允许用户自己建立由不同类型数据组成的组合型的数据结构,它称为结构体(structre)。也可以称为“记录”(record)。

1.2定义结构体类型变量

struct是声明结构体类型时必须使用的关键字,不能省略。

声明一个结构体类型的一般形式为:

struct 结构体名
{
    成员列表
};

结构体名是由用户规定的,也可以称为“结构体标记”(structure tag),以区别于其它结构体类型。

花括号内时该结构体所包括的子项,称为结构体的成员(member)。

对各成员都应该进行类型声明,即:类型名 成员名;

定义结构体类型的三种方法 

1.先声明结构体类型,再定义该类型的变量

#include<stdio.h>

struct Student
{
	int num;//学号
	char name[20];//姓名
	int age;//年龄
	float score;//成绩
};

int main()
{
	struct Student student1, student2;//声明了两个结构体变量为student1,student2

	return 0;
}

这种方法是声明类型和定义变量分离,在声明类型后可以随时定义变量,较为灵活。

2.在声明类型的同时定义变量

这种定义方法的一般形式为:

struct 结构体名
{
    成员列表
}变量名列表;

举例: 

#include<stdio.h>

struct Student
{
	int num;//学号
	char name[20];//姓名
	int age;//年龄
	float score;//成绩
}student1,student2;

3.不指定类型名而直接定义结构体类型变量 

这种定义方法的一般形式为:

struct
{
    成员列表
}变量名列表;

在定义类型时不指定结构体名,也可以称这种定义方式为匿名 。

举例:

#include<stdio.h>

struct 
{
	int num;//学号
	char name[20];//姓名
	int age;//年龄
	float score;//成绩
}student1, student2;

1.3结构体变量的赋值

1. 按照结构体成员的顺序初始化

#include<stdio.h>

struct Student
{
	int num;//学号
	char name[20];//姓名
	char sex[20];//性别
	int age;//年龄
	float score;//成绩
};

int main()
{
	struct Student student1 = { 12138,"Lin","man",20,99 };

	printf("num:%d\nname:%s\nsex:%s\nage:%d\nscore:%d\n", student1.num, student1.name, student1.sex, student1.age, student1.score);

	return 0;
}

2.按照指定的顺序进行初始化 

#include<stdio.h>

struct Student
{
	int num;//学号
	char name[20];//姓名
	char sex[20];//性别
	int age;//年龄
	float score;//成绩
};

int main()
{
	struct Student student1 = { .age = 18,.num = 12138,.sex = "man",.score = 99,.name = "Lin" };

	printf("num:%d\nname:%s\nsex:%s\nage:%d\nscore:%d\n", student1.num, student1.name, student1.sex, student1.age, student1.score);

	return 0;
}

3.单独对结构体中的某个成员赋值 

#include<stdio.h>
#include<string.h>

struct Student
{
	int num;//学号
	char name[20];//姓名
	char sex[20];//性别
	int age;//年龄
	float score;//成绩
};

int main()
{
	struct Student student1, student2;

	//对变量的age赋值
	student1.age = 18;
	//或
	scanf("%d", &student2.age);

	//对student2的name赋值
	gets(student1.name);
	//或
	for (int i = 0; i < 20; i++)
	{
		scanf("%c", &student2.name[i]);
		if (i == 19)
		{
			student2.name[i] = '\0';
		}
	}
	
	//输出
	printf("%d\n", student1.age);
	printf("%d\n", student2.age);
	printf("%s\n", student1.name);
	printf("%s\n", student2.name);

	return 0;
}

在单独对结构体的数组成员赋值时,需要使用循环输入。

1.4内存对齐

结构体内存对齐是指编译器在分配结构体内存时,会按照特定的规则来对齐每个成员的地址,以提高内存访问效率和系统性能。内存对齐主要涉及两个方面:对齐要求和填充字节。 

1. 对齐要求

每种数据类型都有其特定的对齐要求,这通常由数据类型的大小决定。例如:

  • char 类型的对齐要求是 1 字节。
  • short 类型的对齐要求是 2 字节。
  • intfloat 类型的对齐要求是 4 字节。
  • double 类型的对齐要求是 8 字节。

2. 填充字节

为了满足对齐要求,编译器可能会在结构体成员之间或结构体末尾插入额外的字节,这些字节称为填充字节(padding bytes)。这些填充字节确保每个成员的地址都是其对齐要求的倍数。

3.对齐的实际规则

  1. 成员对齐:每个成员的地址必须是该成员大小的整数倍。
  2. 结构体对齐:结构体的总大小必须是最大对齐要求的成员大小的整数倍。

4.影响结构体对齐的因素

  1. 编译器:不同编译器对结构体内存对齐的处理可能有所不同。
  2. 硬件平台:不同的处理器架构可能对内存对齐有不同的要求。
  3. 编译选项:有些编译器允许通过选项指定内存对齐方式。

5.VS2022平台的结构体内存对齐 

在VS2022平台上,默认的对齐数为8个字节,如果结构体的某个成员所需要的字节数比默认字节数小,则选取小的作为对齐数。

结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的位置,其它成员要对齐到对齐数的整数倍的位置。

结构体每个成员变量都有一个对齐数,所有对齐数中最大的即为最大对齐数。

举例:

在上图中:

结构体成员a的类型为short,大小为2个字节,是结构体成员列表中的第一个成员,因此在内存中存放在起始位置偏移量为0的地方;

结构体成员d的类型为char,大小为1个字节,和VS默认对齐数8相较为小,因此对齐数为1,在内存中要存放到对齐数的整数倍位置,也就是偏移量为2的地方;

结构体成员b的类型为long,在x86环境中为4个字节, 和VS默认对齐数8相较为小,因此对齐数为4,在内存中偏移量为3的地方不是4的倍数,因此要浪费一个字节再继续存放;

结构体成员c的类型为long,大小为4个字节,偏移量为8的地方正好是4的倍数,因此可以存放无需浪费;

最大对齐数为4,所以该结构体类型的大小也必须为最大对齐数的倍数,也就是4的倍数,从上如可以看出,存放完所有结构体成员后共占了12个字节,是4的倍数。

综上所述,该结构体类型所占大小为12个字节。

更多相关练习可以查看0318结构体内存对齐的练习 · 81d129b · 霅霅/test_c - Gitee.com

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值