结构体和联合体详解

news2024/12/22 22:45:01

结构体和联合体详解

  • 1.结构体struct
    • 1.1 结构体struct的设计
    • 1.2 结构体struct变量的定义和初始化
      • 举例:使用结构体Student定义并初始化stu1变量。
      • 举例:结构体嵌套的初始化方法。
    • 1.3 结构体struct成员变量的访问(获取与赋值)
      • 1.3.1 使用结构体指针访问和修改成员变量
      • 1.3.2使用结构体指针访问和修改成员变量(函数)
    • 1.4 结构体struct与数组的关系
    • 1.5 结构体struct变量在内存中的表示(遵循内存对齐原则)(结构体大小)
      • 1.5.1 内存对齐原则的深层次理解
        • 为什么要进行内存对齐原则
        • 进行内存对齐原则的优点
        • 如何指定内存对齐数
  • 2.联合体union
  • 3.结构体和联合体
    • 3.1 结构体和联合体的区别
    • 3.2 结构体和联合体的结合

1.结构体struct

客观事物(实体)是复杂的,要描述它必须从多方面进行,也就是用不同的数据类型来描述不同的方面。基于此,就有了结构体struct类型。
结构体类型是c语言的一种自定义类型(设计类型),程序开发人员可以使用结构体来封装一些属性,设计出新的数据类型。

1.1 结构体struct的设计

可以使用结构体 (struct) 来存放一组不同类型的数据。结构体的定义形式为:
struct 结构体名称
{
	成员1;
	成员2;
	...
};
其中,成员可以是基本数据类型,指针,数组或其它结构类型。

举例:如学生实体可以这样来描述:学生姓名 (用字符串描述),学生学号(用字符串描述),,性别(用字符串描述),年龄 (用整型数描述).这里用了属于 2 种不同数据类型,以及四个数据成员 (data member) 来描述学生实体。

struct Student
{
    char s_name[8];
    char s_id[8];
    char s_sex[4];
    int s_age;
};

注意

  1. 关键字struct是数据类型说明符,指出该类型是结构体类型
  2. struct定义后面的**;**必不可少
  3. 标识符Student(上述例子)是结构体的类型名

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

既然结构体是一种数据类型,那么就可以像其他基本数据类型一样用它来定义变量。
结构体是一种数据类型,是创建变量的模板,不占用内存空间;结构体变量才包含了实实在在的数据需要存储空间。

举例:使用结构体Student定义并初始化stu1变量。

struct Student
{
    char s_name[8];
    char s_id[8];
    char s_sex[4];
    int s_age;
};

int main()
{
//在.c文件中 struct Student stu1;
    Student stu1 = {"nxx","2206","w",18};
    
    return 0;
}

举例:结构体嵌套的初始化方法。

struct Date
{
    int year;
    int month;
    int day;
};


struct Student
{
    char s_name[8];
    struct Date birthday;
};

int main()
{

    struct Student stu1 = {"nxx",2000,01,01}; 
    struct Student stu2 = {"nxx",{2000,01,01}};

    struct Date date1 = {2000,01,01};
    
    struct Student stu3 = {"nxx",date1};

    return 0;
}

1.3 结构体struct成员变量的访问(获取与赋值)

获取和赋值结构体变量成员的一般格式:
结构体变量.成员名
struct Student
{
    char s_name[8];
    char s_id[8];
    char s_sex[4];
    int s_age;
};

int main()
{
    struct Student stu1 = {"nxx","2206","w",18};

    cout<< "stu1:"<< stu1.s_name<<" "<<stu1.s_id<<" "<< stu1.s_sex<<" "<< stu1.s_age<<endl;
  
    
    //stu1.s_name = "aaa"; // error
    strcpy(stu1.s_name,"aaa");
    cout<< "stu1:"<< stu1.s_name<<" "<<stu1.s_id<<" "<< stu1.s_sex<<" "<< stu1.s_age<<endl;
    
    struct Student stu2 = stu1;
    cout<< "stu2:"<< stu2.s_name<<" "<<stu2.s_id<<" "<< stu2.s_sex<<" "<< stu2.s_age<<endl;
   
    
    return 0;
}

