C语言:结构体

目录

结构体类型的声明

匿名结构体

全局结构体变量

嵌套结构体

访问结构体成员

结构的自引用

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

结构体内存对齐

结构体内存对齐规则

修改默认对齐数 #pragma pack(n) 

offsetof 求结构体成员相对于结构体开头的偏移量的宏。 

为什么存在内存对齐?

结构体传参

结构体实现位段(位段的填充&可移植性)

什么是位段

位段的内存分配规则

位段的跨平台问题

位段的应用


结构体类型的声明

匿名结构体

/* 匿名结构体类型——只能用一次,即在声明的时候顺带创建一个变量 */
struct
{
    char name[20];
    int age;
} s4;

全局结构体变量

struct Stu
{
    char name[20];
    int age;
} s1, s2; // s1 s2是全局变量

struct Stu
{
    char name[20];
    int age;
} s1 = {"zhanghai", 22}; // 声明并初始化全局变量s1

嵌套结构体

struct Score
{
    int score;
    char str[20];
};

struct Stu
{
    char name[20];
    int age;
    struct Score s;
};

int main()
{
    // 初始化局部变量s3
    struct Stu s3 = {"zhanghai", 22, {100, "q"}};
    printf("%s %d %d %s", s3.name, s3.age, s3.s.score, s3.s.str); // zhanghai 22 100 q
    return 0;
}

访问结构体成员

#include <stdio.h>

/* 本例论如何声明结构体,并使用三种方式访问结构体各成员 */

struct MyType
{
    /* 成员 */
    char name[20];
    int age;
    char sex[10];
    char tele[12];
};

/* 接受结构体指针地址,struct MyType 类型int,表示一种类型;  *p是因为要指针赋值给指针变量时需加* */
void myPrintf(struct MyType *p)
{
    /* *p 表示使用*将指针变量p解引用,得到 *p == obj(可参考09_pointer) */
    printf("%s %d %s %s\n", (*p).name, (*p).age, (*p).sex, (*p).tele); // "zhanghai", 20, "nan", "15556201597"

    /* p 表示指针变量,结构体指针变量 -> 成员名,也就是结构体指针变量若想访问成员,需要操作符 -> */
    printf("%s %d %s %s\n", p->name, p->age, p->sex, p->tele); // "zhanghai", 20, "nan", "15556201597"
}

int main()
{

    struct MyType obj = {"zhanghai", 20, "nan", "15556201597"};

    // 结构体对象.成员名
    printf("%s %d %s %s\n", obj.name, obj.age, obj.sex, obj.tele); // "zhanghai", 20, "nan", "15556201597"

    // 将结构体指针传入
    myPrintf(&obj);

    return 0;
}

结构的自引用

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

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

struct Point
{
     int x;
     int y;
} p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
    //初始化:定义变量的同时赋初值。
struct Point p3 = {x, y};
struct Stu        //类型声明
{
     char name[15];//名字
     int age;      //年龄
};
struct Stu s = {"zhangsan", 20};//初始化
struct Node
{
 int data;
 struct Point p;
 struct Node* next; 
} n1 = {10, {4,5}, NULL}; //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};//结构体嵌套初始化

结构体内存对齐

结构体内存对齐规则

1. 第一个成员在与结构体变量偏移量为0的地址处。

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

对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。(vscode没有编译器对齐数,对齐数就是成员自身大小)

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

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

 

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

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

struct S3
{
    double d;
    char c;
    int i;
};

printf("%d\n", sizeof(struct S1)); // 12
printf("%d\n", sizeof(struct S2)); // 8
printf("%d\n", sizeof(struct S3)); // 16

修改默认对齐数 #pragma pack(n) 

/* 修改编译器默认对齐数为4,请看结构体规则第2点 */
#pragma pack(4)
struct S
{
    int i;    // 0
    double d; // 4 8 ——> 4
};
#pragma pack()

#pragma pack(1)
struct Sd
{
    char c;
    int i;
    char c1;
};
#pragma pack()

printf("%d\n", sizeof(struct S));  // 12 (修改对齐数之前是16)
printf("%d\n", sizeof(struct Sd)); // 6

offsetof 求结构体成员相对于结构体开头的偏移量的宏。 

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

struct S3
{
    double d;
    char c;
    int i;
};


// S1
printf("%d\n", offsetof(struct S1, c1)); // 0
printf("%d\n", offsetof(struct S1, i));  // 4
printf("%d\n", offsetof(struct S1, c2)); // 8
// s3
printf("%d\n", offsetof(struct S3, d)); // 0
printf("%d\n", offsetof(struct S3, c)); // 8
printf("%d\n", offsetof(struct S3, i)); // 12

结构在对齐方式不合适的时候,我们可以自己更改默认对齐数。 

为什么存在内存对齐?

