C语言之结构体篇(简)

结构体

  • 结构体的认知
  • 结构体的声明
    • 一般声明
    • 特殊声明
      • 匿名结构体类型
  • 结构体自引用
  • 结构体变量的定义与初始化
    • 结构体变量的定义
    • 结构体变量的初始化
  • 结构体传参
  • 结构体内存对齐
  • 位段
    • 位段声明
    • 位段的内存分配
    • 位段跨平台问题:

结构体是由我们自己创造的一种类型,使得C语言有能力描述复杂类型。比如学生包含了:名字、年龄、性别、学号…

结构体的认知

结构是一些值的集合,这些值叫做成员变量。每个成员可以是不同类型的变量。
————-----------

结构体与数组的区分:
数组:是一组相同类型的元素的集合。
结构体:是一些值的集合,但这些值可以是不同类型:可以是标量、数组、指针、甚至是其他结构体。

结构体的声明

一般声明

假如这里要描述一个学生:

//描述一个学生
struct Stu   //Stu是结构体变量名,可以自定义(根据实际需求)
{
	//里面是成员列表
	char name[20];//学生名字
	int age;      //学生年龄
	char id[10];  //学生学号
	//....

}s1,s2,s3;  //这里是结构体变量,是全局变量,
           //(创建结构体类型时顺带创建3个结构体全局变量)
          //这后面的分号不能丢
int main()
{
	struct Stu s4, s5; //创建结构体变量,这些是局部变量

	return 0;
}

特殊声明

就是再结构体声明的时候,可以不完全的声明。

匿名结构体类型

struct       //没有结构体变量名
{
	int a;
	char b;
	double c;
}s1;      //只能在这里创建结构体变量名,
         //且只能使用一次

结构体自引用

定义:结构体要能够找到和他同类型的下一个结构体;

  • 结构体中可以包含另一个结构体变量
struct book1
{
	char name[20];
	int age;
	int mony;
};

struct book
{
	char name[20];
	int mony;
	struct book1 s1;
};

但是结构体内不能有自身结构体变量,但是可以用指针指向一个本类型结构体来实现结构体自引用

struct Note
{
	int date;        //数据域
	struct Note* next;//指针域
};

关于typedef(定义类型)定义定义匿名结构体类型 :

typedef struct
{
	int data;//数据域
	struct Node* next;//指针域
} Node;
void main() {
	Node n;
 }

结构体变量的定义与初始化

结构体变量的定义

// struct book 是定义的数据类型的名字,它向编译系统声明这是一个“结构体类型”
struct book  
{
	int mony;
	char name[20];
}s1,s2;//全局变量(创建结构体类型时顺带创建2个结构体全局变量)
       //最后的分号千万不能省略
int main()
{
	struct book s3, s4;//局部变量
	return 0;
}

结构体变量的初始化

struct book
{
int mony;
char name[20];
}s1={30,"xiyouji"}, s2 = {20,"hongloumeng"};//s1,s2也是结构体变量,其是全局变量,
//并对其全部初始化,其实也可不初始化


int main()
{
struct book s3 = { 15,"三国演义" }, s4 = {25,"水浒传"};
//创建结构体对象并完成初始化
//其中初始化一个汉字占两个字节
return 0;
}

结构体嵌套初始化:


struct history
{
int age;
int id;
};


struct book
{
char name[20];
int mony;
struct history s1;
};


int main()
{
struct book s1 = { "abcdef",20,{1000,2023}};//初始化完成
return 0;
}

匿名结构体类型初始化


struct {
char name[20];
int price;
char id[12];
}s = { "git",7,"123" };

注意:

  • 结构体的声明和初始化也可以在函数里面进行
  • 结构体定义完就必须对其进行初始化
  • 结构体也可以通过操作符"."的形式来进行里面值的操作

结构体传参

struct BOOK
{
	char name[20];
	int money;
};

//传值调用
void test1(struct BOOK s)
{
	printf("%s %d", s.name, s.money);
}

//传址调用
void test2(struct BOOK* ps)
{
	printf("%s %d", ps->name, ps->money);
}

int main()
{
	struct BOOK s1 = { "shuihuzhuan",20 };

	test1(s1);//传结构体,不会改变结构体变量
	test2(&s1);//传结构体地址,可以改变结构体变量

	return 0;
}

