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

C语言之结构体详解

C语言之结构体详解

文章目录

  • C语言之结构体详解
    • 1. 结构体类型的声明
    • 2. 结构体变量的创建和初始化
    • 3. 结构体的特殊声明
    • 4. 结构体的自引用
      • 结构体的自引用
      • 匿名结构体的自引用
    • 5. 结构体内存对齐
      • 5.1 练习一
      • 5.2 练习三
    • 6. 为什么存在内存对⻬?

1. 结构体类型的声明

struct tag
{member-list;
}variable-list;

结构体标签:tag
结构体类型:struct tag
成员列表:member-list
结构体变量列表:variable-list

例如:

struct Stu
{char name[20];int age;float score;
};//分号不能丢

2. 结构体变量的创建和初始化

#include <stdio.h>struct Stu
{char name[20];int age;float score;
}s1;int main()
{//按照顺序初始化struct Stu s2 = { "zhangsan",18,90.1f };printf("%s %d %.2f\n", s2.name, s2.age, s2.score);//按照指定顺序初始化struct Stu s3 = { .score = 82.4f,.name = "zhangsan",.age = 20 };printf("%s %d %.2f\n", s3.name, s3.age, s3.score);return 0;
}

在结构体创建的时候,在变量列表创建的变量是全局变量,s1也就是全局变量

struct Stu 为结构体类型,和int创建变量一样(int n = 0;),struct Stu s2创建结构体变量
结构体变量在赋值的时候需要加上大括号,再根据成员列表的顺序,输入对应的值

结构体变量初始化的时候,也可以不按顺序初始化,这时候就需要用到 ( . )结构体访问操作符

3. 结构体的特殊声明

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

#include <stdio.h>struct
{char a;int b;
}x;struct
{char x;int y;
}*p;int main()
{p = &x;return 0;
}

上述两个结构体声明省去了结构体标签(tag)
在对第一个结构体变量取地址的时候,就会报警告
在这里插入图片描述

编译器会将上面声明的两个结构体当成两个不同类型的类型,所以是非法的 匿名的结构体,如果没有对结构体重命名的话,基本上只能使用一次

重命名如下:

#include <stdio.h>typedef struct
{char x;int y;
}S;int main()
{S s1 = { 1,2 };return 0;
}

使用typedef关键字将匿名结构体重命名为S

4. 结构体的自引用

结构体的自引用

代码一:

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

上述代码结构体的自引用是错误的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的⼤⼩就会⽆穷的⼤,是不合理的

正确的自引用:

struct Node
{int data;//数据域 存放数据struct Node *next;//指针域 存放下一个数据的地址
};

由于指针的大小是固定的,在32位平台下,为4字节,在64为平台下,为8字节,这样就确保了结构体变量的大小

在内存中,有些数据不是连续存放的,要想找到下一个数据,可以使用指针的方式
在这里插入图片描述

匿名结构体的自引用

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

匿名结构体的自引用是不可行的,虽然使用typedef关键字重命名了匿名结构体,但是在重命名之前,Node是在重命名之后才产生的,但是在Node产生之前就已经使用Node创建了结构体指针变量,这是不行的

5. 结构体内存对齐

⾸先得掌握结构体的对⻬规则:

  1. 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
  2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
    对⻬数 = 编译器默认的⼀个对⻬数 与 该成员变量⼤⼩的较⼩值。
  • VS 中默认的值为 8
  • Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩
  1. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的
    整数倍。
  2. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构
    体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

5.1 练习一

#include <stdio.h>struct s1
{char c1;char c2;int i;
};struct s2
{char c1;int i;char c2;
};
int main()
{printf("struct s1 = %zd\n", sizeof(struct s1));printf("struct s2 = %zd\n", sizeof(struct s2));return 0;
}

代码运行结果如下:>
struct s1 = 8
struct s2 = 12

