C语言 结构体

news2024/11/23 19:53:07

C语言 结构体

  • 一、结构体的声明和初始化
    • 1. 结构体声明
    • 2. 结构体初始化
  • 二、typedef 重定义结构体
  • 三、结构体成员的类型
  • 四、结构体成员的访问
  • 五、结构体传参
  • 六、结构体的自引用
  • 七、结构体的内存对齐
    • 对齐规则
    • 程序清单1
    • 程序清单2
    • 程序清单3
    • 程序清单4
    • 修改默认对齐数

一、结构体的声明和初始化

1. 结构体声明

程序清单:

#include <stdio.h>

struct Student {
	char name[20];		// 姓名
	int age;			// 年龄
	int studentID;		// 学号
}student1, student2;

int main() {

	struct Student student3;

	return 0;
}

2. 结构体初始化

程序清单:

#include <stdio.h>

struct Student {
	char name[20];			// 姓名
	int age;				// 年龄
	int studentID;			// 学号
}student1 = {"Rose", 19, 06}, student2={"小红", 17, 03};

int main() {

	struct Student student3 = {"Jack", 18, 02};
	
	return 0;
}

注意事项:

上面是结构体声明和初始化的标准格式,随着 Student 类型被创建出来,那么后面就可以根据其来创建相应的结构体变量:student1,student2,student3.

student1,student2 为全局变量,student3 为局部变量。

二、typedef 重定义结构体

#include <stdio.h>

typedef struct Student {
	char name[20];			// 姓名
	int age;				// 年龄
	int studentID;			// 学号
}Student;

int main() {

	Student student = { "Rose", 17, 03 };
	// struct Student student = ...

	return 0;
}

我们平时写类型的时候,都需要在自定义类型前加上 struct,才能够表示一个完整的结构体类型。而经 typedef 关键字重定义后, " struct Student " 和 " Student " 就是等价的了,这可以让我们后续更加简单地创建 Student 变量了。这里只需要注意写法即可。

三、结构体成员的类型

结构的成员可以是标量、数组、指针,甚至是其他结构体。如下所示:

程序清单:

#include <stdio.h>

struct Grade {
	int chinese;				// 语文成绩
	int math;					// 数学成绩
	int english;				// 英语成绩
};

struct Student {
	char name[20];				// 姓名
	int age;					// 年龄
	int studentID;				// 学号
	struct Grade grade;			// 成绩
};

int main() {

	struct Student student = { "Rose", 17, 03, {90, 95, 93} };
	printf("学生信息:%s %d %d\n", student.name, student.age, student.studentID);
	printf("学生成绩:%d %d %d\n", student.grade.chinese, student.grade.math, student.grade.english);

	return 0;
}

输出结果:

1-1

四、结构体成员的访问

在访问结构体成员时,可以采用 . 或者 -> ,前者对应结构体变量,后者对应结构体指针变量。

程序清单:

#include <stdio.h>

struct Student {
	char name[20];  // 名字
	int age;		// 年龄
	int studentID;  // 学号
};

int main() {
	struct Student student1 = {"Jack", 18, 32};  
	struct Student student2 = {"Bruce", 20, 05};
	printf("%s %d %d\n", student1.name, student1.age, student1.studentID);

	struct Student* ps1 = &student1;
	printf("%s %d %d\n", (*ps1).name, (*ps1).age, (*ps1).studentID);
	printf("%s %d %d\n", ps1->name, ps1->age, ps1->studentID);
	return 0;
}

输出结果:

1-2

五、结构体传参

程序清单:

#include <stdio.h>

struct Student {
	char name[20];		// 姓名
	int age;			// 年龄
	int studentID;		// 学号
};

void print1(struct Student student) {

	printf("%s %d %d\n", student.name, student.age, student.studentID);
}

void print2(struct Student* ps) {

	printf("%s %d %d\n", ps->name, ps->age, ps->studentID);
}

int main() {

	struct Student student1 = {"Jack", 18, 02};

	print1(student1);	// 1. 传值
	print2(&student1);	// 2. 传地址

	return 0;
}

输出结果:

1-3

注意事项:

① 函数在定义时,应该写在结构体之后。由于 main 函数作为程序的入口,之后在 main 函数中调用 print1 和 print2 函数时,程序就需要自上到下扫描,如果两个打印函数都写在了结构体之前,就会产生找不到结构体类型的现象。

② 上面在使用结构体传参时,采用了两个传参类型。
第一,传值调用 print1 函数;第二,传地址调用 print2 函数。然而,从调用函数的效率角度看,后者更加高效。

前者在传值时,传的是整个结构体过去的,如果结构体过大,形参在接收实参传来的结构体时,也要同样开辟一个与原结构体一样大的内存空间,后续才能接收结构体中各种各样的成员变量。然而,后者在传整个结构体的地址时,形参只需要拿一个结构体指针变量接收即可,因为一个指针变量占内存的大小要么是 4,要么是 8 ,所以整体系统开销较小。

此外,这里结构体传参应该与数组传参区分开来,如果将一个结构体变量作为函数的参数,那么传递的就是整个结构体;然而,如果将一个数组作为函数的参数,那么传递的就是数组首元素的地址。

综上所述,结构体在传参时,应该尽可能传它的地址,而不是结构体变量本身。

六、结构体的自引用

结构体的自引用常用于表示数据结构中的链表,结构体中包含数据域和指针域。

#include <stdio.h>

struct Node {
	int data;				// 数据域
	struct Node* next;		// 指针域
};

int main() {

	struct Node node1;

	return 0;
}

七、结构体的内存对齐

对齐规则

1. 第一个结构体成员在处于偏移量为 0 的地址处。

2. 其他结构体成员需要从对齐数的整数倍的偏移量地址处开始继续偏移。
( 对齐数 = 编译器默认的对齐数与该结构体成员大小两者比较后的较小值 )
在 VS 编译器中,默认值为 8;在 Linux 环境下,没有默认对齐数,此时结构体成员的自身大小就是它的对齐数。

3. 结构体占用内存的总大小为最大对齐数的整数倍,单位字节。
( 最大对齐数 = 所有结构体成员对齐数中的最大值 )

4. 如果出现了结构体嵌套结构体的情况,那么内部的结构体对齐到自己的最大对齐数的整数倍处。最终,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

程序清单1

#include <stdio.h>
#include <stddef.h>

struct S1 
{
	char c1;		// 1
	int i;			// 4
	char c2;		// 1
};

int main() {

	printf("%d\n", sizeof(struct S1)); // 12

	printf("%u ", offsetof(struct S1, c1)); // 0
	printf("%u ", offsetof(struct S1, i)); // 4
	printf("%u \n", offsetof(struct S1, c2)); // 8

	return 0;
}

注意事项:

offsetof 可以用来计算结构体成员的偏移量。

S1 所占内存的计算过程:

1-4

程序清单2

#include <stdio.h>
#include <stddef.h>

struct S2
{
	char c1;		// 1
	char c2;		// 1
	int i;			// 4
};

int main() {

	printf("%d\n", sizeof(struct S2)); 

	return 0;
}

// 输出结果:8

S2 所占内存的计算过程:

1-5

程序清单3

#include <stdio.h>
#include <stddef.h>

struct S3
{
	double d;		// 8
	char c;			// 1
	int i;			// 4
};

int main() {

	printf("%d\n", sizeof(struct S3));

	return 0;
}

// 输出结果:16

S3 所占内存的计算过程:

1-6

程序清单4

#include <stdio.h>
#include <stddef.h>

struct S3			// 16
{
	double d;		// 8
	char c;			// 1
	int i;			// 4
};

struct S4
{
	char c1;		// 1
	struct S3 s3;	// 最大对齐数 8
	double d;		// 8
};

int main() {

	printf("%d\n", sizeof(struct S4)); // 32

	printf("%u ", offsetof(struct S4, c1)); // 0
	printf("%u ", offsetof(struct S4, s3)); // 8
	printf("%u \n", offsetof(struct S4, d)); // 24

	return 0;
}

S4 所占内存的计算过程:

1-7

修改默认对齐数

#include <stdio.h>
#include <stddef.h>

struct S1 {
	char c;			// 1
	double d;		// 8
};

#pragma pack(4)
struct S2 {
	char c;			// 1
	double d;		// 8 (取对齐数为 4)
};
#pragma pack()

