C语言之结构体篇(简)

news2024/11/17 7:30:49

结构体

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

结构体是由我们自己创造的一种类型,使得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个小时真不是盖的呀 ^ _ ^ 最后希望兄弟姐妹们小小支持一下呗,咱下次肝到爆。

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

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

相关文章

kafka权威指南(阅读摘录)

零复制 Kafka 使用零复制技术向客户端发送消息——也就是说&#xff0c;Kafka 直接把消息从文件&#xff08;或者更确切地说是 Linux 文件系统缓存&#xff09;里发送到网络通道&#xff0c;而不需要经过任何中间缓冲区。这是 Kafka 与其他大部分数据库系统不一样的地方&#…

归并排序——“数据结构与算法”

各位CSDN的uu们好呀&#xff0c;今天&#xff0c;小雅兰的内容仍然是数据结构与算法专栏的排序呀&#xff0c;下面&#xff0c;让我们进入归并排序的世界吧&#xff01;&#xff01;&#xff01; 归并排序 归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种…

[腾讯云 Cloud studio 实战训练营] 制作Scrapy Demo爬取起点网月票榜小说数据

首语 最近接触到了一个关于云开发的IDE&#xff0c;什么意思呢&#xff1f; 就是我们通常开发不是在电脑上吗&#xff0c;既要下载编译器&#xff0c;还要下载合适的编辑器&#xff0c;有的时候甚至还需要配置开发环境&#xff0c;有些繁琐。而这个云开发的IDE就是只需要一台…

“苏豪 x 莱佛士”再度携手,惊艳亮相上海发型师节!

2023年6月28日&#xff0c;以“为爱启航”为主题的第16届AHF亚洲发型师节在上海跨国采购中心盛大开幕。继上次在施华蔻专业2023春夏新季风发布会上&#xff0c;苏豪x莱佛士合作的大秀&#xff0c;赢得了现场观众阵阵掌声。这次“Kraemer苏豪x莱佛士”再度携手&#xff0c;惊艳亮…

PoseiSwap:基于 Nautilus Chain ,构建全新价值体系

在 DeFi Summer 后&#xff0c;以太坊自身的弊端不断凸显&#xff0c;而以 Layer2 的方式为其扩容成为了行业很长一段时间的叙事方向之一。虽然以太坊已经顺利的从 PoW 的 1.0 迈向了 PoS 的 2.0 时代&#xff0c;但以太坊创始人 Vitalik Buterin 表示&#xff0c; Layer2 未来…

MySQL常见问题处理(三)

MySQL 常见问题解决 夕阳留恋的不是黄昏&#xff0c;而是朝阳 上一章简单介绍了MySQL数据库安装(二), 如果没有看过, 请观看上一章 一. root 用户密码忘记&#xff0c;进行重置操作 复制内容来源链接: https://blog.csdn.net/weixin_48927364/article/details/123556927 一.…

【MFC]实现16进制文件浏览器-详细步骤+代码

学习MFC已经两天了&#xff0c;我们来写一个小项目&#xff1a;16进制文件浏览器&#xff0c;简单实现&#xff0c;因为我们MFC学的还不是很透彻&#xff0c;这里会给出详细的每一个步骤&#xff0c;并且详细解释每一个方法&#xff1a; 文章目录 提前了解步骤&#xff1a;基本…

Apache poi 对单元格进行合并

需求背景: 在导出excel时, 需要对内容相同的单元格进行纵向合并 期望达到的效果: poi 实现合并单元格的方法 sheet.addMergedRegion(new CellRangeAddress(开始行, 结束行, 开始列, 结束列)); 个人的实现思路: 1): 举个列子, 就拿截图贴出的 [公司类别] 这一列来进行说明 …

【QT学习】01:helloqt

helloqt OVERVIEW helloqt一、helloqt1.使用向导创建2.手动创建3.pro文件4.Qt应用程序框架 二、按钮创建main.cppmywidget.cpp 三、对象模型1.对象树引入2.存在的问题 一、helloqt 创建一个qt项目&#xff0c;可以使用creator的向导创建&#xff0c;也可自己手动创建&#xff…