注意:对结构变量整体赋值有三种情况

  1. 定义结构体变量 (用{ }初始化)
  2. 用已定义的结构变量初始化
  3. 结构体类型相同的变量可以作为整体相互赋值.

在其他情况的使用过程中只能对成员逐一赋值。
在c 语言中不存在对结构体类型的强制转换 (和内置类型的区别).

1.3.1 使用结构体指针访问和修改成员变量

内置类型能够定义指针变量,结构体类型也可以定义结构体类型指针。
结构体类型指针访问成员的获取和赋值形式:

  1. (*p).成员名(.的优先级高于 *,(*p) 两边的括号不能少)
  2. p-> 成员名 (>是 减号加大于号,中间没有空格称为指向符)
struct Student
{
    char s_name[8];
    char s_id[8];
    char s_sex[4];
    int s_age;
};

int main()
{
    struct Student stu1 = {"nxx","2206","w",18};

    struct Student *p = &stu1;
     
    (*p).s_age = 17;
    cout<< "stu1:"<< p->s_name<<" "<< p->s_id<<" "<< p->s_sex<<" "<< p->s_age<<endl;
  
    p->s_age = 16;
    cout<< "stu1:"<< (*p).s_name<<" "<<(*p).s_id<<" "<< (*p).s_sex<<" "<< (*p).s_age<<endl; 
   
    
    return 0;
}

1.3.2使用结构体指针访问和修改成员变量(函数)

struct Student
{
    char s_name[8];
    char s_id[8];
    char s_sex[4];
    int s_age;
};
void Print_a(struct Student stu)
{
    cout<< "Print_a:"<< stu.s_name<<" "<<stu.s_id<<" "<< stu.s_sex<<" "<< stu.s_age<<endl; 
   
}
void Print_b(struct Student *stu)
{
    cout<< "Print_b:"<< stu->s_name<<" "<<stu->s_id<<" "<< stu->s_sex<<" "<< stu->s_age<<endl; 
   
}
int main()
{
    struct Student stu1 = {"nxx","2206","w",18};

    Print_a(stu1);
    Print_b(&stu1);
    
    return 0;
}

1.4 结构体struct与数组的关系

struct Student stu1[] = {
        {"aaa","2206","w",18},
        {"bbb","2206","w",18}
    };

1.5 结构体struct变量在内存中的表示(遵循内存对齐原则)(结构体大小)

struct Node
{
    char cha;
    int ia;
    char chb;
};

int main()
{
    struct Node node = {'a',1,'a'};

    cout<<"sizeof(node)" << sizeof(node)<< endl; //12
    cout<< "sizeof(struct Node)"<< sizeof(struct Node)<< endl;//12
    
    return 0;
}

那么为什么sizeof(node)为什么会==12呐,按照我们之前的理解:sizeof(node) = sizeof(node.cha) +sizeof(node.cha) +sizeof(node.cha) = 6。但是我们实际运行起来,其大小却是12。这是为什么呐?
接下来,就要引出struct的内存对齐原则:

  1. 对齐数=min(编译器默认的一个对齐数,sizeof(该成员)) ;linux 中默认为4,vs 中的默认值为8)
  2. 结构体变量的首地址能够被min(结构体最大基本类型成员大小,对齐基数中)所整除
  3. 第一个成员在与结构体变量偏移量为0的地址
  4. 其他成员变量要对齐到对齐数的整数倍的地址处,如有需要编译器会在成员之间加上填充字节 (internal padding)
  5. 结构体总大小为最大对齐数的整数倍(每个成员变量除了第一个成员都有一个对齐数),如有需要编译器会在成员之间加上填充字节 (internal padding)

依据内存对齐原则,下面对结构体Node的大小进行分析:

struct Node
{
    char cha;//1字节(对齐数==1,)
    //这里要填充的原因参考4
    int ia;//4字节(前面有3字节填充。对齐数==4,没有填充字节)
    char chb;//1字节(,对齐数==1,后面有3字节对齐)
    //这里要填充的原因参考5
};

