C语言学习之结构体

     在C语言中,我们已经学了好几种类型的数据。比如整型int、char、short等,浮点型double、float等。但是这些都是基本数据类型,而这些数据类型应用在实际编程里显然是不够用的。比如我们没有办法用一旦数据类型来定义一个”人“的属性。因此这里我们需要一个框,将不同的数据类型放在一起,这就是结构体,接下来我们将深入学习结构体的知识。

结构体

结构体的创建与初始化

        结构体是一些值的集合,这些值被称之为成员变量。结构的每个成员都是不同类型的变量。

        语法结构:

struct name//名字自定义
{member-list;//一个或者多个成员变量
}variable-list//可以省略。是结构体变量列表

举例说明

struct person
{char name[20];int age;float height;
};
int main()
{struct person p1 = {"诗华",18,1.72};return 0;
}

结构体的特殊声明

        声明结构体的时候可以不完全声明。

        但是匿名结构体的声明其后必须创建变量

struct//匿名结构体类型
{char name[20];int age;float height;
}s1,s2;

        匿名结构体类型只能用一次。

        但是切记这种方法不行

拓展:数据结构简单科普

        链表是一种数据结构。数据结构是描述数据在内存当中的存储结构,对数据的操作就是对数据结构的操作。

        大体上数据结构分为以下几种

结构的自引用

        在结构体中包含一个类型为该结构本身的成员是否可以呢?

上图报错的原因:结构体内不能包含同类型的自己。 

如果想正确表达,需要这样

struct Node
{int data;struct Node *next;
};int main()
{printf("%zd\n",sizeof(struct Node));/*struct person p1 = {"诗华",18,1.72};*/return 0;
}

结果为: 

而在上图中,int data为数据域,struct Node * next为指针域。

        也可以简化为:

typedef struct Node
{int data;struct Node *next;
}Node;

之后如果使用结构体定义变量可以直接写为

Node n;

但是这么些是绝对错误的

typedef struct 
{int data;Node *next;
}Node;

结构体内存对齐

        我们学会结构体的基本使用后,那么问题来了,结构体的大小是怎么计算的呢?我们举例分析。

#include<stdio.h>
struct s1
{char c1;//1char c2;//1int n;//4
};
struct s2
{char c1;//1int n;//4char c2;//1
};
struct s3
{double d1;//8int n;//1char c1;//4
};
struct s4
{double d1;//8struct s3 S;//16char c2;//1
};
int main()
{printf("%zd\n",sizeof(struct s1));printf("%zd\n", sizeof(struct s2));printf("%zd\n",sizeof(struct s3));printf("%zd\n",sizeof(struct s4));return 0;
}

结果为:

        

        为什么是这样呢?

        结构体成员在存储的时候有内存对齐的现象。

        这里介绍一个新概念:offsetof——一个宏,可以计算出一个结构体成员相较于结构体起始位置的偏移量。(其实之前博主的博客预处理那节讲解过:C语言学习之预处理指令-CSDN博客)

        内存对齐的规则

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

2.其他成员变量要对齐到某个数字(对齐数)的整数被的地址处

对齐数=编译器默认的一个对齐数与该成员变量大小的较小值。

VS中默认的值为8

Linux中gcc编译器没有默认对齐数,对齐数就是成员变量自身的大小

3.结构体总大小为最大对齐数(结构体中每个成员变量都有一个对齐数,所有对齐数中最大值)的整数倍

4.如果嵌套了结构体的情况下,嵌套的结构体成员对齐到自己成员中最大对其数的整数倍,结构体的整体大小就是所有最大对其数(含嵌套结构体中成员的对齐数)的整数倍。

        

 

为什么会存在内存对齐?

1.不是所有的硬件平台都能访问任意地址的任意数据;某些硬件平台只能在某些地址处去某些特定类型的数据。

2.数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问仅仅需要一次访问。

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

那在设计结构体的时候,我们既要满足对齐,又要节省空间如何做到呢“

让占用空间小的成员尽量集中在一起。

