【C语言】进阶——结构体+枚举+联合

 ①前言:

在之前【C语言】初阶——结构体 ,简单介绍了结构体。而C语言中结构体的内容还有更深层次的内容。

一.结构体

结构体(struct)是由一系列具有相同类型或不同类型的数据项构成的数据集合,这些数据项称为结构体的成员。

 1.结构体的声明

// 创建结构体
struct student
{char name[10];  // 学生名字int num;     // 学生学号int age;     // 学生年龄
}stu;

struct student 是类型,stu是结构体类型变量

2.结构体的定义和初始化 

2.1结构体的初始化 
struct Stu         //类型声明
{char name[15];    //名字int age;          //年龄
};
struct Stu s = {"zhangsan", 20};    //初始化
 2.2结构体的自引用
struct Node
{int data;struct Point p;struct Node* next; 
}n1 = {10, {4,5}, NULL};     //结构体嵌套初始化
struct Node n2 = {20, {5, 6}, NULL};    //结构体嵌套初始化

3.结构体内存对齐 

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

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

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

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

        VS中默认的值为8

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

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

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

 

s1在内存中所占的字节大小是多少?

VS默认对齐数是8

Linux没有默认对齐数自身大小就是其对齐数

首先c1是char类型 占1个字节与8比,8大,对齐数是1,放在偏移量为0的位置

c2也是char 类型对其数是1放在1的整数倍处也就是放在偏移量为1的位置

i是int型占4个字节,最大对其数是4,放在偏移量为4的位置

最后s1偏移量为7一共占用了8个字节,8是最大偏移量4的整数倍,所以s1在内存中占用了8个字节

 3.1为什么存在内存对齐?

    结构体的内存对齐是拿空间来换取时间的做法。
那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到?
    让占用空间小的成员尽量集中在一起。 

1. 平台原因(移植原因):

        不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特 定类型的数据,否则抛出硬件异常。

2. 性能原因:

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

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

3.2修改默认对齐数

	#pragma pack(8)//设置默认对齐数为8#pragma pack()//取消设置的默认对齐数,还原为默认
#pragma pack(8)//设置默认对齐数为8
struct S1
{char c1;int i;char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认

3.3offsetof宏

用法:计算偏移量

头文件:#include <stddef.h> 

offsetof (type,member)
struct address {char name[50];char street[50];int phone;
};int main()
{printf("address结构中的name 偏移 = %d 字节\n", offsetof(struct address, name));//0字节printf("address结构中的street 偏移 = %d 字节\n", offsetof(struct address, street)); //50字节  
}
 👊模拟实现
//模拟实现宏offsetof
#define my_offsetof(struct_name, member_name)  (int)&(((struct_name*)0)->member_name)
//例如shtruct S{char a; char b;};
//用宏替换:(int)&(((struct S*)0)->a)
//解释:把结构体的首地址看成0,那么每个成员的地址(减去首地址0)也就是相对于首地址的偏移量,所以取到成员的地址并转换为int型就是偏移量;//注意:以上把0强制转换成struct S*类型之后(把0转换成一个结构体指针类型),那么从0地址往后看就是一个结构体的形式看
//也可以说是在0地址的位置上放了一个结构体;

注:结构体传参主要以传地址为主。

二.位段 

  1. 位指的是二进制位;
  2. 位段的成员必须是 int、unsigned int signed int、char
  3. 位段的成员名后边有一个冒号和一个数字;
  4. 可以使数据单元节省储存空间,当程序需要成千上万个数据单元时,这种方法就显得尤为重要;
  5. 不支持跨平台
struct str
{int _a : 2;int _b : 3;		//后面的数字是bit位,不是字节
};

 

int main()
{unsigned char puc[4];struct tagPIM{unsigned char ucPim1;unsigned char ucData0 : 1;unsigned char ucData1 : 2;unsigned char ucData2 : 3;}*pstPimData;pstPimData = (struct tagPIM*)puc;memset(puc, 0, 4);pstPimData->ucPim1 = 2;pstPimData->ucData0 = 3;pstPimData->ucData1 = 4;pstPimData->ucData2 = 5;printf("%02x %02x %02x %02x\n", puc[0], puc[1], puc[2], puc[3]);return 0;
}

puc是一个char数组,每次跳转一个字节,结构体不是,它只有第一个元素单独享用一字节,其他三个元素一起共用一字节,所以puc被结构体填充后,本身只有两个字节会被写入,后两个字节肯定是0,

然后第一个字节是2就是2了,第二个字节比较麻烦,首先ucData0给了3其实是越界了,1位的数字只能是0或1,所以11截断后只有1,同理ucData1给的4也是越界的,100截断后是00,只有5的101是正常的。填充序列是类似小端的低地址在低位,所以排列顺序是00 101 00 1。也就是0010 1001,

所以结果为02 29 00 00

三.枚举

枚举是一个被命名的整型常数的集合,枚举在日常生活中很常见,例如表示星期的SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就是一个枚举。

枚举就是一个对象的所有可能取值的集合 

#include<stdio.h>
enum sex
{male,female,secret,
};

枚举的关键字是enum 在括号内部注意每个成员名后面要加逗号,同时别忘了括号外面的分号

枚举内部的成员被依次赋值为0,1,2

我们假如将female赋值为1,secret就被赋值为2,male赋值为0

3.1枚举的优点 

1.增加代码的可读性和可维护性

2. 和 #define 定义的标识符比较枚举有类型检查,更加严谨。

3. 防止了命名污染(封装)

4. 便于调试

5. 使用方便,一次可以定义多个常量

enum Sex        //枚举的申明定义
{Male,        //枚举常量,默认值是0开始,一次递增1female,secret
};
enum Color
{RED=1,        //也可以手动赋值GREEN=2,BLUE=4
};
int main(){enum Sex a = Male;         //枚举顾名思义就是一一列举。enum Color b = RED;printf("%d %d %d\n", Male, female, secret);printf("%d %d %d\n", RED, GREEN, BLUE);
}

 

四.联合(共用体)

定义: 联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。 

联合大小的计算 

