自定义类型:结构体

一、结构体

1.1什么是结构体

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

1.2定义结构体类型变量

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

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

cpp 复制代码
struct 结构体名
{
    成员列表
};

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

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

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

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

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

cpp 复制代码
#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.在声明类型的同时定义变量

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

cpp 复制代码
struct 结构体名
{
    成员列表
}变量名列表;

举例:

cpp 复制代码
#include<stdio.h>

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

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

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

cpp 复制代码
struct
{
    成员列表
}变量名列表;

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

举例:

cpp 复制代码
#include<stdio.h>

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

1.3结构体变量的赋值

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

cpp 复制代码
#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.按照指定的顺序进行初始化

cpp 复制代码
#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.单独对结构体中的某个成员赋值

cpp 复制代码
#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 字节。
  • int 和 float 类型的对齐要求是 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

相关推荐
青柠之夏cc35 分钟前
前端拖拽功能原生实现,不引入拖拽库完成业务
开发语言·前端·python
ym hyd 11142 分钟前
题库建立与考试系统源码 Java+SpringBoot+Vue3 前后分离
java·开发语言·vue.js·spring boot·毕设
OKkankan1 小时前
Python 高阶语法(一):高阶函数、闭包、lambda 与 functools——从底层真正理解装饰器
开发语言·python
geovindu1 小时前
rust: Facade Pattern
开发语言·后端·设计模式·rust·外观模式
List<String> error_P1 小时前
freertos数据结构--链表
数据结构·链表
福大大架构师每日一题1 小时前
Rust 1.99.0发布:C 可变参数、裸函数、Cargo 配置、Rustdoc 性能与大量兼容性调整全解析
c语言·开发语言·rust
智鸟科技GemeOpen开发者智能设备1 小时前
平安校园 AI 智能实时告警系统 智鸟科技·GemeOpen + 谷华科技·融合创新方案白皮书
java·开发语言·python·物联网·智能家居
Wang's Blog2 小时前
Java框架 SpringCloud 快速入门: Feign 的自定义配置与日志级别
java·开发语言·spring cloud
成旭先生2 小时前
企业全景信息查询 API:工商照面 33 项、股东出资、变更与社保一次查全
java·开发语言·api接口·企业信息查询·工商数据·风控尽调·供应商准入
2601_962885722 小时前
批量取数比循环单取快多少?用 AlphaFeed klines.batch给多股票取数提速
开发语言·php·batch