struct A
{char c1;char c2;int n;
};
struct B
{char c1;int n;char c2;
};
int main()
{printf("%zd\n",sizeof(struct A));printf("%zd\n",sizeof(struct B));return 0;
}

修改默认对齐数

        #pragma这个预处理指令可以改变编译器默认对齐数量。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#pragma pack(1)//设置结构体默认对齐数为1
struct S 
{char c1;int n;char c2;
};
#pragma	pack()//恢复默认对齐数int main()
{printf("%d\n",sizeof(struct S));return 0;
}

结构体传参

         

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
struct S
{int data[1000];int num;
};
struct S s = { {1,2,3,4,5} ,1000 };
//结构体传参
void print1(struct S s)
{printf("%d\n",s.num);
}
void print2(struct S *ps)
{printf("%p\n",ps->num);
}
int main()
{print1(s );print2(&s);return 0;
}

        上面的print1和print 2哪个更好呢?答案是print2

原因:

函数传参的时候,参数是需要压栈的,会有时间和空间上系统的开销。

如果传递一个结构体对象时,结构体体积过大,参数压栈的系统开销过大,会导致性能下降。

结论:结构体传参的时候,要穿结构体的地址

结构体实现位段  

        位段

        位段的声明和结构必须要类似,有两个不同。

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

2.位段的成员名之后有一个冒号和一个数字

#include<stdio.h>
//结构体
struct A
{int a;int b;int c;int d;
};
//位段
struct B
{int _a:2;int _b : 3;int _c : 10;int _d : 5;
};
int main()
{printf("%zd\n", sizeof(struct A));printf("%zd\n", sizeof(struct B));return 0;
}

结构体和位段的大小分别为: 

        

位段的空间是如何开辟的呢?

位段的内存分配

1.位段的成员可以是:int,unsigned int或者signed int或者char类型

2.位段的空间上按照需要以4个字节和1个字节的方式开辟

3.位段涉及很多不确定因素,位段是不跨平台的,注重可移植程序的程序避免使用。

//位段
struct B
{int _a:2;int _b : 3;int _c : 10;int _d : 30;
};
int main()
{/*printf("%zd\n", sizeof(struct A));*/printf("%zd\n", sizeof(struct B));return 0;
}

1.int位段被当成有符号还是无符号不确定

2.位段最大位的数目不确定(16位机器最大16,32位机器最大32.写成27的话16 位机器会出问题)

3.位段成员在内存中从左向右分配还是右向左分配是不确定的

4.当一个结构体包含两个位段的时候,第二个位段成员比较大,无法荣达第一个剩余的位时是舍弃剩余位置还是保留不确定

总结:与结构相比,位段可以达到同样的效果,更加节省空间不过会有跨平台的问题。

位段的应用

在网络协议的IP数据报中,用位段可以更节省空间的达到想要的效果,对网络畅通很有帮助。

 位段使用的注意事项

        位段的几个成员共同用一个字节,这样有i协成员起始位置并不是某个字节起始位置,那么这些位置只是没有地址的。内存中每个字节分配一个地址,一个字节内部的比特位是没有地址的。

        所以不能对位段的成员使用&*操作符,这样就不能直接使用scanf直接给位段的成员输入值。只能先输入放在一个变量里然后赋值给位段的成员

struct B
{int _a:2;int _b : 5;int _c : 10;int _d : 30;
};
int main()
{struct B s = {0};scanf("%d",&sa.b);//这是错误的//正确示范int b = 0;scanf("%d",b);sa._b = b;return 0;
}

感谢看到这里的读者朋友,求一个赞谢谢。

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

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

相关文章

架构-计算机系统基础

计算机系统基础 一、计算机系统组成 &#xff08;一&#xff09;计算机系统层次结构 硬件组成 主机&#xff1a;包含CPU&#xff08;运算器控制器&#xff09;、主存储器&#xff08;内存&#xff09;。外设&#xff1a;输入设备、输出设备、辅助存储器&#xff08;外存&…