注意:
结构体传参尽可能传地址,因为传参数需要压栈,如果传递一个结构体对象时,结构体过大,参数压栈的系统开稍过大,所以会导致性能下降;
传址调用时,可以用上const;结构体传参中const的作用:使原结构体只可以进行读取操作不可以进行更改操作,以防止误操作。

结构体内存对齐

现在我们已经了解结构体的基本使用了,接下来我们探讨一个深入的问题:
如何计算结构体的大小?
这里就涉及到结构体内存对齐
要了解这些,首先给大家介绍一个宏( offsetof() ),其可以计算结构体成员相对于结构体起始位置的偏移量

size_t offsetof( structName, memberName );

1. 第一个参数是结构体名称
2.第二个参数是结构体成员
输入两个参数,就会返回结构体成员相对于结构体起始位置的偏移量。

以这个代码来探究:

struct s1
{
	char c1;
	int i;
	char c2;
};

struct s2
{
	int i;
	char c1;
	char c2;
};

int main()
{
	printf("%d\n", offsetof(s1, c1));
	printf("%d\n", offsetof(s1, i));
	printf("%d\n", offsetof(s1, c2));
	return 0;
}

在这里插入图片描述

当知道了偏移量,咱们就可以来分析结构体储存在内存中的方式;
结构体存储在栈区

在这里插入图片描述

这样的话应该是9个字节,但结果是12字节,这是为什么?

在这里插入图片描述

上面出现的问题,说明结构体成员不是按照顺序在内存中连续存放的,而是有一定规律的。

结构体内存对齐的规则:

1. 结构体的第一个成员永远放在相较于结构体变量起始位置偏移量为0的位置。

2.从第二个成员开始,往后的每个成员都要对齐到某个数(对齐数)的整数倍的地址处,
对齐数:编译器默认的一个对齐数与该成员大小值的较小值。
VS编译器上对齐数默认是:8
gcc:没有默认对齐数,对齐数就是该结构体成员的大小。

3.结构体的总大小必须是最大对齐数的整数倍。
最大对齐数:所有对齐数的最大值

4.如果嵌套了结构体,嵌套的结构体对齐到自己的最大对齐数的整数倍出,结构体的整数大小,就是所以最大对齐数(包含了嵌套结构体的对齐数)的整数倍。

那怎么运用这个规则呢?
看图说话:
在这里插入图片描述

特殊一点 : 如果成员变量是一个数组。


举个例子:int arr [3];则把其看成三个整形变量依次存放就好。

为什么要内存对齐?

1.平台原因:

  • 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2.性能原因

  • 数据结构应该尽可能的在自然边界上对齐。原因在于:为了访问未对齐的内存,处理器需要两次内存访问;而对齐的内存访问仅只需要一次访问。

3.如图解释:
在这里插入图片描述
总结:结构体内存对齐是拿空间换时间的做法

所以在设计结构体时我们既要满足对齐,又要节省空间:

1.合理安排好结构体成员空间


2.也可以修改默认对齐数

修改默认对齐数:

#include <stdio.h>
//修改默认对齐数为1
#pragma pack(1)
struct Hand
{
	char c;
	int size;
	char q;
};
#pragma pack()//取消设置的默认对齐,还原默认
int main() {
	printf("%d\n", sizeof(struct Hand));//默认对齐数8时——12,默认对齐数1时——6
	return 0;
}

位段

相比结构体更能节省空间,但是有跨平台问题;

为什么存在位段:因为如果一个字节里有32bit,当你只用到了2个bit,其他的空间就都浪费了,而位段就可以选择你的每一个成员占多大的空间,所以可以更好的节约空间。

位段声明

其声明和结构体声明是类似的,有两点不同:

1.位段成员可以是 int 、unsigned int 、signed int 或者是 char (整形家族)类型。
__
2.位段的成员名后边有一个冒号和一个数字。
__

举例:

** S 就是一个位段类型**

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};

位段的内存分配

就拿上述的位段类型,来探究;

1. 位段的成员可以是 int 、unsigned int、signed int 或者是 char (整形家族)类型
2. 位段的空间上是按照需要以4个字节(int)或1个字节(char )的方式来开辟的
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
4.首先每一个成员后面的数字都是位,是二进制位