#pragma pack(1)
struct S3 {
	char c;			// 1
	double d;		// 8 (取对齐数为 1)
};
#pragma pack()

int main() {

	printf("%d\n", sizeof(struct S1)); // 16
	printf("%d\n", sizeof(struct S2)); // 12
	printf("%d\n", sizeof(struct S3)); // 9

	return 0;
}

1-8

注意事项:

在 VS 编译器中,对齐数默认值为 8;在 Linux 环境下,没有默认对齐数,此时结构体成员的自身大小就是它的对齐数。当我们为程序设计对齐数时,也应该考虑到硬件的访问情况,通常应该将对齐数设置为 2 的次方。

此外,在对齐数相同的情况下,我们应该尽可能地将占用空间小的结构体成员集中在一起。对比上面程序清单1 和 程序清单2,同样的对齐数,同样的程序,后者将 char 类型的结构体成员放在了一起,结果既满足了对齐,又节省了空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/51278.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

nvcc编译器之设备和主机独立编译(chapter 6)

目录 6. CUDA中的独立编译 6.1 单独编译时的代码改动 6.2 nvcc独立编译选项 6.3 库 6.4 示例 6.5 分布编译优化 6.6 独立编译的潜在问题 6. CUDA中的独立编译 在5.0版本之前&#xff0c;CUDA不支持分开编译&#xff0c;因此CUDA代码不能访问跨文件&#xff08;编译单元&…

挂耳式耳机品牌排行榜,五款目前排行靠前的耳机分享

耳机传声的方式无非就是空气传播以及骨骼传播&#xff0c;而骨传导耳机就属后者&#xff0c;通过骨骼震动来完成声波的传递&#xff0c;在传递的过程无需经过外耳道和鼓膜&#xff0c;在一定程度上缓解了对耳道造成的损伤&#xff0c;减少对于耳道的负担&#xff0c;看到这里如…

OSI参考模型个人总结

附&#xff1a;参考模型 OSI参考模型 基于国际标准化组织ISO的建议&#xff0c;作为各种层上使用的协议国际标准化的第一步发展起来的&#xff0c;被称作ISO开放系统互联参考模型&#xff08;open system interconnection refertence model&#xff09;&#xff0c;简称为OSI模…

[附源码]Python计算机毕业设计Django高校后勤保障系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

【解决问题】413错误 413 Request Entity Too Large 接口返回413 报413nginx

文章目录问题排查解决方案1、修改nginx配置文件nginx.conf2、更新完成后需要重启nginx3、其他可能性博客背景&#xff1a;JAVA项目&#xff0c;前端想弄个便捷富文本&#xff0c;直接很多图片转base64编码直接存库了。字段为longtext类型。这种问题通常是在使用http请求对象太大…

python实现中缀表达式转后缀表达式

前缀、中缀、后缀表达式&#xff08;逆波兰表达式&#xff09; 前缀表达式称为波兰表达式&#xff0c;前缀表达式的运算符位于操作符之前 举例说明&#xff1a;&#xff08;34&#xff09;x 5 – 6 对应的前缀表达式就是- X 3 4 5 6 中缀表达式转为后缀表达式&#xff1a; …

概率论与数理统计——事件间的关系

包含 事件A发生必然导致事件B发生。 代数中经常用这种方法证明两个事件相等。 事件的并&#xff08;和&#xff09; A与B至少有一个发生 事件的交&#xff08;积&#xff09; A与B同时发生 无限可列个&#xff1a;能按某种规律能把他排成一个序列&#xff08;实变函数…

Typora导出Word

1.Typora导出Word配置 1.访问GitHub&#xff1a; https://github.com/jgm/pandoc/releases/tag/2.11.3.2https://github.com/jgm/pandoc/releases/tag/2.11.3.2 2.下载对应的文件 3.到本地安装Typora目录下解压 4.配置环境变量 我的电脑–属性–高级系统设置–高级–环境变量…

Python数据分析案例12——网飞影视剧数据分析及其可视化