【计算机网络性能优化】从基础理论到实战调优

目录 前言技术背景与价值当前技术痛点解决方案概述目标读者说明 一、技术原理剖析核心概念图解核心作用讲解关键技术模块说明技术选型对比 二、实战演示环境配置要求核心代码实现案例1&#xff1a;iPerf3带宽测试案例2&#xff1a;TCP窗口优化案例3&#xff1a;QoS流量整形 运行…

Python 自动化办公:Excel 数据处理的“秘密武器”

引言 在日常的 IT 办公场景里&#xff0c;Excel 是数据处理与分析的 “常胜将军”。无论是财务人员整理账目、销售团队统计业绩&#xff0c;还是运营人员分析用户数据&#xff0c;Excel 都发挥着关键作用。但面对海量数据&#xff0c;手动操作 Excel 不仅效率低下&#xff0c;还…

缓存集群技术深度解析:从原理到实战

缓存集群技术深度解析&#xff1a;从原理到实战 一、缓存集群核心定位与架构选型 1. 集群模式核心价值 缓存集群通过数据分片、高可用保障、水平扩展解决单节点瓶颈&#xff0c;核心能力包括&#xff1a; 数据分片&#xff1a;将数据分散到多个节点&#xff0c;突破单节点内…

CSDN编辑文章时如何自动生成目录

如何自动生成目录 前置条件1. 插入目录标识符2. 编写标题层级 前置条件 需要使用markdown编辑&#xff0c;并且只有按照markdown语法编写不同的标题级别&#xff0c;才能使用这个方法自动生成对应的目录。 1. 插入目录标识符 在文章的顶部添加以下代码&#xff1a; [TOC](文…

产品经理对于电商接口的梳理||电商接口文档梳理与接入

接口梳理7个注意点总结 ①注意要测试环境和生产环境。生产上线时候要提醒研发换到生产环境调用。 ②注意必输字段和选输字段&#xff0c;要传入字段的含义和校验。枚举值不清楚含义的要询问对方含义&#xff0c;比如说单据类型字段枚举值是B2C发货单&#xff0c;BBC发货单&am…

更快的图像局部修改与可控生成:Flex.2-preview

Flex.2-preview 文本生成图像扩散模型介绍 一、模型简介 Flex.2-preview 是一种 开源的 80 亿参数文本生成图像扩散模型&#xff0c;具备通用控制和修复支持功能&#xff0c;是 Flex.1alpha 的下一代版本。该模型由社区开发并为社区服务&#xff0c;采用 Apache 2.0 许可证&a…

【Castle-X机器人】一、模块安装与调试:机器人底盘

持续更新。。。。。。。。。。。。。。。 【ROS机器人】模块安装 一、Castle-X机器人底盘1.1 结构概述1.2 驱动执行结构1.3 环境传感器1.4 电气系统1.5 Castle-x机器人底盘测试激光雷达传感器测试及数据可视化超声波传感器实时数据获取防跌落传感器测试陀螺仪测试键盘控制测试…

条件、列表渲染.

#### v-for 1. 渲染列表 vue <template> <ul v-for"(item,index) in list" > <li>{{ item }}</li> </ul> </template> <script setup> import { ref } from vue; let list ref([苹果, 香蕉, 橙子]) </script>…

node20的安装和vue的入门准备

一、node20的安装 直接下载路径&#xff1a;https://nodejs.org/download/release/v20.11.0/node-v20.11.0-x64.msi 安装&#xff0c;双击msi文件 点击同意协议 更改下载路径 什么也不用选&#xff0c;点击next进行下一步 什么也不用选&#xff0c;点击next进行下一步 点击安…

从 Java 到 Kotlin:在现有项目中迁移的最佳实践!

全文目录&#xff1a; 开篇语 1. 为什么选择 Kotlin&#xff1f;1.1 Kotlin 与 Java 的兼容性1.2 Kotlin 的优势1.3 Kotlin 的挑战 2. Kotlin 迁移最佳实践2.1 渐进式迁移2.1.1 步骤一&#xff1a;将 Kotlin 集成到现有的构建工具中2.1.2 步骤二&#xff1a;逐步迁移2.1.3 步骤…