解释:
struct s1 = 8
在这里插入图片描述

  1. 结构体的第一个成员是 char c1; 会放在偏移量为0的位置,第一个字节,当对于自己偏移量就是0,所以只占一格
  2. 结构体的第二个成员是 char c2; char 为1个字节,在VS中默认的对齐数为8,取较小值 1,同时在偏移量中找到1的整数倍,也就是偏移量为1的位置,所以占一个字节
  3. 结构体的第三个成员是 int i; int 为4个字节,相对于默认值8,取较小值4,同时在偏移量中找到4的整数倍,跳过偏移量2 3 找到4,同时int 占四个字节,所以占4个字节
  4. 总共占了8个字节,在结构体中的对齐数有1 1 4,取最大值4,判断现在是否为最大对齐数4的整数倍
  5. 最终struct s1的大小为8个字节

struct s2 = 12
在这里插入图片描述

  1. 结构体的第一个成员是 char c1; 会放在偏移量为0的位置,第一个字节,当对于自己偏移量就是0,所以只占一格
  2. 结构体中的第二个成员是 int i; int 为4个字节,和默认对齐数8相比,取较小值4,在偏移量中找到4的整数倍,跳过1 2 3 ,找到偏移量为4的位置,int 为4个字节,所以占4格
  3. 结构体中的第三个成员是char c2; char 为1个字节,和默认对齐数8相比,取较小值1,在偏移量中找到1的整数倍,也就是偏移量为8的位置,char占一格
  4. 总共占了9个字节,相比结构体中的对齐数,1 1 4,取最大对齐数4,判断是否为整数倍,不是则取整数倍,也就是12字节,所以结构体的大小为12字节

5.2 练习三

#include <stdio.h>
struct S3
{double d;char c;int i;
};
struct S4
{char c1;struct S3 s3;double d;
};
int main()
{printf("struct s4 = %zd\n", sizeof(struct S4));return 0;
}

代码运行结果:>
struct s4 = 32

解释:
按照练习一的方法得出struct S3 的大小为16
在这里插入图片描述
struct S4
在这里插入图片描述

结构体嵌套时,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,上述代码中在S4中嵌套了一个S3,计算S3的偏移量使用的是S3中最大的对齐数,也就是8字节

  1. char会放在偏移量为0的位置,也就是一个字节
  2. 嵌套的结构体最大对齐数为8,和默认数一致,取偏移量为8的整数倍,也就是在8的位置,S3结构体的大小为16个字节,所以占16格
  3. double占8个字节,和默认对齐数一致,取偏移量为8的整数倍,也就是在24的位置,占8个字节
  4. 总共占了32个字节,S4中的最大对齐数为8字节,是8的整数倍
  5. 所以struct S4的大小为32个字节

6. 为什么存在内存对⻬?

  1. 平台原因 (移植原因):
    不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
  2. 性能原因:
    数据结构(尤其是栈)应该尽可能地在⾃然边界上对⻬。原因在于,为了访问未对⻬的内存,处理器需要作两次内存访问;⽽对⻬的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对⻬成8的倍数,那么就可以⽤⼀个内存操作来读或者写值了。否则,我们可能需要执⾏两次内存访问,因为对象可能被分放在两个8字节内存块中。

平台原因:在C语言没有明确规定int类型的数据是无符号还是有符号的,这取决于编译器,不同的编译器会有不同的解释

性能原因:结构体的内存对⻬是拿空间来换取时间的做法

TIPS:在设计结构体的时候,我们既要满⾜对⻬,⼜要节省空间,可以将占用空间小的成员尽量聚集在一块

例如:

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

相比较于s2,s1的占用空间较小一点

相关文章:

C语言之结构体详解

