【C语言__结构体__复习篇3】

news2025/4/8 21:41:51

目录

前言

 一、结构体基础知识

        1.1 结构体的语法形式

        1.2 创建结构体变量

        1.3 结构体变量的初始化

        1.4 点(.)操作符和箭头(->)操作符

二、匿名结构体

三、结构体自引用 

四、结构体内存对齐

        4.1 内存对齐的规则

        4.2 出现结构体内存对齐的原因

        4.3 修改默认对齐数

五、结构体传参

六、结构体实现位段

        6.1 什么是位段

        6.2 位段成员的内存分配

        6.3 位段的跨平台问题

        6.4 位段使用的注意事项


前言

本篇主要讨论以下问题:

结构体基础知识:

1. 结构体是用来做什么的,结构体的语法形式是怎样的

2. 如何创建一个结构体类型,结构体的全局变量和局部变量如何创建

3. 如何初始化结构体变量,如何自定义顺序初始化结构体变量,结构体中结构体如何初始化

4. 如何找到结构体变量的成员变量

匿名结构体:

5. 匿名结构体如何创建,它有什么需要注意的点

结构体自引用:

6. 结构体如何自引用,结构体自引用时有什么需要注意的点

结构体内存对齐:

7. 结构体内存对齐的规则有哪几点,怎样计算结构体成员变量相对于默认起始地址的偏移

8. 为什么会出现结构体内存对齐,如何改变结构体的默认对齐数

结构体传参:

9. 结构体传参采用传值调用还是传址调用好,为什么

结构体实现位段:

10. 结构体如何实现位段,用结构体实现位段有什么优点和缺点

11. 用结构体实现位段,位段的成员在内存中如何开辟空间的

12. 我们在使用位段时有什么注意事项,采用位段的示例

 一、结构体基础知识

1. 结构体是用来描述复杂对象的,例如,当我们想要描述学生、商品、书籍等自身包含多信息的复杂对象时,就可以创建结构体类型,再去创建结构体变量。

1.1 结构体的语法形式

1. 语法形式:struct tag

                      {

                              成员列表...

                      }变量列表;

① struct是结构体关键字,tag是结构体标签名,struct tag整体表示这个结构体类型的名称。

② { }内的成员列表,用于表示这个结构体类型中有哪些类型的变量(即,复杂对象包含哪些信息),这些成员变量不需要初始化。

③ 变量列表处创建的变量是全局变量,在此处我们可以一次性创建多个全局变量。

2. 创建结构体类型的举例:

    struct stu

    {

            char name[20];

            int age;

            int id[10];

    };

1.2 创建结构体变量

struct Stu
{
	char name[20];
	int age;
	char id[10];
}s1, s2;//全局变量

int main()
{
	struct Stu s3;//局部变量
	struct Stu s4;//局部变量
	return 0;
}

1.3 结构体变量的初始化

1. 按结构体成员的顺序初始化结构体变量,用{ }像数组一样直接初始化即可。

2. 自定义顺序初始化结构体变量,采用(.)找到结构体成员名,再赋值即可。

3. 结构体中的结构体的初始话也是用{ }, 类似于二维数组中一维数组的初始化。

struct Stu
{
	char name[20];
	int age;
	char id[10];
}s1 = { "张三", 18, "10023211" }, s2 = {"翠花", 19, "10023245"};

int main()
{
	struct Stu s3 = {"lisi", 17, "10023233"};
	struct Stu s4 = {.age = 16, .id = "10012323", .name = "kiki"}; //自定义顺序
	return 0;
}
struct Point
{
	int x;
	int y;
};

struct test
{
	float score;
	struct Point k;
};

int main()
{
	struct test t1 = { 90.8f, {2, 4} };//结构体中的结构体成员初始化
	struct test t2 = {.k.y = 4, .k.x = 9, .score = 89.7f};//自定义顺序
	return 0;
}

1.4 点(.)操作符和箭头(->)操作符

1. 点操作符:结构体变量名.成员名

2. 箭头操作符:结构体指针->成员名(表示通过地址找到它所指向的结构体变量的某个成员)

#include <stdio.h>

struct Stu
{
	char name[20];
	int age;
	char id[10];
}s1 = { "张三", 18, "10023211" }, s2 = {"翠花", 19, "10023245"};