struct S
{
	//因为先是char类型,先开辟一个字节--8个bit位
	char a : 3;//a成员占3个bit
	char b : 4;//b成员占4个bit
	char c : 5;//这时用了7个bit,还剩1个,因为下面还是char类型,不够就再开辟一个字节
	//c成员在新开辟的字节占5个bit
	char d : 4;//这时还剩3个bit,char类型,开辟一个字节,d成员占4个bit
	//因此S位段类型所占3个字节
};

int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 20;
	s.c = 3;
	s.d = 4;

	printf("%d ", sizeof S);//3
	return 0;
}

在VS编译器下一个字节内部的数据,先使用低比特位的地址,再使用高比特位的地址(在内存中分配从右往左使用)
在这里插入图片描述

位段跨平台问题:

1. int 位段类型,被当做有符号位还是无符号位,是不确定的;
2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的

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

连肝6个小时真不是盖的呀 ^ _ ^ 最后希望兄弟姐妹们小小支持一下呗,咱下次肝到爆。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

js-匈牙利算法

匈牙利算法 素数伴侣新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个注脚注释也是必不可少的K…

H5打包封装小程序系统开发

H5打包封装小程序系统开发 H5打包封装小程序系统开发是指将H5页面打包封装成小程序的开发过程。下面是一个简单的步骤&#xff1a; 准备工作&#xff1a;首先&#xff0c;需要准备好H5页面的代码和资源文件。确保H5页面在浏览器中正常运行&#xff0c;并且没有依赖于浏览器特…

『赠书活动 | 第十六期』《深入浅出Java虚拟机:JVM原理与实战》

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; 『赠书活动 &#xff5c; 第十六期』 本期书籍&#xff1a;《深入浅出Java虚拟机&#xff1a;JVM原理与实战》 赠书规则&#xff1a;评论区&#xff1a;点赞&#xff…

将Visio和Excel导出成没有白边的PDF文件

1、VISIO如何无白边导出pdf格式 在使用Latex时&#xff0c;要导入矢量图eps格式。但是VISIO无法输出eps格式&#xff0c;这就需要将其导出为pdf。但是导出pdf时&#xff0c;往往会有大量的白边。VISIO无白边导出pdf格式的方法如下&#xff1a; 1.文件——开发工具——显示sha…

并发 如何创建线程 多线程

进程&#xff1a;一个程序的执行过程 线程&#xff1a;一个方法就是一个线程 并发&#xff1a;多个线程抢夺一个资源 操作同一个对象 创建线程方法1 //创建线程方法1 继承Thread类 重写润方法 调用start开启线程 public class TestThead extends Thread{Overridepublic voi…

redis 高级篇 redis 源码的读取分析

一 redis源码分析 1.1 源码分析 1每一个kv键值对应有一个dictEntry。 2.底层数据结构

景联文科技高质量成品数据集上新啦!

景联文科技近期上新多个成品数据集&#xff0c;包含图像、视频等多种类型的数据&#xff0c;涵盖丰富的场景&#xff0c;可满足不同模型的多元化需求。 高质量成品数据集可用于训练和优化模型&#xff0c;使得模型能够更加全面和精准地理解和处理任务&#xff0c;更好地应对复…

eclipse was unable to locate its companion shared library

当转移或者Copy工程时&#xff0c; eclipse was unable to locate its companion shared library eclipse.ini 里面的路径配置错误导致 --launcher.library C:/Users/**/.p2/pool/plugins/org.eclipse.equinox. launcher.win32.win32.x86_64_1.2.700.v20221108-1024 -product …

17. Spring 事务

目录 1. 事务定义 2. MySQL 中的事务使用 3. 没有事务时的插入 4. Spring 编程式事务 5. Spring 声明式事务 5.1 Transactional 作用范围 5.2 Transactional 参数说明 5.3 Transactional 工作原理 1. 事务定义 将⼀组操作封装成一个执行单元&#xff08;封装到一起…

人工智能安全-2-非平衡数据处理