企业如何搭建矩阵内容,才能真正实现目的?

当下&#xff0c;新媒体矩阵营销已成为众多企业的营销选择之一&#xff0c;各企业可以通过新媒体矩阵实现扩大品牌声量、维持用户关系、提高销售业绩等不同的目的。 而不同目的的矩阵&#xff0c;它的内容运营模式会稍有差别&#xff0c;评价体系也会大不相同。 企业在运营某类…

性能优化-react路由懒加载和组件懒加载

背景 随着项目越来越大&#xff0c;打包后的包体积也越来越大&#xff0c;严重影响了首屏加载速度&#xff0c;需要对路由和组件做懒加载处理 主要用到了react中的lazy和Suspense。 废话不多说&#xff0c;直接上干货 路由懒加载 核心代码 import React, { lazy, Suspens…

精准感知+高效预判——城市“听诊器”防水患于未然

近期&#xff0c;全国多地遭遇暴雨猛袭&#xff0c;引发城市积涝&#xff0c;更有台风“杜苏芮”登陆后引发沿海内陆极端降水&#xff0c;各地排水防涝工作面临严峻考验。 快速定位城市排水管网系统的管道淤堵点&#xff0c;对河道水位、雨量情况、污水厂进水流量以及泵站运行情…

【云原生K8s】二进制部署单master K8s+etcd集群

一、实验设计 mater节点master01192.168.190.10kube-apiserver kube-controller-manager kube-scheduler etcd node节点node01192.168.190.20kubelet kube-proxy docker (容…

HTTP(超文本传输协议)学习

关于HTTP补学 一、HTTP能干什么 通过下图能够直观的看出&#xff1a;“交换数据 ” 二、HTTP请求例子 一个 HTTP 方法&#xff0c;通常是由一个动词&#xff0c;像 GET、POST 等&#xff0c;或者一个名词&#xff0c;像 OPTIONS、HEAD 等&#xff0c;来定义客户端执行的动作。…

Centos7.x修改密码

Centos7.x修改密码 root修改自己的密码 云服务器 云服务器则直接在控制台修改(例如阿里云服务器直接在阿里云服务器控制台修改&#xff0c;不赘述)命令行方式: 命令行输入: passwd 再两次输入新密码 root修改普通用户的密码 命令行方式: passwd 用户名再输入新的密码 普…

深入理解TCP三次握手:连接可靠性与安全风险

目录 导言TCP简介和工作原理的回顾TCP三次握手的目的和步骤TCP三次握手过程中可能出现的问题和安全风险为什么TCP三次握手是必要的&#xff1f;是否可以增加或减少三次握手的次数&#xff1f;TCP四次挥手与三次握手的异同点 导言 在网络通信中&#xff0c;TCP&#xff08;Tra…

7个更好用的数据可视化工具,建议收藏

在数据可视化工具的帮助下&#xff0c;设计师可以将数据转化为图像进行表达&#xff0c;让数据更加直观&#xff0c;更容易理解。今天本文会与打击分享7个好用的数据可视化工具&#xff0c;一起来get更好用的数据表达方式吧&#xff01; 1、即时设计 即时设计是一个非常受欢迎…

【js】FormData方法介绍和使用:

文章目录 一、接口使用FormData提交数据&#xff1a;二、FormData概述&#xff1a;三、FormData案例&#xff1a; 一、接口使用FormData提交数据&#xff1a; 二、FormData概述&#xff1a; 三、FormData案例&#xff1a; let formData new FormData() for (const key in tha…

一篇聊聊JVM优化:堆

一、Java 堆概念 1、简介 对于Java应用程序来说&#xff0c;Java堆&#xff08;Java Heap&#xff09;是虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享 的一块内存区域&#xff0c;在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例&#xff0c;Java 世界…

成为“黑客“前,必须经过的“学习路线“!!!

成为"黑客"前&#xff0c;必须经过的"学习路线"&#xff01;&#xff01;&#xff01; 最近很多小伙伴在看了我的文章后&#xff0c;有一个共同的问题&#xff0c;"我们是小白&#xff0c;我们没有基础&#xff0c;我们如何通过自学&#xff0c;就可…