1. 平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些

2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐

的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

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

结构体传参

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

/* 结构体传参 */
struct SC
{
    int data[1000];
    int num;
};

// 结构体传参
void print1(struct SC s)
{
    printf("%d\n", s.num);
}
// 结构体地址传参
void print2(struct SC *ps)
{
    printf("%d\n", ps->num);
}
    

struct SC s = {{1, 2, 3, 4}, 1000};
print1(s);  // 传结构体
print2(&s); // 传地址

结构体实现位段(位段的填充&可移植性)

什么是位段

位段的声明和结构是类似的,有两个不同:
  1. 位段的成员必须是 int、unsigned int 或signed int 。
  1. 位段的成员名后边有一个冒号和一个数字。
struct A
{
    int _a : 2; 
    int _b : 5;
    int _c : 10; 
    int _d : 30; 
};

 A就是一个位段类型。

位段的内存分配规则

1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型

2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。

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

4. int 位段被当成有符号数还是无符号数是不确定的。

5. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。

6. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

7. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的

struct A
{
    /* 上来二话不说先开辟4个字节空间,也就是 32个bit位 */
    int _a : 2;  // _a占2个bit位
    int _b : 5;  // _b占5个bit位
    int _c : 10; // _c占10个bit位
    /* 到这里发现 只剩 15 个bit位,不够存放_d的30个bit,那么会再次开辟4个字节用来存放 */
    int _d : 30; // _d占30个bit位
};

printf("%d\n", sizeof(struct A)); // 8 未使用位段就是16个字节

位段的跨平台问题

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

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

位段的应用

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

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

相关文章

JavaScript 异步编程

异步的概念 异步&#xff08;Asynchronous, async&#xff09;是与同步&#xff08;Synchronous, sync&#xff09;相对的概念。 在我们学习的传统单线程编程中&#xff0c;程序的运行是同步的&#xff08;同步不意味着所有步骤同时运行&#xff0c;而是指步骤在一个控制流序…

【论文阅读】MAG:一种用于航天器遥测数据中有效异常检测的新方法

文章目录 摘要1 引言2 问题描述3 拟议框架4 所提出方法的细节A.数据预处理B.变量相关分析C.MAG模型D.异常分数 5 实验A.数据集和性能指标B.实验设置与平台C.结果和比较 6 结论 摘要 异常检测是保证航天器稳定性的关键。在航天器运行过程中&#xff0c;传感器和控制器产生大量周…

六大排序(插入排序、希尔排序、冒泡排序、选择排序、堆排序、快速排序)未完

文章目录 排序一、 排序的概念1.排序&#xff1a;2.稳定性&#xff1a;3.内部排序&#xff1a;4.外部排序&#xff1a; 二、插入排序1.直接插入排序2.希尔排序 三、选择排序1.直接选择排序方法一方法二直接插入排序和直接排序的区别 2.堆排序 四、交换排序1.冒泡排序2.快速排序…

论文阅读——DiffusionDet

在目标检测上使用扩散模型 前向过程&#xff1a;真实框-->随机框 后向过程&#xff1a;随机框-->真实框 前向过程&#xff1a; 一般一张图片真实框的数目不同&#xff0c;填补到同一的N个框&#xff0c;填补方法可以是重复真实框&#xff0c;填补和图片大小一样的框&a…

Hive语法,函数--学习笔记

1&#xff0c;排序处理 1.1cluster by排序 &#xff0c;在Hive中使用order by排序时是全表扫描&#xff0c;且仅使用一个Reduce完成。 在海量数据待排序查询处理时&#xff0c;可以采用【先分桶再排序】的策略提升效率。此时&#xff0c; 就可以使用cluster by语法。 cluster…

从能用到好用,国产CPU不是你想象中的样子了?

最近看到了挺多关于国产CPU的评测视频&#xff0c;主要测试了鲲鹏、飞腾、海光、龙芯这四家。作为信创从业者&#xff0c;也想结合日常工作中接触到的国产CPU使用体验&#xff0c;发表些自己的看法。 我看到的评测&#xff0c;主要是采用SPEC CPU2006进行横向对比。SPEC CPU20…

VMware Workstation 与 Device/Credential Guard 不兼容 解决办法

问题描述 问题描述&#xff1a; VMware 启动虚拟机会报错。无法运行。 错误信息&#xff1a;VMware Workstation 与 Device/Credential Guard 不兼容。在禁用 Device/Credential Guard 原因分析&#xff1a; 通常原因是 Window 系统开启了 内置的Hyper-V 虚拟机。 解决方案&…

在 Linux 上搭建 Java Web 项目环境(最简单的进行搭建)