int main()
{
	struct Stu s3 = {"lisi", 17, "10023233"};
	struct Stu s4 = {.age = 16, .id = "10012323", .name = "kiki"}; //自定义顺序

	struct Stu* ps3 = &s3;
	struct Stu* ps4 = &s4;
	printf("%s %d %s\n", s1.name, s1.age, s1.id);
	printf("%s %d %s\n", s2.name, s2.age, s2.id);
	printf("%s %d %s\n", ps3->name, ps3->age, ps3->id);
	printf("%s %d %s\n", ps4->name, ps4->age, ps4->id);
	return 0;
}

 

二、匿名结构体

1. 匿名结构体,即结构体类型在定义时 tag 不写。

2. 匿名结构体的特点,可以定义多个全局变量,但不可以定义局部变量。(如果对这个匿名结构体用typedef重命名后,是可以定义局部变量的)

3. 定义两个完全相同的匿名结构体类型,会被编译器认为是不同的结构体类型。

struct
{
	int num1;
	float num2;
};//匿名结构体

三、结构体自引用 

1. 结构体自引用,即在结构体类型定义时,结构体成员变量中存在本结构体类型的指针变量。

2. 匿名结构体不能自引用,这样写的代码可读性很差。

3. 一般在结构体自引用时,会先定义结构体,再用typedef对结构体重命名,注意!重命名结构体后不需要去更改自引用指针变量的类型名称,否则会出现未定义先使用的错误。

typedef struct Stu
{
	char name[20];
	struct Stu* ps;
}Stu;

typedef struct Stu
{
	char name[20];
	Stu* ps;//err,先使用后定义的错误
}Stu;

四、结构体内存对齐

1. 结构体内存对齐==结构体大小如何计算。

4.1 内存对齐的规则

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

2. 其他成员变量要对⻬到对⻬数的整数倍的偏移地址处。

    对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。

    - VS 中默认的值为 8

    - Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。

4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍的偏移位置处,结构体的整体⼤⼩是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

补充:计算结构体成员变量相对于默认起始地址的偏移量用到的宏:offsetof(type, member),type是结构体类型,member是结构体成员名,头文件<stddef.h>,计算结果为size_t类型。

 

 4.2 出现结构体内存对齐的原因

1. 平台原因 (移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2. 性能原因: 数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。

总体来说:结构体的内存对⻬是拿空间来换取时间的做法。

补充:在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,如何做到:让占⽤空间⼩的成员尽量集中在⼀起

4.3 修改默认对齐数

1. #pragma 这个预处理指令,可以改变编译器的默认对⻬数。

2. 修改的默认对齐数不要用除1之外的奇数,因为类型的大小通常为偶数。(通常会将默认对齐数设置成1)

#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{
     char c1;
     int i;
     char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认
int main()
{
     //输出的结果是什么?
     printf("%d\n", sizeof(struct S));
     return 0;
}

五、结构体传参

1. 直接传结构体变量名,函数形参用结构体变量接收。

2. 传结构体变量地址,函数形参用结构体变量指针接收。

3. 结构体传参采用传结构体变量地址好,因为函数传参的时候,参数是需要压栈的,会有时间和空间上的系统开销,如果传递⼀个结构体对象所需开辟的内存空间过⼤,会导致参数压栈的系统开销⽐较⼤,代码性能的下降。

六、结构体实现位段

6.1 什么是位段

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

1. 位段的成员必须是 int、unsigned int 或 signed int ,在C99中位段成员的类型也可以选择其他类型。

2. 位段的成员名后边有⼀个冒号和⼀个数字。

struct A
{
     int _a:2;
     int _b:5;
     int _c:10;
     int _d:30;
};

 6.2 位段成员的内存分配

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

2. 位段的内存空间上是按照需要以一次开辟4个字节( int )或者1个字节( char )的⽅式来申请内存空间的。

3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。(要想知道位段成员在内存中如何分配空间的,需要针对不同的平台去研究)

6.3 位段的跨平台问题

1. int 位段被当成有符号数还是⽆符号数是不确定的。

2. 位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会出问题)

3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

4. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃剩余的位还是利⽤,这也是不确定的。

总结: 跟结构相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

6.4 位段使用的注意事项