威雅利电子|业界领先的高隔离度用于5G基站的吸收式SPDT开关“NT1819“

业界领先的高隔离度 用于5G基站的吸收式SPDT开关"NT1819" 为了实现智能社会&#xff0c;已经启动了5G服务。这样&#xff0c;高速、低延迟、大容量的数据通信成为可能&#xff0c;也给我们的生活和工业发展带来了巨大的变化。 在5G基站有很多天线&#xff0c;每个天…

Diamond软件的使用--(6)访问FPGA的专用SPI接口

1.什么是FPGA的专用SPI接口&#xff1f; 此处的SPI FLASH接口即为FPGA的专用SPI接口&#xff0c;上电时&#xff0c;FPGA从该FLASH读出程序并运行。 2.访问SPI PROM需要注意哪些地方&#xff1f; 1&#xff09;处于MASTER SPI MODE 2&#xff09;调用USRMCLK原语&#xff0c;…

Go 语言中的 `select` 语句详解

select 是 Go 语言中处理通道(Channel)操作的一个强大控制结构&#xff0c;它允许 goroutine 同时等待多个通道操作。下面我将全面详细地解释 select 语句的各个方面。 基本语法 select 语句的基本语法如下&#xff1a; select { case <-ch1:// 如果从 ch1 成功接收数据&…

AI如何重塑CC防护行业?五大变革与实战策略解析

一、CC防护行业的技术痛点与AI的破局方向 CC攻击&#xff08;Challenge Collapsar&#xff09;作为一种以高频合法请求消耗服务器资源的DDoS攻击手段&#xff0c;传统防护技术面临三大核心挑战&#xff1a; 规则依赖性强&#xff1a;基于IP封禁或请求频率的静态规则易被绕过&a…

GPT-4o最新图像生成完全指南:10大应用场景与提示词模板

引言 OpenAI于近期推出的全新GPT-4o图像生成功能&#xff0c;代表了AI图像创作领域的重大突破。作为一个原生多模态系统&#xff0c;GPT-4o将文本理解和图像生成无缝整合&#xff0c;为创作者、教育工作者和专业人士提供了前所未有的视觉创作灵活性。本文将分享10个GPT-4o图像…

Linux驱动开发2 - 内核定时器驱动

背景 所有驱动开发都是基于全志T507&#xff08;Android 10&#xff09;进行开发&#xff0c;用于记录驱动开发过程。 简介 定时器是比较常用的一个功能&#xff0c;用来执行周期性任务。一般不太精确的定时可以用系统提供的延时函数进行。如果需要进行较为精确的延时&#…

Dify 使用 excel 或者 csv 文件创建知识库

Dify 使用 excel 或者 csv 文件创建知识库 1. 创建知识库2. 创建聊天助手3. 其他 1. 创建知识库 创建知识库&#xff0c;导入excel/csv文件&#xff0c; 文件内容&#xff0c; 单击 “预览块”&#xff0c;可以确认会生成多个键值对的块&#xff0c; 配置 Embedding 模型和检索…

23种设计模式-行为型模式之迭代器模式(Java版本)

Java 迭代器模式&#xff08;Iterator Pattern&#xff09;详解 &#x1f9e0; 什么是迭代器模式&#xff1f; 迭代器模式是一种行为型设计模式&#xff0c;它提供一种方法顺序访问一个聚合对象中的各个元素&#xff0c;而不暴露该对象的内部表示。 &#x1f3af; 使用场景 …

使用nodeJs的express+axios+cors做代理

使用nodeJs的expressaxioscors做代理 前端在请求后端时通常会遇到跨域cors问题&#xff0c;如果只在本地开发可以通过webpack或vite的proxy设置。但如果需要在线上或者其他地方绕过跨域&#xff0c;可以使用代理的方法。 1. 创建文件夹 并创建以下文件 package.json {"…