要在 Linux 上安装的程序有 1.JDK (要想运行 java 程序 JDK 是必不可少的) 2.Tomcat &#xff08;HTTP 服务器&#xff0c;是管理 Web 项目的常用工具&#xff09; 3. mysql &#xff08;数据库&#xff09; 一.安装 JDK 博主使用的 Linux 发行版是 centos &#xff0c;cen…

【MySQL--->用户管理】

文章目录 [TOC](文章目录) 一、用户管理表二、基本操作三、用户权限分配给用户某个数据库中某个表的某个权限. grant 权限 on 库.表名 to 用户名主机名. ![在这里插入图片描述](https://img-blog.csdnimg.cn/fe8eb171ef9343c3a09bd64d4f0db5c1.png)分配给用户某个数据库中全部表…

node实战——koa实现文件上传

文章目录 ⭐前言⭐koa实现文件上传⭐foxapi测试⭐总结⭐结束⭐前言 大家好,我是yma16,本文分享关于node实战——node实战——koa实现文件上传。 本文适用对象:前端初学者转node方向,在校大学生,即将毕业的同学,计算机爱好者。 node系列往期文章 node_windows环境变量配置…

【项目管理】中途接手的项目应对实用指南

导读&#xff1a;作为项目经理中途接手项目往往不可避免&#xff0c;为了保证项目成功需要项目经理额外考虑更多的因素和处理相关问题&#xff0c;也往往带来很大的挑战性。本文提供可应对借鉴的思路&#xff0c;在一定程度上可以作为最佳实践。 目录 1、首先、了解项目项目背…

基于SSM的北海旅游网站设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

支付宝沙箱支付

支付宝沙箱支付 支付宝沙箱&#xff08;Alipay Sandbox&#xff09;是支付宝提供的一个模拟环境&#xff0c;用于开发者在不影响真实交易的情况下进行支付宝相关功能的测试和调试。在软件开发中&#xff0c;沙箱环境通常指的是一个隔离的测试环境&#xff0c;可以模拟真实环境…

【算法挨揍日记】day23——740. 删除并获得点数、LCR 091. 粉刷房子

740. 删除并获得点数 740. 删除并获得点数 题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;你可以对它进行一些操作。 每次操作中&#xff0c;选择任意一个 nums[i] &#xff0c;删除它并获得 nums[i] 的点数。之后&#xff0c;你必须删除 所有 等于 nums[i] - 1…

Web3 分布式存储 IPFS(Web3项目一实战之四)

IPFS是一种分布式文件存储协议,它允许世界各地的计算机存储和服务文件作为一个巨大的对等网络的一部分来存储和服务文件。 世界上任何地方的任何计算机都可以下载IPFS软件并开始托管和提供文件。 如果有人在自己的计算机上运行IPFS,并将文件上传到IPFS网络,那么世界上其他任…

一文了解Word2vec 阐述训练流程

一文了解Word2vec 阐述训练流程 个性嵌入&#xff08;Personality Embeddings&#xff09; 词嵌入&#xff08;Word Embeddings&#xff09; 嵌入向量效果分析 语言模型 模型介绍 模型训练 Word2vec训练方法 CBOW方法 Skip-gram方法 CBOW方法与Skip-gram方法总结 重构…

【算法】树形DP③ 监控二叉树 ⭐(二叉树染色二叉树灯饰)!

文章目录 前期知识 & 相关链接例题968. 监控二叉树解法1——标记状态贪心解法2——动态规划 相关练习题目P2458 [SDOI2006] 保安站岗⭐&#xff08;有多个儿子节点&#xff09;&#x1f6b9;LCP 34. 二叉树染色⭐&#xff08;每个节点 单独dp[k 1]数组&#xff09;LCP 64.…

STM32 EC11 旋转编码器

**先给大家看看我选用的EC11元器件**代码在最后&#xff0c;复制可直接食用 以及我的电路图 在研究EC11的时序之前首先要了解一点&#xff0c;EC11按旋转的输出动作可以分为两种。一种是转两格&#xff0c;A、B对C端输出一个完整脉冲&#xff08;转一格就只是由低电平->高电…

DevToys:开发者的多功能瑞士军刀,让编程更高效!

DevToys&#xff1a;开发者的多功能瑞士军刀&#xff0c;让编程更高效&#xff01; DevToys 是一款专为开发者设计的实用工具&#xff0c;它能够帮助用户完成日常的开发任务&#xff0c;如格式化 JSON、比较文本和测试正则表达式&#xff08;RegExp&#xff09;。它的优势在于…

【每日一题】三个无重叠子数组的最大和

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;滑动窗口 写在最后 Tag 【滑动窗口】【数组】【2023-11-19】 题目来源 689. 三个无重叠子数组的最大和 题目解读 解题思路 方法一&#xff1a;滑动窗口 单个子数组的最大和 我们先来考虑一个长度为 k 的子数组的最…