  1. 联合的大小至少是最大成员的大小。
  2. 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍 
// 联合体/共用体  -  里面的成员同一时间只能用一个union Un
{char c;     // 1int i;     // 4
};union U
{short s[7]; // 14int i; // 4
};int main()
{union Un u = { 0 };printf("%d\n", sizeof(u)); // 输出 4// 下面三行输出同样的地址printf("%p\n", &u); printf("%p\n", &(u.c));printf("%p\n", &(u.i)); union U c = { 0 };printf("%d\n", sizeof(c)); // 输出 16 内存对齐了(最大对齐数4的倍数)printf("%p\n", &c); // 输出printf("%p\n", &(c.s)); // 输出printf("%p\n", &(c.i)); // 输出return 0;
}

 联合体、大小端结合


int check_sys()
{union J{char m;int n;};J s1;s1.n = 1;return s1.m;
}
int main()
{if (check_sys()){printf("小端\n");}else{printf("大端\n");}return 0;
}
int main()
{union{short k;char i[2];}*s, a;s = &a;s->i[0] = 0x39;s->i[1] = 0x38;printf("%x\n", a.k);return 0;
}

 union只有2字节,2字节的十六进制只有4位,所以答案CD排除。而位顺序类似小端,低地址在低处,所以39是低地址,在低位,38在高位,所以是3839