1. 位段的⼏个成员可能共用同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置。内存中每个字节有唯一的⼀个地址,⼀个字节内部的bit位是没有地址的。 所以不能对位段的成员使⽤&操作符,这样也就意味着不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员。

2. 采用位段的示例:⽹络协议中,IP数据报就是使用的结构体位段。

struct A
{
	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;
};

int main()
{
	struct A sa = { 0 };
	//scanf("%d", &(sa.b));//这是错误的

	//正确的⽰范
	int b = 0;
	scanf("%d", &b);
	sa.b = b;
	return 0;
}

本篇文章已完结,谢谢支持!!! 

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

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

相关文章

【YOLOv9】完胜V8的SOTA模型Yolov9(论文阅读笔记)

官方论文地址&#xff1a; 论文地址点击即可跳转 官方代码地址&#xff1a; GitCode - 开发者的代码家园 官方代码地址点击即可跳转 1 总述 当输入数据经过各层的特征提取和变换的时候&#xff0c;都会丢失一定的信息。针对这一问题&#xff1a; 论文中提出的可编程梯度信息…

高颜值的B端界面,如果漂亮也有错,就让它错下去吧。

分享一波漂亮的B端界面&#xff0c;内行看门道&#xff0c;外行看热闹&#xff0c;不喜勿喷昂。

防止狗上沙发,写一个浏览器实时识别目标检测功能

家里有一条狗&#x1f436;&#xff0c;很喜欢乘人不备睡沙发&#x1f6cb;️&#xff0c;恰好最近刚搬家 狗迎来了掉毛期 不想让沙发上很多毛。所以希望能识别到狗&#xff0c;然后播放“gun 下去”的音频&#x1f4e3;。 需求分析 需要一个摄像头&#x1f4f7; 利用 chrome…

反激电源RC吸收电路设计

一、什么是RC吸收&#xff1f; RC吸收是指在电路设计中&#xff0c;尤其是在开关电源、功率电子设备以及电力电子系统中&#xff0c;使用电阻与电容串联组成的电路结构&#xff0c;用于吸收和衰减电路中由于开关元件&#xff08;如MOSFET、IGBT等&#xff09;的快速切换所产生的…

Blender2.83 下载地址及安装教程

Blender是一款开源的3D计算机图形软件&#xff0c;广泛应用于动画制作、游戏开发、建模、渲染等领域。它提供了一套强大的工具和功能&#xff0c;让用户能够进行三维建模、动画制作和视觉效果的创作。 Blender支持多种文件格式的导入和导出&#xff0c;使用户能够与其他软件进…

从汇编指令看函数调用堆栈的详细过程

我们以下述代码为例来说明函数调用堆栈的详细过程 #include<iostream>int sum(int a,int b) {int temp0;tempab;return temp; }int main() {int a10;int b20;int retsum(a,b);std::cout<<"ret:"<<ret<<std::endl;return 0; } 接下来&#…

粒子群优化算法PSO与鹈鹕优化算法(POA)求解无人机三维路径规划(MATLAB代码)

