C 语言结构体
本博客涉及的结构体知识有:
1.0:结构体的创建和使用
2.0: typedef 关键字与#define 关键字的区别
3.0: 结构体成员的访问【地址访问与成员访问】
4.0: 结构体嵌套调用
5.0 数组访问赋值结构体成员
......
1.0:结构体的创建和使用
结构体类型和枚举类型一样,是一种用户自定义的数据类型,它可以使用一个变量来描述事物的多种属性,便于数据的管理,数据类型声明的语法格式:或者说->【结构是一些值的集合,这些值被称为成员变量,结构体中的成员可以是不同的变量类型 】,和数组相比结构体的数据类型更为的灵活,数组指的是一组相同类型的值的集合,数组的下标是从0开始的。
格式1:
// 结构体的声明格式
typedef struct
{成员列表
}structName_t;注:成员列表中的成员并不是变量在什么的时候并不会开辟内存空间内存空间的开辟要在创建结构体变量之后************************************ 结构体: 创建结构体案例* 参数 : 结构体成员 char name[20]; char tel[12];* 返回值: 无* 时间 : 2024/7/13 * 作者 : _沧浪之水_************************************
**/
typedef struct
{char name[20];char tel[12];char sex[5];int high;
}People_t;
格式2:
结构体变量的声明struct Person
{
// 成员列表char name[20];int age;
}struct_name_t;上面的结构体是一个结构体数据类型,并不是一个变量,创建结构体类型的时候并不会开辟内存空间struct Stu
{
// 成员列表char name[20];int age;
}s1,s2;
s1 与 s2 是 struct Stu 类型的变量(s1 s2 是结构体成员的全局变量)
格式3:
// 结构体成员变量声明
struct Point
{int x;int y;}p1 = {2,3};struct score
{int n;char ch;};struct Stu
{char name[20];int age;struct score s;};成员变量的初始化
int main()
{struct Stu s1 = {"8888",20,{100,'q'}};printf(%s %d %d %c,s1.name,s1.age,s1.n,s1.s.ch);}
结构体变量的定义
/************************************** 结构体: 结构体变量定义后同时进行赋值* 参数 : 无参数* 返回值: 无返回值* 时间 : 2024/7/13 * 作者 : _沧浪之水_************************************
**/
People_t peo = { "张三","15863310892","男",181 };
2.0 typedef 关键字的使用
typedef关键关键字:用于定义一个已有关键字的别名,具体创建格式如下所示
/************************************** 结构体: typedef 关键字的用法* 参数 : 无参数* 返回值: 无返回值* 时间 : 2024/7/13 * 作者 : _沧浪之水_************************************
**/
typedef 数据类 名字typedef uint8_t uint;typedef unsigned char uchar;
typedef 关键字和define关键字的区别,两者都是给对象取一个别名增强程序的可读性【尽量防止程序中魔鬼数字的出现】,两者有如下的区别
1: 使用场景不同
- typedef 关键字用于给数据类型定义别名
- #define关键字又称为(宏定义) 用于给数字,表达式(写表达式时注意带上括号防止异常问题的出现),代码语句定义别名。
2:执行时机不同
- typedef在编译阶段执行;
- #define在预编译阶段执行;
3:定义方法不同
- #define别名在替换对象的前面,并且定义后面不用加分号;
#define PI 3.14#define MAX_NUM_LIST 9
typedef的别名在替换对象的后面,并且定义后面需要加分号;
typedef unsigned char uchar;
3.0: 结构体成员的访问
注:有指针访问和圆点运算符访问两种访问方式
使用圆点运算符并打印输出
/************************************** 结构体: 结构体成员变量访问* 参数 : 结构体成员访问* 返回值: 无* 时间 : 2024/7/13 * 作者 : _沧浪之水_**************************************/// peo 是结构体变量,结构体变量创建后才会开辟内存控制,结构体变量初始化People_t peo = { "张三","15863310892","男",181 };// 浮点数在内存中不能精确的存储,结构体嵌套初始化recallStruct_t recal = { {"李四","15863310892","男",181},100, 88.8f };printf("%s %s %s %d\n", peo.name, peo.tel, peo.sex, peo.high);printf("%s %s %s %d %d %f\n", recal.p.name,recal.p.tel,recal.p.sex, recal.p.high,recal.num,recal.f);
使用指针访问【地址访问】
/************************************** 结构体: 结构体传递地址调用* 参数 : *p 指针地址* 返回值: 无* 时间 : 2024/7/13 * 作者 : _沧浪之水_************************************
**/
void Print(People_t *p)
{// 左边的是结构体指针 “结构体指针->成员变量”printf("%s %s %s %d\n", p->name, p->tel, p->sex, p->high);
}
注:以上的这种写法需要在主函数中进行调用
4.0: 结构体嵌套调用
#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include <string.h>
#include "add.h"
#include <stdint.h>/************************************** 结构体: 创建结构体* 参数 : 结构体成员 char name[20]; char tel[12];* 返回值: 无* 时间 : 2024/7/13* 作者 : _沧浪之水_************************************
**/
typedef struct
{char name[20];char tel[12];char sex[5];int high;
}People_t;/************************************** 结构体: 创建结构体* 参数 : 结构体成员,内部包含结构体【顺带变量初始化,结构体初始化】* 返回值: 无* 时间 : 2024/7/13* 作者 : _沧浪之水_************************************
**/
typedef struct
{People_t p;int num;float f;
}recallStruct_t;/************************************** 结构体: 结构体传递地址调用* 参数 : *p 指针地址* 返回值: 无* 时间 : 2024/7/13* 作者 : _沧浪之水_************************************
**/
void Print(People_t *p)
{// 左边的是结构体指针 “结构体指针->成员变量”printf("%s %s %s %d\n", p->name, p->tel, p->sex, p->high);
}/************************************** 结构体: 结构体成员变量访问* 参数 : 结构体成员访问* 返回值: 无* 时间 : 2024/7/13* 作者 : _沧浪之水_************************************
**/
void PrintTwo(People_t input)
{// 左边的是结构体变量 “结构体变量.成员变量”printf("%s %s %s %d\n", input.name, input.tel, input.sex, input.high);
}int main()
{// peo 是结构体变量,结构体变量创建后才会开辟内存控制,结构体变量初始化People_t peo = { "张三","15863310892","男",181 };// 浮点数在内存中不能精确的存储,结构体嵌套初始化recallStruct_t recal = { {"李四","15863310892","男",181},100, 88.8f };printf("%s %s %s %d\n", peo.name, peo.tel, peo.sex, peo.high);printf("%s %s %s %d %d %f\n", recal.p.name, recal.p.tel, recal.p.sex, recal.p.high, recal.num, recal.f);Print(&peo);PrintTwo(peo);return 0;
}
注:以上的注释编写方式仅限于更好的理解知识,实际的开发过程会更为规范,推荐书籍《高质量 C C++ 编程指南 》。
注:上面的两种打印输出方式哪一种打印方式更好,结构体传递参数吧 “对象” 进行结构体传参的时PrintTwo() 这种参数传递方式方式时PrintTwo(peo),peo实际是一个对象已经在内存中开辟了一块内存空间,如果把对象作为实际参数传递给形参的时候“形参里面的内容实际上是实际参数的一份拷贝” 打印peo的时候打印输出的数据是一样的,空间和时间的浪费会降低程序的性能。【推荐使用地址传递的方式把参数的地址传递进去(地址的大小就是4-8字节)通过地址找到数据】参数传递的时候会压栈,结构体传递参数的时候尽量传递结构体的地址节省时间和空间。
5.0 数组方式给结构体成员赋值
#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include <string.h>
#include "add.h"
#include <stdint.h>#define MAX_LIST_NUM (sizeof(arrList) / sizeof(arrList[0]))/************************************** 结构体: 创建结构体* 参数 : 结构体成员 char name[20]; char tel[12];* 返回值: 无* 时间 : 2024/7/13* 作者 : _沧浪之水_************************************
**/
typedef struct
{char name[20];char tel[12];char sex[5];int high;
}People_t;static People_t arrList[] =
{{"Keil","122345625","男",180},{"Ling","122345625","女",185}
};int main()
{uint8_t i = 0;for (i = 0; i < MAX_LIST_NUM; i++) {printf("%s %s %s %d\n", arrList[i].name, arrList[i].tel, arrList[i].sex, arrList[i].high);}return 0;
}
6.0 结构体内存对齐
现代处理器在读取或写入内存时,倾向于处理特定长度的数据,这些长度通常是2字节、4字节、8字节等。这是因为处理器内部的总线宽度、缓存行大小和寄存器大小通常是这些值的倍数。当数据对齐到这些边界时,处理器可以更高效地读取或写入数据,避免了多次内存访问或者额外的数据重排操作,从而提高了执行速度。
-
硬件限制
某些硬件平台不能在任意内存地址上访问任意数据类型。例如,一些处理器可能无法直接读取非对齐的长字节数据,尝试这样做可能会导致硬件异常,比如对齐错误(alignment fault)。
-
跨平台和移植性
不同的硬件架构可能有不同的内存对齐偏好。如果一个结构体在一个架构下没有正确对齐,而在另一个架构下被读取,可能会导致不可预测的行为或错误。通过遵循标准的对齐规则,可以增强代码的跨平台兼容性和可移植性。
-
内存访问模式
对齐也有助于优化缓存行为。缓存通常以特定大小的块存储数据,如果数据对齐得当,就更有可能在缓存命中时加载更多的相关数据,从而减少缓存缺失和提高性能。
结构体内存定义示意图:
offsetof结构体偏移量验证
#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include <string.h>
#include "add.h"
#include <stdint.h>struct s1
{char a; int i;char b;
};struct s2
{char b;char a;int i;
};int main()
{struct s1 sOne;struct s2 sTwo;printf("%d\n", sizeof(struct s1));printf("%d\n", sizeof(struct s2));printf("|+----------+|----------+|---------+|-----------|");// 使用offsetof计算偏移量printf("%d\n", offsetof(struct s1,a));printf("%d\n", offsetof(struct s1,i));printf("%d\n", offsetof(struct s1,b));return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include <string.h>
#include "add.h"
#include <stdint.h>struct s1
{char a; int i;char b;
};struct s2
{char b;char a;int i;
};int main()
{struct s1 sOne;struct s2 sTwo;printf("%d\n", sizeof(struct s1));printf("%d\n", sizeof(struct s2));printf("|+----------+|----------+|---------+|-----------|");printf("\n");// 使用offsetof计算偏移量printf("%d\n", offsetof(struct s1,a));printf("%d\n", offsetof(struct s1,i));printf("%d\n", offsetof(struct s1,b));printf("%d\n", offsetof(struct s2, b));printf("%d\n", offsetof(struct s2, a));printf("%d\n", offsetof(struct s2, i));return 0;
}
计算结构体成员的大小
嵌套结构体大小计算
结构体修改默认对齐数
#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include <string.h>
#include "add.h"
#include <stdint.h>// 修改默认对齐数
#pragma pack(4)
struct S
{int i; // 4 ----- > 4 和 4 进行比较那就是 4 double d; // 8 -----> 8 和 4 进行比较那么还是 4
};
#pragma pack()int main()
{printf("%d\n", sizeof(struct S)); // 12return 0;
}
结构体参数传递方式
注:结构体传递参数的方式一共有两种,一个是地址传递,一个是.操作符传递,函数传递参数的时候,需要压栈,会有时间和空间上的系统开销如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大会导致程序性能的下降,涉及结构体参数传递的时候尽量使用地址传递的方式。
结构体传递参数方式 1
#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include <string.h>
#include "add.h"
#include <stdint.h>struct S
{int data[100];int num;
};static void Print(struct S ss)
{uint8_t i;for (i = 0; i < 3; i++) {printf("%d ", ss.data[i]);}printf("%d ", ss.num);
}int main()
{struct S s = { {1,6,8},100 };Print(s);return 0;
}
结构体传递参数方式 2
#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include <string.h>
#include "add.h"
#include <stdint.h>struct S
{int data[100];int num;
};static void Print(struct S ss)
{uint8_t i;for (i = 0; i < 3; i++) {printf("%d ", ss.data[i]);}printf("%d ", ss.num);
}static void Print_p(struct S *ps)
{uint8_t i;for (i = 0; i < 3; i++){printf("%d ", ps->data[i]);}printf("%d ", ps->num);
}
int main()
{struct S s = { {1,6,8},100 };Print(s);printf("\n");printf("|-------------+----------+------+------|\n");Print_p(&s);return 0;
}
......
相关文章:

C 语言结构体
本博客涉及的结构体知识有: 1.0:结构体的创建和使用 2.0: typedef 关键字与#define 关键字的区别 3.0: 结构体成员的访问【地址访问与成员访问】 4.0: 结构体嵌套调用 5.0 数组访问赋值结构体成员 ...... 1.0:结构体的创建和使用 结…...

MySQl高级篇-主从复制
主从复制 复制的基本原理 slave会从master读取binlog来进行数据同步 MySQL复制过程分成三步: master将改变记录到二进制日志(binary log)。这些记录过程叫做二进制日志事件,binary log events;slave将master的binary log events拷贝到它的中继日志(r…...

JMeter案例分享:通过数据验证的错误,说说CSV数据文件设置中的线程共享模式
前言 用过JMeter参数化的小伙伴,想必对CSV Data Set Config非常熟悉。大家平时更关注变量名称,是否忽略首行等参数,其余的一般都使用默认值。然而我最近遇到一个未按照我的预想读取数据的案例,原因就出在最后一个参数“线程共享模…...

数学建模·Topsis优劣解距离法
Topsis优劣解 一种新的评价方法,特点就是利用原有数据,客观性强。 相较于模糊评价和层次评价 更加客观,充分利用原有数据,精确反映方案差距 基本原理 离最优解最近,离最劣解越远 具体步骤 正向化 代码与原理与熵权…...

数学建模中常用的数据处理方法
常用的数据处理方法 本文参考 B站西电数模协会的讲解视频 ,只作笔记提纲,想要详细学习具体内容请观看 up 的学习视频。国赛的 C 题一般数据量比较大。 这里介绍以下两种方法: 数据预处理方法 数据分析方法 数据预处理方法 1. 数据清洗 为…...

C嘎嘎:函数模版和类模版
目录 泛型编程 函数模版 函数模版概念 函数模版的格式 函数模版的原理 函数模版的实例化 函数参数的匹配原则 类模版 类模版的定义格式 类模版的实例化 泛型编程 如何实现一个通用的交换函数呢 void Swap(int& left, int& right) {int temp left;left rig…...

使用 Apache Pulsar 构建弹性可扩展的事件驱动应用
本视频来自 2024 Apache Pulsar 欧洲峰会,由 David Kjerrumgaard, 《Pulsar in Action》书作者给大家带来的《使用 Apache Pulsar 构建弹性可扩展的事件驱动应用》分享。 嘉宾|David Kjerrumgaard,Apache Pulsar Committer,《Pul…...

【国产开源可视化引擎Meta2d.js】视频
视频 meta2d 支持Html音视频。 // 音频 const pen {name: video,x: 100,y: 100,width: 100,height: 10,audio: https://down.ear0.com:3321/preview?soundid37418&typemp3,autoPlay: true, }; meta2d.addPen(pen); meta2d.inactive();// 视频 const pen {name: video,x…...

零信任网络安全
随着数字化转型的发生,网络边界也在不断被重新定义,因此,组织必须使用新的安全方法重新定义其防御策略。 零信任是一种基于“永不信任,永远验证”原则的安全方法,它强调无论在公司内部或外部,任何用户、设…...

Python酷库之旅-第三方库Pandas(022)
目录 一、用法精讲 55、pandas.lreshape函数 55-1、语法 55-2、参数 55-3、功能 55-4、返回值 55-5、说明 55-6、用法 55-6-1、数据准备 55-6-2、代码示例 55-6-3、结果输出 56、pandas.wide_to_long函数 56-1、语法 56-2、参数 56-3、功能 56-4、返回值 56-5…...

数据建设实践之大数据平台(一)准备环境
大数据组件版本信息 zookeeper-3.5.7hadoop-3.3.5mysql-5.7.28apache-hive-3.1.3spark-3.3.1dataxapache-dolphinscheduler-3.1.9大数据技术架构 大数据组件部署规划 node101node102node103node104node105datax datax datax ZK ZK ZK RM RM NM...

VUE2用elementUI实现父组件中校验子组件中的表单
需求是VUE2框架用elementUI写复杂表单组件,比如,3个相同功能的表单共用一个提交按钮,把相同功能的表单写成一个子组件,另一个父组件包含子组件的重复调用和一个提交按钮,并且要求提交时校验必填项。 注意: …...

人工智能算法工程师(中级)课程9-PyTorch神经网络之全连接神经网络实战与代码详解
大家好,我是微学AI,今天给大家介绍一下人工智能算法工程师(中级)课程9-PyTorch神经网络之全连接神经网络实战与代码详解。本文将给大家展示全连接神经网络与代码详解,包括全连接模型的设计、数学原理介绍,并从手写数字识别到猫狗识…...

UDP网络通信(发送端+接收端)实例 —— Python
简介 在网络通信编程中,用的最多的就是UDP和TCP通信了,原理这里就不分析了,网上介绍也很多,这里简单列举一下各自的优缺点和使用场景 通信方式优点缺点适用场景UDP及时性好,快速视网络情况,存在丢包 与嵌入…...

从零开始实现大语言模型(五):缩放点积注意力机制
1. 前言 缩放点积注意力机制(scaled dot-product attention)是OpenAI的GPT系列大语言模型所使用的多头注意力机制(multi-head attention)的核心,其目标与前文所述简单自注意力机制完全相同,即输入向量序列 x 1 , x 2 , ⋯ , x n x_1, x_2, \cdots, x_n x...

PTA 7-15 希尔排序
本题目要求读入N个整数,采用希尔排序法进行排序,采用增量序列{5,3,1},输出完成增量5和增量3后的5子排序和3子排序结果。 输入格式: 输入不超过100的正整数N和N个整数(空格分隔)。 输出格式: …...

【密码学】分组密码的设计原则
分组密码设计的目标是在密钥控制下,从一个巨大的置换集合中高效地选取一个置换,用于加密给定的明文块。 一、混淆原则 混淆原则是密码学中一个至关重要的概念,由克劳德香农提出。混淆原则就是将密文、明文、密钥三者之间的统计关系和代数关系…...

深入解析【C++ list 容器】:高效数据管理的秘密武器
目录 1. list 的介绍及使用 1.1 list 的介绍 知识点: 小李的理解: 1.2 list 的使用 1.2.1 list 的构造 知识点: 小李的理解: 代码示例: 1.2.2 list 迭代器的使用 知识点: 小李的理解࿱…...

NFS服务器、autofs自动挂载综合实验
综合实验 现有主机 node01 和 node02,完成如下需求: 1、在 node01 主机上提供 DNS 和 WEB 服务 2、dns 服务提供本实验所有主机名解析 3、web服务提供 www.rhce.com 虚拟主机 4、该虚拟主机的documentroot目录在 /nfs/rhce 目录 5、该目录由 node02 主机…...

自动驾驶事故频发,安全痛点在哪里?
大数据产业创新服务媒体 ——聚焦数据 改变商业 近日,武汉城市留言板上出现了多条关于萝卜快跑的投诉,多名市民反映萝卜快跑出现无故停在马路中间、高架上占最左道低速行驶、转弯卡着不动等情况,导致早晚高峰时段出现拥堵。萝卜快跑是百度 A…...

SpringSecurity框架【认证】
目录 一. 快速入门 二. 认证 2.1 登陆校验流程 2.2 原理初探 2.3 解决问题 2.3.1 思路分析 2.3.2 准备工作 2.3.3 实现 2.3.3.1 数据库校验用户 2.3.3.2 密码加密存储 2.3.3.3 登录接口 2.3.3.4 认证过滤器 2.3.3.5 退出登录 Spring Security是Spring家族中的一个…...

python安全脚本开发简单思路
文章目录 为什么选择python作为安全脚本开发语言如何编写人生第一个安全脚本开发后续学习 为什么选择python作为安全脚本开发语言 易读性和易维护性:Python以其简洁的语法和清晰的代码结构著称,这使得它非常易于阅读和维护。在安全领域,代码…...

WPF学习(4) -- 数据模板
一、DataTemplate 在WPF(Windows Presentation Foundation)中,DataTemplate 用于定义数据的可视化呈现方式。它允许你自定义如何展示数据对象,从而实现更灵活和丰富的用户界面。DataTemplate 通常用于控件(如ListBox、…...

GuLi商城-商品服务-API-品牌管理-JSR303分组校验
注解:@Validated 实体类: package com.nanjing.gulimall.product.entity;import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.nanjing.common.valid.ListValue; import com.nanjing.common.valid.Updat…...

PyTorch DataLoader 学习
1. DataLoader的核心概念 DataLoader是PyTorch中一个重要的类,用于将数据集(dataset)和数据加载器(sampler)结合起来,以实现批量数据加载和处理。它可以高效地处理数据加载、多线程加载、批处理和数据增强…...

TCP传输控制协议二
TCP 是 TCP/IP 模型中的传输层一个最核心的协议,不仅如此,在整个 4 层模型中,它都是核心的协议,要不然模型怎么会叫做 TCP/IP 模型呢。 它向下使用网络层的 IP 协议,向上为 FTP、SMTP、POP3、SSH、Telnet、HTTP 等应用…...

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(五)-同时支持无人机和eMBB用户数据传输的用例
引言 本文是3GPP TR 22.829 V17.1.0技术报告,专注于无人机(UAV)在3GPP系统中的增强支持。文章提出了多个无人机应用场景,分析了相应的能力要求,并建议了新的服务级别要求和关键性能指标(KPIs)。…...

使用F1C200S从零制作掌机之debian文件系统完善NES
一、模拟器源码 源码:https://files.cnblogs.com/files/twzy/arm-NES-linux-master.zip 二、文件系统 文件系统:debian bullseye 使用builtroot2018构建的文件系统,使用InfoNES模拟器存在bug,搞不定,所以放弃&…...

Vue 3 与 TypeScript:最佳实践详解
大家好,我是CodeQi! 很多人问我为什么要用TypeScript? 因为 Vue3 喜欢它! 开个玩笑... 在我们开始探索 Vue 3 和 TypeScript 最佳实践之前,让我们先打个比方。 如果你曾经尝试过在没有 GPS 的情况下开车到一个陌生的地方,你可能会知道那种迷失方向的感觉。 而 Typ…...

PyMysql error : Packet Sequence Number Wrong - got 1 expected 0
文章目录 错误一错误原因解决方案 错误二原因解决方案 我自己知道的,这类问题有两类原因,两种解决方案。 错误一 错误原因 pymysql的主进程启动的connect无法给子进程中使用,所以读取大批量数据时最后容易出现了此类问题。 解决方案 换成…...