当前位置: 首页 > news >正文

C语言自定义类型详解(1)结构体知识汇总

本篇概要

本篇主要讲述C语言结构体的相关知识,包括结构体的基本声明,结构体的匿名结构,结构体的自引用,结构体变量的定义和初始化以及结构体的内存对齐等相关知识。

文章目录

  • 本篇概要
  • 1.结构体
    • 1.1结构体的基本声明
    • 1.2结构体的特殊声明(匿名结构体类型)
    • 1.3结构体的自引用
    • 1.4结构体变量的定义和初始化
    • 1.5结构体内存对齐
    • 1.6结构体传参


1.结构体

1.1结构体的基本声明

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

结构体的基本用法如下:

struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
}; //分号不能丢

当然,我在这里还有一些补充

struct student
{
}s1,s2,s3;//这s1,s2,s3是三个结构体变量int main()
{
struct student s4,s5,s6;//s4,s5,s6是三个结构体变量
return 0;
}

需要说明的是s1,s2,s3是全局变量,s4,s5,s6是局部变量。

1.2结构体的特殊声明(匿名结构体类型)

struct 
{char neme[20];int age;char sex[5];//一个汉字2个字符float score;
}s1,s2;

↑如上代码所示,可以去掉结构体的名字 匿名结构体类型,但只能用一次,后面再想定义变量不可以(只可以使用s1和s2)

struct 
{char neme[20];int age;char sex[5];//一个汉字2个字符float score;
}b;struct 
{char neme[20];int age;char sex[5];//一个汉字2个字符float score;
}*p;int main()
{
p=&b;
return 0;
}

↑这里,两个结构体类型完全一样,但是p=&b这样是有问题的,虽然结构体类型,成员完全一样,但在编译器看来,这依然是两种结构体类型,编译器认为p和&b是不一样的。

1.3结构体的自引用

即在结构中包含一个类型为该结构本身的成员

自引用错误用法:

struct Node
{
int data;
struct Node n;
};int main()
{
printf("%d",sizeof(struct Node));
return 0;
}

提示:这么使用编译器会报警,运行不成功。因为struct中有一个整形为4字节,后面有一个结构体,那么是4+n,那么n中又是4+n,算不了。

自引用的正确用法:

struct Node
{
int data;
struct Node* n;
};
int main()
{
printf("%d",sizeof(struct Node));
return 0;
}

此时,代码可以运行,用指针的话,指针存放下一个节点的地址,指针本身的大小也是固定的,所以可以计算出来。

1.4结构体变量的定义和初始化

下面列举几种结构体的定义:

struct Point
{int x;int y;
}p1 = {1,2};struct Point p3 = {4,5};int main()
{int a = 10;int b = 20;struct Point p2 = {a, b};return 0;
}

结构体可以这么定义

struct Stu
{char name[15];//名字int age;
};
struct Stu s = { "zhangsan", 20 };
struct Stu s2 = { .age=18, .name="如花"};int main()
{
printf("%s %d\n", s.name, s.age);
printf("%s %d\n", s2.name, s2.age);
}

也可以这么定义

struct Node
{int data;struct Point p;struct Node* next;
};
int main()
{
struct Node n = { 100, {20, 21}, NULL };
printf("%d x=%d y=%d\n", n.data, n.p.x, n.p.y);
}

更加复杂,也还可以这么定义,嵌套结构体。

1.5结构体内存对齐

我们先来看以下的代码:

struct S1
{char c1;int i;char c2;
};struct S2
{char c1;char c2;int i;
};int main()
{printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));return 0;
}

在这里插入图片描述
提示:我们可以看到两个一摸一样的结构体,计算出来的大小,却不一样,这是什么导致的呢?

就是结构体的内存对齐导致的。

下来给大家介绍一个宏 offsetof,其头文件为<stddef.h>
它可以计算结构体成员相较于起始位置的偏移量

我们在上面代码的main函数加上以下代码:

	int main()
{printf("%d\n", offsetof(struct S1, c1));printf("%d\n", offsetof(struct S1, c2));printf("%d\n", offsetof(struct S1, i));//printf("%d\n", sizeof(struct S1));//printf("%d\n", sizeof(struct S2));return 0;
}

在这里插入图片描述
在这里插入图片描述

我们可以看到offsetof计算出来的结果为0,4,8,上面的示意图对应的就是struct内存的结构,灰色为char c1,蓝色为int i,橙色为char c2,红色的取余浪费了。

使用同样的操作,我们可以得出struct的结构示意图。
在这里插入图片描述

c1,c2,i都已在图中标注出来,红色为浪费部分。

提示:但是为什么要这个样子呢?为什么要内存对齐?

首先得掌握结构体的对齐规则:

  1. 第一个成员在与结构体变量偏移量为0的地址处存储。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
    VS中默认的值为8
  3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的成员中最大对齐数的整数倍处,结构体的整 体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

例如struct s2
char c1偏移量为0 ,直接占第一个字节
char c2 偏移量为min{1,8}
int i 偏移量为min{4,8}
此结构体最大对齐数位4,根据规则三结构体总大小取4的整数倍,即8.