背景介绍 Netflix是最受欢迎的媒体和视频流平台之一。他们的平台上有超过 8000 部电影或电视节目。截至 2021 年年中&#xff0c;他们在全球拥有超过 2 亿订阅者。 博主看美剧也较为多&#xff0c;像《怪奇物语》、《性爱自修室》等高分美剧都是网飞的。 对于网飞的影视剧&a…

【Docker学习系列】Docker学习1-docker安装

从本篇开始&#xff0c;凯哥将和大家一起学学docker。本篇是docker学习系列第一篇&#xff1a;安装docker。 docker安装前提条件&#xff1a;目前,centos发行版中的内核支持Docker.Docker运行在Centos7 64位上&#xff0c;要求系统为64位、linux系统内核版本为3.8以上的。凯哥所…

mybatis实战:三、mybatis多表查询的映射

依照上一期的继续 1.UserMapper.xml 除了直接对应基本数据类类型、表的实体类&#xff0c;还可能用到多表查询。 <select id"selectRolesByUserId" resultType"tk.mybatis.simple.model.SysRole">select r.id, r.role_name roleName, r.enabled, r…

一篇个人陈述应该包括这三个内容

大家好呀&#xff0c;申请季正如火如荼地进行着&#xff0c;不知道大家都忙碌得怎么样了呢&#xff1f;今天我们来聊聊个人陈述Personal statement,的撰写&#xff0c;包括一篇个人陈述应该包括哪些部分。 申请时&#xff0c;除了学习成绩等“硬背景”&#xff0c;个人陈述(PS…

五款朴实无华却又能极大提升办公效率的软件

最近后台收到好多小伙伴的私信&#xff0c;今天继续推荐五款小工具&#xff0c;都是免费使用的&#xff0c;大家可以去试试看。 1.光追动画制作——Luxion KeyShot Luxion KeyShot是一款互动性的光线追踪与全域光渲染3D渲染与动画制作软件&#xff0c;内置丰富多样的材质&…

Redis使用基础教程

本篇文章转载自&#xff1a;通俗易懂的Redis数据结构基础教程_Java程序员-张凯的博客-CSDN博客 Redis有5个基本数据结构&#xff0c;string、list、hash、set和zset。它们是日常开发中使用频率非常高应用最为广泛的数据结构&#xff0c;把这5个数据结构都吃透了&#xff0c;你…

Spring Security-全面详解(学习总结---从入门到深化)

目录 Spring Security介绍 Spring Security认证_项目搭建 Spring Security认证_内存认证 Spring Security认证_UserDetailsService Spring Security认证_数据库认证 Spring Security认证_PasswordEncoder Spring Security认证_自定义登录页面 Spring Security认证_会…

package-info.java

package-info.java 文件估计大家见过但是自己却很少去创建和使用它、因为对于一般应用来说可能真的太少见了。 它的作用主要是三个 描述包使用注解修饰包、达到修饰该包下的类声明包中使用的类和常量(这个比较少用) 描述包 package-info.java 文件 /*** 我是描述信息*/ pa…

如何用蓝牙实现无线定位(三)--本地定位显示

1. 被定位目标 本项目设计有两个定位装置&#xff0c;一个用于固定目标&#xff0c;一个用于可移动设备。在定位系统的帮助下&#xff0c;我们可以操作可移动设备向固定目标移动。假设这是一个救援场景的话&#xff0c;我们就可以把固定的目标看作等待救援的人或物&#xff0c;…

【使用 BERT 的问答系统】第 2 章 :用于自然语言处理的神经网络

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

大数据开发之词频统计传参打包成jar包发送到Hadoop运行并创建可执行文件方便运行

文章目录添加spark的jar包main传参调试打包成jar包发送到Hadoop运行使用脚本运行参考添加spark的jar包 点击Project Structure Global Libararies中 点击 选择java 然后选择spark文件里的jars下所有的jar包 然后点击ok即可。 main传参调试 首先给出词频统计代码 //包 imp…

OpenCV图像处理——光流估计

总目录 图像处理总目录←点击这里 二十二、光流估计 22.1、原理 光流 是空间运动物体在观测成像平面上的像素运动的“瞬时速度”&#xff0c;根据各个像素点的速度矢量特征&#xff0c;可以对图像进行动态分析&#xff0c;例如目标跟踪。 亮度恒定&#xff1a;同一点随着时…