0 提纲 现象与原因非平衡数据处理方法概览数据预处理层面特征层算法层面1 现象与原因 非平衡数据分类问题:在网络信息安全问题中,诸如恶意软件检测、SQL注入、不良信息检测等许多问题都可以归结为机器学习分类问题。这类机器学习应用问题中,普遍存在非平衡数据的现象。 产…

java系列之list集合分组

文章目录 前言一、list是什么&#xff1f;二、list集合分组总结 前言 在Java编程中&#xff0c;List集合是一种常用的数据结构&#xff0c;用于存储一组元素。有时候&#xff0c;我们需要对List集合中的元素进行分组操作&#xff0c;即将相同属性或特征的元素归类到一组。这种…

向 Maven 中央仓库上传一个修改过的基于jeecg的autoPOI的 jar包记录

1、注册https://issues.sonatype.org/账号 下面就代表注册好了&#xff0c;同时提交的工单也通过了 2、这里主要是goupId 需要进行认证&#xff0c;需要到域名注册商近一个txt的解析&#xff0c;以便确保这个是你的 通过下面来验证你的域名信息&#xff0c;这里主要是上面的工…

前端文件上传实践与后端处理——文件分块上传

文件上传是现代Web应用程序中常见的功能之一。在这篇博客中&#xff0c;我们将探讨一个简单但完整的前端文件上传实践&#xff0c;同时提供一个后端示例&#xff0c;演示如何处理上传的文件。我们将使用JavaScript作为前端语言&#xff0c;并结合Node.js作为后端环境。让我们开…

Postgresql源码(109)并行框架实例与分析

1 PostgreSQL并行参数 系统参数 系统总worker限制&#xff1a;max_worker_processes 默认8 系统总并发限制&#xff1a;max_parallel_workers 默认8 单Query限制&#xff1a;max_parallel_workers_per_gather 默认2 表参数限制&#xff1a;parallel_workers alter table tbl …

针对高可靠性和高性能优化的1200V硅碳化物沟道MOSFET

目录 标题&#xff1a;1200V SiC Trench-MOSFET Optimized for High Reliability and High Performance摘要信息解释研究了什么文章创新点文章的研究方法文章的结论 标题&#xff1a;1200V SiC Trench-MOSFET Optimized for High Reliability and High Performance 摘要 本文详…

Flask学习笔记_异步论坛(四)

Flask学习笔记_异步论坛&#xff08;四&#xff09; 1.配置和数据库链接1.exts.py里面实例化sqlalchemy数据库2.config.py配置app和数据库信息3.app.py导入exts和config并初始化到app上 2.创建用户模型并映射到数据库1.models/auth.py创建用户模型2.app.py导入模型并用flask-mi…

ORB算法在opencv中实现方法

在OPenCV中实现ORB算法&#xff0c;使用的是&#xff1a; 1.实例化ORB orb cv.xfeatures2d.orb_create(nfeatures)参数&#xff1a; nfeatures: 特征点的最大数量 2.利用orb.detectAndCompute()检测关键点并计算 kp,des orb.detectAndCompute(gray,None)参数&#xff1a…

Windows驱动开发

开发Windows驱动程序时&#xff0c;debug比较困难&#xff0c;并且程序容易导致系统崩溃&#xff0c;这时可以使用Virtual Box进行程序调试&#xff0c;用WinDbg在主机上进行调试。 需要使用的工具&#xff1a; Virtual Box&#xff1a;用于安装虚拟机系统&#xff0c;用于运…

使用SSM框架实现个人博客管理平台以及实现Web自动化测试

文章目录 前言1. 项目概述2. 项目需求2.1功能需求2.2 其他需求2.3 系统功能模块图 3. 开发环境4. 项目结构5. 部分功能介绍5.1 数据库密码密文存储5.2 统一数据格式返回5.3 登录拦截器 6. 项目展示7. 项目测试7.1 测试用例7.2 执行部分自动化测试用例 前言 在几个月前实现了一…

Transformer 论文学习笔记

重新学习了一下&#xff0c;整理了一下笔记 论文&#xff1a;《Attention Is All You Need》 代码&#xff1a;http://nlp.seas.harvard.edu/annotated-transformer/ 地址&#xff1a;https://arxiv.org/abs/1706.03762v5 翻译&#xff1a;Transformer论文翻译 特点&#xff1…