再例如struct s1
char c1偏移量位0,直接占第一个字节
int i偏移量位min{4,8},从第四个字节开始占4个字节
char c2,偏移量为1的倍数,直接放在i后面
这是一共占了9个字节,最大对齐数为4,结构体大小应为4的倍数,故为12.

接下来就来说一说为什么要对齐!

  1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常。
  2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

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

对于第二点如下:
在这里插入图片描述

提示:在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到: 让占用空间小的成员尽量集中在一起。
例如struct s2就比struct s1所占空间小!!

上面讲到VS的默认对齐数为8,我们也可以更改它
pragma pack(5) //设置默认对齐数为5
pragma pack() //取消设置的默认对齐数,还原为默认

1.6结构体传参

代码使用了结构体传值调用和传址调用两种方法:

struct S
{int data[1000];int num;
};void print1(struct S t)
{printf("%d %d %d %d\n", t.data[0], t.data[1], t.data[2], t.num);
}void print2(const struct S * ps)
{printf("%d %d %d %d\n", ps->data[0], ps->data[1], ps->data[2], ps->num);
}int main() 
{struct S s = { {1,2,3}, 100 };print1(s);//传值调用print2(&s);//传址调用return 0;
}

函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销。
如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的 下降。所以我们优先使用传址调用。

相关文章:

C语言自定义类型详解(1)结构体知识汇总

本篇概要 本篇主要讲述C语言结构体的相关知识&#xff0c;包括结构体的基本声明&#xff0c;结构体的匿名结构&#xff0c;结构体的自引用&#xff0c;结构体变量的定义和初始化以及结构体的内存对齐等相关知识。 文章目录 本篇概要1.结构体1.1结构体的基本声明1.2结构体的特殊…...

小程序中如何查看会员的访问记录

​在小程序中&#xff0c;我们可以通过如下方式来查看会员的访问记录。下面是具体的操作流程&#xff1a; 1. 找到指定的会员卡。在管理员后台->会员管理处&#xff0c;找到需要查看访客记录的会员卡。也支持对会员卡按卡号、手机号和等级进行搜索。 2. 查看会员卡详情。点…...

SpringCloud Alibaba - Sentinel

接上文SpringCloud Alibaba - Nacos 1.Sentinel 流量防卫兵 1.1 安装与部署 和Nacos一样&#xff0c;它是独立安装和部署的&#xff0c;下载地址https://github.com/alibaba/Sentinel/releases 下载后的jar放到目录 然后配置 启动并访问,用户名密码都是 sentinel 此时就…...

内存泄漏,内存溢出,抽象类和接口,netstat、ping、ifconfig的区别

持续学习是我们必备的技能之一&#xff0c;保持与时俱进&#xff0c;保持行业的敏感度&#xff0c;关注行业发展趋势&#xff0c;了解新技术&#xff0c;加强自己的认知&#xff0c;积极的应对变化 内存泄漏 memory leak 是指程序在申请内存后&#xff0c;无法释放已申请的内…...

TensorFlow安装 ,在原本的虚拟环境下配置Tensorflow.

1.TensorFlow安装 &#xff0c;在原本的虚拟环境下配置Tensorflowh和pytorch 2.我首先在anaconda的环境下创建了一个tensorflow文件夹 如何先进入D盘&#xff0c;再进入tensorflow文件夹的目录D:cd D:\Anaconda\TensorFlowSoftWarepip install tensorflow如图所示报错解决方法 …...

如何使用HTML, CSS和JavaScript开发一个浏览器打字游戏:从零到一的详细步骤与完整代码教程

第一部分:游戏概述与HTML结构 1. 游戏概述 打字游戏是一个训练用户打字速度和准确性的游戏。用户将会看到一个随机的单词或句子,并在限定时间内尽快准确地键入该单词或句子。每次正确输入,玩家得分,每次输入错误,扣分。这个游戏不仅能够增加用户的打字速度,还可以为学习…...

安卓玩机搞机----不用刷第三方官改固件即可享受“高级设置”的操作 ChiMi安装使用步骤

很多玩友特别喜欢第三方作者修改的带有高级设置的官改包。因为他可以随意修改系统里面的有关设置选项。包括但不限于修改状态栏 显示日期 秒等等的操作。 第三方带高级设置的官改 一般官改带高级设置的类似与 今天给大家分享下不用刷这些官改包即可享受高级设置的操作。 红米…...

代码随想录|392.判断子序列,115.不同的子序列(需要二刷)