一、无人机路径规划模型介绍 二、算法介绍 close all clear clc dbstop if all error warning (off) global model model CreateModel(); % 创建模型 FF1; [Xmin,Xmax,dim,fobj] fun_info(F);%获取函数信息 pop100;%种群大小(可以自己修改) maxgen100;%最大迭代次数(可以自己…

电商数据采集的网页抓取数据、淘宝、天猫、京东等平台的电商数据抓取|电商数据API接口网页爬虫、采集网站数据

电商数据采集的网页抓取数据、淘宝、天猫、京东等平台的电商数据抓取&#xff0c;网页爬虫、采集网站数据、网页数据采集软件、python爬虫、HTM网页提取、APP数据抓包、APP数据采集、一站式网站采集技术、BI数据的数据分析、数据标注等成为大数据发展中的热门技术关键词。那么电…

以太网数据量大小字符串生成方法(可变单位)

0 前言 当我们想显示以太网数据量大小时&#xff0c;往往有个头疼的单位需要处理&#xff0c;单位取小了不一目了然&#xff0c;单位取大了精度太低。本例设计一个函数&#xff0c;将根据以太网数据量大小自动生成单位可变的字符串&#xff08;KB、MB、GB、TB、PB&#xff09;…

CSS之固定定位、相对定位、绝对定位

一、相对定位 相对元素自身所在的原来的位置进行定位&#xff0c;可以设置 left&#xff0c;right&#xff0c;top&#xff0c;bottom四个属性。 效果&#xff1a;在进行相对定位以后&#xff0c;元素原来所在的位置被保留了&#xff0c;既保留占位&#xff0c;其他元素的位置…

软件全周期检查单

1、立项阶段 2、计划阶段 3、需求阶段 4、设计阶段 5、编码集成阶段 6、测试阶段 7、交付阶段 8、结项阶段 软件全套资料包获取进主页或本文末个人名片直接获取。

Bezier曲线的绘制 matlab

式中&#xff1a; 称为基函数。 。 因为n表示次数&#xff0c;点数为n1&#xff0c;显然i表示第i个控制点。 显然在Matlab中可以同矩阵的形式来计算C(u)。 关键代码为&#xff1a; clc clear % 假设控制点P取值为&#xff1a; P [4,7;13,12;19,4;25,12;30,3]; % 因此&a…

5 个让日常编码更简单的 Python 库

今天我们一起来研究一些非常有用的第三方模块&#xff0c;可以使得我们的日常编码变得更加简单方便 sh https://github.com/amoffat/sh 如果曾经在 Python 中使用过 subprocess 库&#xff0c;那么我们很有可能对它感到失望&#xff0c;它不是最直观的库&#xff0c;可能还有些…

C语言中局部变量和全局变量是否可以重名?为什么?

可以重名 在C语言中, 局部变量指的是定义在函数内的变量, 全局变量指的是定义在函数外的变量 他们在程序中的使用方法是不同的, 当重名时, 局部变量在其所在的作用域内具有更高的优先级, 会覆盖或者说隐藏同名的全局变量 具体来说: 局部变量的生命周期只在函数内部,如果出了…

mars3d.MaterialType.Image2修改配置面状:图片2的speed数值实现动画效果说明

摘要&#xff1a; mars3d.MaterialType.Image2修改配置面状&#xff1a;图片2的speed数值实现动画效果说明 前提&#xff1a; 1.在示例中&#xff0c;尝试给mars3d.MaterialType.Image2材质的图片加上speed参数&#xff0c;实现动画效果&#xff0c;但是没有看到流动效果说明…

酷漫熊,最好用的漫画工具,资源全,支持下载,全部免费!

hi&#xff0c;大家好我是技术苟&#xff0c;每周准时上线为你带来实用黑科技&#xff01;由于公众号改版&#xff0c;现在的公众号消息已经不再按照时间顺序排送了。因此小伙伴们就很容易错过精彩内容。喜欢黑科技的小伙伴&#xff0c;可以将黑科技百科公众号设为标星&#xf…

互联网轻量级框架整合之MyBatis核心组件

在看本篇内容之前&#xff0c;最好先理解一下Hibernate和MyBatis的本质区别&#xff0c;这篇Hibernate和MyBatis使用对比实例做了实际的代码级对比&#xff0c;而MyBatis作为更适合互联网产品的持久层首选必定有必然的原因 MyBatis核心组件 MyBatis能够成为数据持久层首选框&a…

中国移动传关停8元保号套餐?或是5G成本带来的压力所致

日前有网友发现希望使用中国移动的保号套餐&#xff0c;却发现已无法办理&#xff0c;媒体对此多有报道&#xff0c;这意味着中国移动的套餐业务发生了重大变动&#xff0c;如此做或许在于5G成本上涨带来的压力促使它不得不提高套餐的门槛。 中国移动已建成最多的5G基站&#x…

世界各国柴油价格22.7统计

数据详情介绍&#xff1a; 统计时间为2022年7月4日。在该月份&#xff0c;全球柴油的平均价格为每升1.43美元。然而&#xff0c;各国间存在明显的价格差异。一般而言&#xff0c;西欧等发达国家的价格基本在每升2美元以上&#xff1b;相反&#xff0c;像伊朗、委内瑞拉、利比亚…

基于springboot实现视频网站管理系统【项目源码+论文说明】计算机毕业设计

基于springboot实现视频网站管理系统演示 摘要 使用旧方法对视频信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在视频信息的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问…