C语言之结构体详解 文章目录 C语言之结构体详解1. 结构体类型的声明2. 结构体变量的创建和初始化3. 结构体的特殊声明4. 结构体的自引用结构体的自引用匿名结构体的自引用 5. 结构体内存对齐5.1 练习一5.2 练习三 6. 为什么存在内存对⻬? 1. 结构体类型的声明 struct tag {me…...

学习canvas

Canvas是一个基于像素的渲染引擎&#xff0c;它使用JavaScript API在画布上绘制图像。以下是它的一些优点和缺点&#xff1a; 优点&#xff1a; Canvas的渲染速度快&#xff0c;适合处理大量图像和高度动态的图像。 可以直接操作像素&#xff0c;从而能够创建出高质量、流畅的…...

浏览器的渲染原理

以下内容来源于渡一前端大师课&#xff0c;仅作个人学习记录。 渲染的第一步是 解析HTML 解析过程中遇到CSS解析CSS&#xff0c;遇到JS执行JS。为了提高解析效率&#xff0c;浏览器在开始解析之前&#xff0c;会启动一个预解析的线程&#xff0c;率先下载HTML中的外部CSS文件和…...

从 JSON 转 Java 实体的多种方法详解

将 JSON 数据转换为 Java 对象是现代应用程序开发中常见的任务。在 Java 中&#xff0c;有多种方法可以实现这一目标。本文将详细介绍几种常见的方法&#xff0c;以及它们的优缺点。 1. 手动映射&#xff08;Manual Mapping&#xff09; 手动映射是最基础的方法之一&#xff…...

数据库的多表查询(MYSQL)表表联立

根据以上三张表格&#xff0c;对三张表格进行不同的联立&#xff0c;查询并显示符合条件的内容。 1. 查出至少有一个员工的部门。显示部门编号、部门名称、部门位置、部门人数。 mysql> SELECT d.deptno AS 部门编号, d.dname as 部门名称, d.loc as 部门位置, COUNT(e.emp…...

P8650 [蓝桥杯 2017 省 A] 正则问题(dfs )

多重括号&#xff0c;利用回溯来对上一层括号中的内容进行反馈 实现&#xff1a; 若为 x 长度加一 若为 &#xff08; 进入递归计算 (计算相当于子表达式) 若为 &#xff09; 结束当前递归 若为 | …...

【ESP32】手势识别实现笔记:红外温度阵列 | 双三次插值 | 神经网络 | TensorFlow | ESP-DL

目录 一、开发环境搭建与新建工程模板1.1、开发环境搭建与卸载1.2、新建工程目录1.3、自定义组件 二、驱动移植与应用开发2.1、I2C驱动移植与AMG8833应用开发2.2、SPI驱动移植与LCD应用开发2.3、绘制温度云图2.4、启用PSRAM&#xff08;可选&#xff09;2.5、画面动静和距离检测…...

No matching version found for @babel/compat-data@^7.23.5 处理

npm ERR! notarget No matching version found for babel/compat-data^7.23.5 处理 报错信息 npm WARN ERESOLVE overriding peer dependency npm ERR! code ETARGET npm ERR! notarget No matching version found for babel/compat-data^7.23.5. npm ERR! notarget In most …...

手持机|三防智能手机_4寸/5寸/6寸安卓系统三防手机PDA手持终端方案

随着科技的不断发展&#xff0c;三防手持机作为一种多功能设备&#xff0c;正逐渐在各行业得到广泛应用。这款手持机采用高性能处理器&#xff0c;支持高精度北斗定位和工业本安防爆功能&#xff0c;并具备IP67级防水防尘性能和1.5米防跌落能力。因此&#xff0c;它在仓储管理、…...

蓝桥杯算法心得——仙界诅咒(dfs)

大家好&#xff0c;我是晴天学长&#xff0c;搜索型的dfs&#xff0c;差点开二维矩阵了&#xff0c;仔细一想&#xff0c;没那么夸张啊&#xff0c;哈哈哈&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1…...

List集合,遍历,数据结构

一.List常见的方法&#xff1a; 二. List集合的遍历方式 除了 迭代器遍历 增强for遍历 Lambda表达式遍历&#xff0c;还有自己独有的普通for遍历&#xff0c;列表迭代器遍历 1.迭代器遍历 2.增强for遍历 3.Lambda表达式遍历 4.普通for遍历 5.列表迭代器遍历 列表迭代器相对于…...

2的幂运算

2的幂 描述 : 给你一个整数 n&#xff0c;请你判断该整数是否是 2 的幂次方。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 如果存在一个整数 x 使得 n 2x &#xff0c;则认为 n 是 2 的幂次方。 题目 : LeetCode 231.2的幂 : 231. 2 的幂 分…...

优先队列经典例题leetcode思路代码详解

目录 leetcode215题.数组中的第k个最大元素 leetcode347题.前k个高频元素 leetcode295题.数据流的中位数 对优先队列感兴趣的朋友可以去看我上一篇文章。 优先队列基础讲解-CSDN博客 leetcode215题.数组中的第k个最大元素 215. 数组中的第K个最大元素 - 力扣&#xff08;…...

新型Python环境与依赖管理工具——pipenv

文章目录 pipenv介绍pipenv安装pipenv使用创建虚拟环境删除虚拟环境安装依赖查看包之间的依赖图卸载依赖在虚拟环境中执行命令shell环境下通过requirements.txt安装依赖导出requirements.txt文件查看虚拟环境的路径 pipenv介绍 pipenv可以看做是pip和virtualenv的组合体&#…...

FastDFS+Nginx - 本地搭建文件服务器同时实现在外远程访问「内网穿透」

文章目录 前言1. 本地搭建FastDFS文件系统1.1 环境安装1.2 安装libfastcommon1.3 安装FastDFS1.4 配置Tracker1.5 配置Storage1.6 测试上传下载1.7 与Nginx整合1.8 安装Nginx1.9 配置Nginx 2. 局域网测试访问FastDFS3. 安装cpolar内网穿透4. 配置公网访问地址5. 固定公网地址5.…...

kendo-splitter动态分配分隔框大小

通过size方法&#xff0c;动态改变框大小&#xff0c;参考链接&#xff1a;https://docs.telerik.com/kendo-ui/api/javascript/ui/splitter/methods/size vue画面 <kendo-button type"primary" click"changePane">button</kendo-button><…...

网站提示不安全?

随着互联网的普及和发展&#xff0c;网络安全问题日益严重。黑客攻击、数据泄露、恶意软件等问题层出不穷&#xff0c;给企业和个人带来了巨大的损失。在这个背景下&#xff0c;确保网站安全显得尤为重要&#xff0c;而使用SSL证书是解决这些问题的有效措施。 什么是SSL证书&am…...

C# 泛型编译特性对性能的影响

C#作为一种强类型语言&#xff0c;具有丰富的泛型支持&#xff0c;允许开发者编写可以应对不同数据类型的通用代码。然而&#xff0c;在泛型编译时&#xff0c;针对结构和类作为泛型参数时&#xff0c;会对性能产生不同的影响。 泛型编译行为 在C#中&#xff0c;泛型编译行为取…...

11-30 JavaWeb

修改与删除操作 防止空指针异常 localhost:8080 -> 分页查询 修改流程&#xff1a;(先查后改(两个servlet)) 修改&#xff1a; 传用户id(用户id怎么得到 -> 循环一次得到一个user 对象 user对象里用user.getId()得到用户id) UpdateUserQueryServlet.java &#xff08;…...

LCR 047. 二叉树剪枝 和 leetCode 1110. 删点成林 + 递归 + 图解

给定一个二叉树 根节点 root &#xff0c;树的每个节点的值要么是 0&#xff0c;要么是 1。请剪除该二叉树中所有节点的值为 0 的子树。节点 node 的子树为 node 本身&#xff0c;以及所有 node 的后代。 示例 1: 输入: [1,null,0,0,1] 输出: [1,null,0,null,1] 解释: 只有红…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

4. TypeScript 类型推断与类型组合

一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式&#xff0c;自动确定它们的类型。 这一特性减少了显式类型注解的需要&#xff0c;在保持类型安全的同时简化了代码。通过分析上下文和初始值&#xff0c;TypeSc…...

如何在Windows本机安装Python并确保与Python.NET兼容

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...