392.判断子序列 先用双指针做 class Solution {public boolean isSubsequence(String s, String t) {//双指针int ms.length();int nt.length();int slow0;int i0;int j0;while(i<m&&j<n){if(s.charAt(i)t.charAt(j)){i;System.out.println(i);}j;}return im?…...

Linux——文件系统

✅<1>主页&#xff1a;&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;Linux——文件系统 ☂️<3>开发环境&#xff1a;Centos7 &#x1f4ac;<4>前言&#xff1a;上期我们了解了文件在内存中得组织方式&#xff0c;那么文件在磁盘中…...

《动手学深度学习 Pytorch版》 7.3 网络中的网络(NiN)

LeNet、AlexNet和VGG的设计模式都是先用卷积层与汇聚层提取特征&#xff0c;然后用全连接层对特征进行处理。 AlexNet和VGG对LeNet的改进主要在于扩大和加深这两个模块。网络中的网络&#xff08;NiN&#xff09;则是在每个像素的通道上分别使用多层感知机。 import torch fr…...

古代有没有电子元器件?

手机&#xff0c;电脑&#xff0c;电视等等电子产品&#xff0c;无时无刻充斥在我们的生活中&#xff0c;如果有一天突然没有了这些功能多样的电子产品&#xff0c;估计大部分人都会一时之间难以适应。 这就好比正在上网&#xff0c;结果突然被人断了网&#xff0c;导致无网络连…...

log4j2或者logback配置模版实现灵活输出服务名

介绍 在我们使用log4j2或者logback打印日志时&#xff0c;输出的内容中通常是一定要加上服务名的。以log4j2为例&#xff1a; <!--输出控制台的配置--> <Console name"Console" target"SYSTEM_OUT"><!-- 输出日志的格式 --><Patter…...

使用HTTP爬虫ip中的常见误区与解决方法

在如今的互联网时代&#xff0c;为了保障个人隐私和实现匿名浏览&#xff0c;许多人选择使用HTTP爬虫ip。然而&#xff0c;由于缺乏了解和使用经验&#xff0c;常常会出现一些误区。本文将为大家介绍使用HTTP爬虫ip过程中常见的误区&#xff0c;并提供相应的解决方法&#xff0…...

MySQL学习笔记3

MySQL的源码编译安装&#xff1a; 1、参考MySQL的源码安装官方文档&#xff1a; 2、源码安装定制选项&#xff1a; 3、源码安装三部曲&#xff1a;配置、编译、安装。 4、软件安装包&#xff1a; mysql-boost-5.7.43.tar.gz 5、安装需求&#xff1a; 安装需求具体配置安装目…...

快速掌握ES6

什么是ES6 ES6&#xff08;ECMAScript 6&#xff09;&#xff0c;也被称为ES2015&#xff0c;是JavaScript的第六个版本&#xff0c;于2015年发布。ES6引入了许多新的语法和功能&#xff0c;旨在提高JavaScript的开发效率和代码质量。 ES6的一些主要特性和改进包括&#xff1…...

电池厂提供excel电池曲线zcv到mtk电池曲线zcv转换

#encoding:utf8 #电池厂提供excel电池曲线zcv到mtk电池曲线zcv转换 import pandas as pd import openpyxl import math # 读取Excel文件 df pd.read_excel("a55-zcv.xlsx") for j in range(0,10): if(j<3): offset0 #T0~T2 if(j3): offset…...

重写和重载、抽象类和接口

文章目录 前言一、重载与重写1.重载&#xff08;Overload&#xff09;&#xff08;1&#xff09;条件&#xff08;2&#xff09;举例 2.重写&#xff08;Override)&#xff08;1&#xff09;规则&#xff08;2&#xff09;举例 3.重载和重写区别 二、抽象类与接口1.抽象类&…...

Untiy UDP局域网 异步发送图片

同步画面有问题&#xff0c;传图片吧 using System.Text; using System.Net.Sockets; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using System.Net; using System; using System.Threading.Tasks; using Sy…...

移动端H5封装一个 ScrollList 横向滚动列表组件,实现向左滑动

效果&#xff1a; 1.封装组件&#xff1a; <template><div class"scroll-list"><divclass"scroll-list-content":style"{ background, color, fontSize: size }"ref"scrollListContent"><div class"scroll…...

Docker一键安装和基本配置

一键安装脚本 注&#xff1a;该脚本需要root权限 curl -sSL https://get.docker.com/ | sh非root组用户赋权 sudo groupadd docker # 若使用一键安装脚本会自动创建这个组&#xff0c;提示已存在 sudo gpasswd -a ${USER} docker # 将当前用户添加到docker组&#xff0c;也…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用&#xff0c;操作系统&#xff1a;Ubuntu24.04&#xff0c;Neofj版本&#xff1a;2025.04.0。 Apt安装 Neofj可以进行官网安装&#xff1a;Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

FastAPI 教程:从入门到实践

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;支持 Python 3.6。它基于标准 Python 类型提示&#xff0c;易于学习且功能强大。以下是一个完整的 FastAPI 入门教程&#xff0c;涵盖从环境搭建到创建并运行一个简单的…...

el-switch文字内置

el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

C# 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成

一个面向 Java 开发者的 Sring-Ai 示例工程项目&#xff0c;该项目是一个 Spring AI 快速入门的样例工程项目&#xff0c;旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计&#xff0c;每个模块都专注于特定的功能领域&#xff0c;便于学习和…...