在这里插入图片描述

1.5.1 内存对齐原则的深层次理解

为什么要进行内存对齐原则
  1. 内存大小的基本单位是字节(byte)。
    理论上来讲,可以从任意地址访问变量。
    但是实际上,CPU 并非逐字节读写内存,而是以 2,4,或 8 的倍数的字节块来读写内存,因此就会对基本数据类型的地址作出一些限制,即它的地址必须是 2,4或8 的倍数。那么就要求各种数据类型按照一定的规则在空间上排列,这就是对齐。
    即内存对齐可以使得CPU一次就可以将所需的数据读进来。
  2. 有些平台每次读都是从偶地址开始,如果一个 int型(假设为 32 位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这 32bit,而如果存放在奇地址开始的地方,就需要 2 个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该 32bit 数据。显然在读取效率上下降很多。
    同样,相比于存取对齐的数据,存取非对齐的数据需要花费更多的时间;
  3. 由于不同平台对齐方式可能不同,如此一来,同样的结构在不同的平台其大小可能不同,在无意识的情况下互相发送的数据可能出现错乱,甚至引发严重的问题
  4. 此外,会有一些硬件设备的限制:
    某些硬件设备只能存取对齐数据,存取非对齐的数据可能会引发异常;
    某些硬件设备不能保证在存取非对齐数据的时候的操作是原子操作;
    某些处理器虽然支持非对齐数据的访问,但会引发对齐陷阱(alignment trap);
    某些硬件设备只支持简单数据指令非对齐存取,不支持复杂数据指令的非对齐存取。
进行内存对齐原则的优点
  1. 便于在不同的平台之间进行移植,因为有些硬件平台不能够支持任意地址的数据访问,只能在某些地址处取某些特定的数据,否则会抛出异常
  2. 提高内存的访问效率,因为 CPU 在读取内存时,是一块一块的读取
如何指定内存对齐数

预处理指令#pragma pack(n) 可以改变默认对齐数。n 取值是 1,2,4,8,16

#pragma pack(1)
struct Node
{
    char cha;
    int ia;
    char chb;
};

//#pragma pack(1)需要以#pragma pack()结束,表示该种对齐方式到此结束
#pragma pack() 

int main()
{
    struct Node node = {'a',1,'a'};

    cout<<"sizeof(node)" << sizeof(node)<< endl; //6
    
    return 0;
}

2.联合体union

在联合体中,各成员共享同一段内存空间,一个联合变量的长度等于各成员中最长的长度
应该说明的是,这里所谓的共享不是指把多个成员同时装入一个联合变量内,而是指该联合变量可被赋予任一成员值,但每次只能赋一种值,赋入新值则冲去旧值。(覆盖)
一个联合体类型必须经过定义之后,才能使用它,才能把一个变量声明定义为该联合体类型。

union Node
{
    char cha;
    int ia;
    short sa;
};



int main()
{
    union Node node;
    
    node.ia = 0x01234567;
    
    cout<< "sizeof(node):" << sizeof(node)<< endl; //6
            
                        //0X67->char类型g   0X01234567    0X4567
    cout<< "node:"<< hex<< node.cha<< " "<< node.ia<< " "<< node.sa<< endl;   
    
    return 0;
}

图示说明:
在这里插入图片描述

3.结构体和联合体

3.1 结构体和联合体的区别

  1. 结构体将不同类型的数据组合成一个整体;
    共同体是不同类型的几个变量共同占用一段内存。
  2. sizeof()大小:
    sizeof(struct)是内存对齐后所有成员长度的总和
    sizeof(union)是内存对齐后最长数据成员的长度
  3. 内存使用:
    结构体中的每个成员都有自己独立的地址,它们是同时存在的
    共同体中的所有成员占用同一段内存,它们不能同时存在(会覆盖)

3.2 结构体和联合体的结合

union UnDat1
{
	unsigned int xi;
	unsigned char s1,s2,s3,s4;
};

union UnDat2
{
	unsigned int xi;
	struct 
	{
		unsigned char s1,s2,s3,s4;
	};
};

在这里插入图片描述

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

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

相关文章

科研宝典·工具篇 | CiteSpace: 科学文献分析

​文献计量学是指用数学和统计学的方法&#xff0c;定量地分析一切知识载体的交叉科学。它是集数学、统计学、文献学为一体&#xff0c;注重量化的综合性知识体系。特别是&#xff0c;信息可视化技术手段和方法的运用&#xff0c;可直观的展示主题的研究发展历程、研究现状、研…

小度打头阵,百度大模型能否“赋能万物”?

文 | 智能相对论 作者 | 楷楷 近日&#xff0c;百度集团副总裁、小度科技原CEO景鲲因个人原因辞任&#xff0c;百度集团副总裁、首席信息官&#xff08;CIO&#xff09;李莹轮岗出任小度科技CEO&#xff0c;并向李彦宏直接汇报。 随着“景鲲时代”落幕&#xff0c;新任CEO李…

软件测试(二)用例

软件测试 测试用例&#xff08;Test Case&#xff09;是为了实施测试而向被测试的系统提供的一组集合&#xff0c;这组集合包含&#xff1a;测试环境、操作步骤、测试数据、预期结果等要素 及测试用例的四要素&#xff1a; 测试环境操作步骤测试数据预期结果 评价测试用例的…

QML(11)——qml界面之间通信方式详解

目录 一、属性绑定1、直接绑定 property01: property02实例代码 2、条件绑定 Qt.binding实例代码 二、信号传递1、on<Property>Changed实例代码 2、on<Signal>实例代码 3、条件信号传递 connect实例代码 4、Connections 一、属性绑定 属性绑定具有持续性 1、直接…

轻舟500天搞定征程5,L2+方案明年交付

作者 | Amy 编辑 | 德新 2022年L2商业化赛道发展得如火如荼&#xff0c;各家主机厂和智驾供应商的高速NOA相继迸发。 华为、小鹏率先在上海、广州多地开放城市NOA&#xff0c;蔚来、理想紧随其后。宝骏云朵联合大疆车载&#xff0c;吉利博越L联合地平线&#xff0c;将搭载高…

MySQL服务安装与登录

&#xff08;1&#xff09;以管理员身份启动命令提示符&#xff1a; &#xff08;2&#xff09;定位到安装目录的bin目录下&#xff08;根据自己的安装路径进行调整即可&#xff09;。先输入“d:”&#xff0c;定位到d盘&#xff0c;输入cd空格文件路径&#xff08;直接复制粘贴…

Windows系统安装ESP32 ESP-IDF开发环境

陈拓 2023/10/07-2023/10/10 1. 概述 在《用乐鑫国内Gitee镜像搭建ESP32开发环境》 https://blog.csdn.net/chentuo2000/article/details/113424934?spm1001.2014.3001.5502 一文中我们讲述了Linux环境下ESP32开发工具ESP-IDF的安装。 本文讲述Windows环境下ESP32开发工具…

C#使用PPT组件的CreateVideo方法生成视频

目录 需求 实现 CreateVideo方法 关键代码 CreateVideoStatus 其它 需求 我们在使用PowerPoint文档时&#xff0c;经常会使用其导出功能以创建视频&#xff0c;如下图&#xff1a; 手工操作下&#xff0c;在制作好PPT文件后&#xff0c;点击文件 -> 导出 -> 创建视…

new、delete与构造、析构函数的关系

一、指针对象的动态建立与释放 动态建立&#xff1a;new 实现&#xff0c;为指针对象分配空间 动态释放&#xff1a;delete 实现&#xff0c;为指针对象释放空间 #include<iostream> using namespace std; class A{private:int a;public:A(int a10):a(a1){cout<<&…

pyflink 环境测试以及测试案例

1. py 的 环境以来采用Anaconda环境包 安装版本&#xff1a;https://www.anaconda.com/distribution/#download-section Python3.8.8版本&#xff1a;Anaconda3-2021.05-Linux-x86_64.sh 下载地址 https://repo.anaconda.com/archive/ 2. 安装 bash Anaconda3-2021.05-Linux-x…

基于SSM+Vue的汽车服务商城系统

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

SpringFrameWork之注解类管理Bean

1 Bean 注解方式的扫描 1.1 .1注解理解 和 XML 配置文件一样&#xff0c;注解本身并不能执行&#xff0c;注解本身仅仅只是做一个标记&#xff0c;具体的功能是框架检测到注解标记的位置&#xff0c;然后针对这个位置按照注解标记的功能来执行具体操作。 1.1.2 扫描理解 Sp…

怎么使用LightPicture开源搭建图片管理系统并远程访问?【搭建私人图床】

文章目录 1.前言2. Lightpicture网站搭建2.1. Lightpicture下载和安装2.2. Lightpicture网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 现在的手机越来越先进&#xff0c;功能也越来越多&#xff0c;而手机…

【单片机毕业设计】【hj-006-1】烟雾、甲烷气体检测 | 空气质量检测 | 有害气体检测

一、基本介绍 项目名&#xff1a; 基于单片机的烟雾、甲烷气体检测系统设计 基于单片机的空气质量检测 系统设计 基于单片机的有害气体检测系统设计 项目编号&#xff1a;mcuclub-hj-006-1 单片机类型&#xff1a;STM32F103C8T6 具体功能&#xff1a; 1、通过MQ-2检测烟雾值&…

人防行业通信系统

深圳市华脉智联科技有限公司是一家拥有核心自主知识产权的高科技企业&#xff0c;公司致力于公网对讲、融合通信、应急通信、执法调度等领域的系统和技术的开发和探讨&#xff0c;为行业用户提供一整套以通信为基础&#xff0c;软硬件结合的实战解决方案。华脉智联始终坚持将解…

2023年淘宝天猫双十一什么时候开始?天猫双十一满减活动规则和优惠力度是多少

2023年天猫淘宝双十一活动将在10月24日20时开启&#xff0c;同时包含两波正式开买时间点&#xff0c;分别为10月31日20时和11月10日20时。 一、2023天猫淘宝双十一活动时间表 第一波 (1)预售 预售预热&#xff1a;2023年10月24日14:00:00-2023年10月24日19:59:59 定金*支付…

工控机通过Profinet转Modbus RTU网关与报警控制仪通讯配置案例

本文将以工控机通过Profinet转Modbus RTU网关&#xff08;XD-MDPN100&#xff09;与报警控制仪通讯的实际案例为例&#xff0c;介绍Profinet转Modbus RTU网关&#xff08;XD-MDPN100&#xff09;在工业通信的应用。 在某个生产车间的报警系统中&#xff0c;报警控制仪是起到监…

嘉立创新建元件封装进行DRC检测发现无封装

新建元件和封装 元件就是原理图中放的主要是引脚对应正确&#xff0c;封装就是pcb设计中对应元件实际放置的焊盘。所以元件和封装需要关联 关联的时候需要注意 有时候出现元件无封装的情况&#xff0c;一般是因为没有管理封装&#xff08;简单失误&#xff09;或者是关联封…

【NPM】vuex 数据持久化库 vuex-persistedstate

在 GitHub 上找到&#xff1a;vuex-persistedstate。 安装 npm install --save vuex-persistedstate使用 import { createStore } from "vuex"; import createPersistedState from "vuex-persistedstate";const store createStore({// ...plugins: [cr…

echarts设置竖向不同区间范围颜色,并且x轴自定义轴线刻度范围

需求&#xff1a;设置竖向范围区间&#xff0c;不同范围区间颜色不同并且提示信息不同&#xff0c;之后修改x轴的固定间距范围&#xff0c;让0-200-400-600改为0-340-476-754这种&#xff0c;在这我是markLine的方式实现的&#xff0c;这边我还用到x轴的翻转所以显示的是镜像的…