 以上就是我对【C语言】结构体的全部介绍了,身为初学者自知有很多不足,望各位大佬指点得以改正!!!感激不尽。

 

 

 

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

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

相关文章

CSS实现鼠标悬停图片上升显示

文章目录 前言一、实现效果二、实现思路 前言 当我们想在图片上面放置一些文字内容时&#xff0c;发现不管怎么放置&#xff0c;要么就是图片影响到文字的观感&#xff0c;要么就是文字挡住图片的细节&#xff0c;那么怎么可以既看到图片的细节又可以看到对图片的文字描述呢&a…

mysqld_exporter监控MySQL服务

一、MySQL授权 1、登录MySQL服务器对监控使用的账号授权 CREATE USER exporterlocalhost IDENTIFIED BY 123456 WITH MAX_USER_CONNECTIONS 3; GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO exporterlocalhost; flush privileges;2、上传mysqld_exporter安装包&#…

springboot整合MeiliSearch轻量级搜索引擎

一、Meilisearch与Easy Search点击进入官网了解&#xff0c;本文主要从小微型公司业务出发&#xff0c;选择meilisearch来作为项目的全文搜索引擎&#xff0c;还可以当成来mongodb来使用。 二、starter封装 1、项目结构展示 2、引入依赖包 <dependencies><dependenc…

基于图像形态学处理的路面裂缝检测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ...................................................... %1&#xff1a;从文件夹中读取多个…

spring的ThreadPoolTaskExecutor装饰器传递调用线程信息给线程池中的线程

概述 需求是想在线程池执行任务的时候&#xff0c;在开始前将调用线程的信息传到子线程中&#xff0c;在子线程完成后&#xff0c;再清除传入的数据。 下面使用了spring的ThreadPoolTaskExecutor来实现这个需求. ThreadPoolTaskExecutor 在jdk中使用的是ThreadPoolExecutor…

asp.net企业生产管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net 企业生产管理系统 是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c#语 言开发 二、功能介绍 (1)用户管理&…

C# OpenCvSharp 基于直线检测的文本图像倾斜校正

效果 项目 代码 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using OpenCvSharp;namespace OpenCvSharp_基于直线检测的文…

Linux基础指令(五)

目录 前言1. 打包和压缩1.1 是什么1.2 为什么1.3 怎么办&#xff1f; 2. zip & unzip3. tar 指令结语&#xff1a; 前言 欢迎各位伙伴来到学习 Linux 指令的 第五天&#xff01;&#xff01;&#xff01; 在上一篇文章 Linux基本指令(四) 当中&#xff0c;我们学习了 fin…

C语言入门Day_25 函数与指针小结

目录 前言&#xff1a; 1.函数 2.指针 3.易错点 4.思维导图 前言&#xff1a; 函数就像一个“有魔法的加工盒”&#xff0c;你从入口丢一些原材料进去&#xff0c;它就能加工出一个成品。不同的函数能加工出不同的成品。 入口丢进去的瓶子&#xff0c;水和标签就是输入&a…

【服务端 | Redis】如何使用redis 有序集合实现股票交易的订单表(价格优先、时间优先)

前两天倒腾redis的有序集合时&#xff0c;自己发现了一个问题&#xff0c;redis的有序集合在score相同的情况 下是如何排序的&#xff1f; 通过谷歌搜索&#xff0c;发现了一些线索&#xff0c;在score相同的情况下&#xff0c;redis使用字典排序&#xff0c;不过不是太明白什…

计算机网络相关知识点(二)

TCP如何保证传输过程的可靠性&#xff1f; 校验和&#xff1a;发送方在发送数据之前计算校验和&#xff0c;接收方收到数据之后同样需要计算&#xff0c;如果不一致&#xff0c;那么代表传输有问题。 确认应答序&#xff0c;序列号&#xff1a;TCP进行传输时数据都进行了编号…

排查内存泄露

1 通过Performance确认是否存在内存泄露 一个存在内存泄露的 DEMO 代码&#xff1a; App.vue <template><div><button click"myFn" style"width: 200px; height: 200px;"></button><home v-if"ishow"></hom…

Xcode15下载iOS17一直中断解决办法

1、问题描述 目前的 xcode 15 安装时&#xff0c;跟以前有个差别&#xff1a;以往的 xcode 安装时自带了 ide、sdk 等工具包&#xff0c;安装后即可开始开发&#xff0c;而最新的包则被分开成了不同的包&#xff0c;这里以 ios 开发包为例&#xff1a;Xcode_15.xip 和 iOS_17_…

软件测试之敏捷项目风险管理

敏捷项目管理是近年来最为流行的项目管理方式之一。这主要归功于敏捷管理的特点&#xff1a;尽早交付、持续改进、灵活管理、团队投入、充分测试。它能充分利用测试周期&#xff0c;并监测每个测试过程中容易出现的问题&#xff0c;加快项目迭代速度&#xff0c;从而推进项目高…

docker 配置 gpu版pytorch环境--部署缺陷检测--Anomalib

目录 一、docker 配置 gpu版pyhorch环境1、显卡驱动、cuda版本、pytorch cuda版本三者对应2、拉取镜像 二、部署Anomalib1、下载Anomalib2、创建容器并且运行3、安装Anomalib进入项目路径安装依赖测试&#xff1a; 一、docker 配置 gpu版pyhorch环境 1、显卡驱动、cuda版本、p…

《优化接口设计的思路》系列:第四篇—接口的权限控制

系列文章导航 《优化接口设计的思路》系列&#xff1a;第一篇—接口参数的一些弯弯绕绕 《优化接口设计的思路》系列&#xff1a;第二篇—接口用户上下文的设计与实现 《优化接口设计的思路》系列&#xff1a;第三篇—留下用户调用接口的痕迹 《优化接口设计的思路》系列&#…

ROS2 从头开始:第 08/8回 - 使用 ROS2 生命周期节点简化机器人软件组件管理

一、说明 欢迎来到我在 ROS2 上的系列的第八部分。对于那些可能不熟悉该系列的人,我已经涵盖了一系列主题,包括 ROS2 简介、如何创建发布者和订阅者、自定义消息和服务创建、

02 MIT线性代数-矩阵消元 Elimination with matrices

一, 消元法 Method of Elimination 消元法是计算机软件求解线形方程组所用的最常见的方法。任何情况下&#xff0c;只要是矩阵A可逆&#xff0c;均可以通过消元法求得Axb的解 eg: 我们将矩阵左上角的1称之为“主元一”&#xff08;the first pivot&#xff09;&#xff0c;第…

【springboot3.x 记录】关于Jakarta包无法引入的问题

最近想将一些项目升级到springboot3.x和java17的时候&#xff0c;发现项目依赖中有Jakarta的包&#xff0c;但是代码标红提示没有相关的类&#xff0c;特此记录一下排查问题和最终解决问题的思路方法 一、发现问题 因为之前有创建过 springboot3.x java17 的项目&#xff0c;…

第6讲:v-for使用

目录 1.循环遍历 2.v-for遍历整形变量&#xff08;99乘法表&#xff09; 3.v-for遍历普通数组 4.v-for遍历数组对象 1.循环遍历 v-for指令基于一个数组渲染一个列表&#xff0c;它和JavaScript的遍历语法相似&#xff1a; v-for”item in list” list 是一个数组&#xff0c; i…