【C语言】结构体内存对齐

目录

引入结构体 

 结构的声明

创建和初始化

 内部元素的使用;

特殊声明:

结构体在内存中的对齐

练习: 



引入结构体 

        C语言有各种数据类型,我们已经对一些数据类型很熟悉:

  1. 整型(int)- 存储整数值,包括有符号和无符号两种类型。

  2. 浮点型(float、double、long double)- 存储实数值,包括单精度(float)、双精度(double)和长双精度(long double)三种类型。

  3. 字符型(char)- 存储单个字符,包括有符号和无符号两种类型。

  4. 指针类型(pointer)- 存储内存地址,可以指向其他数据类型。

         但是,仅仅有这些类型是无法描述一些对象的特征的,比如:一个人的多个信息,一个家庭的多个成员等等,那么这时候,就要用到结构体类型来描述一个比较复杂的对象。

        结构体类型就像其他简单数据类型(int,float,double)一样,也是一种数据类型,但是它比较复杂,可以包含多个 简单的数据类型, 这些被包含的数据类型可以是不同的类型。

 

         总之,结构体是为了描述复杂对象而引入的一种新的数据类型。

 结构的声明

        C语言中,结构体(struct)是一种自定义的数据类型,可以将不同的数据类型组合在一起形成一个 新的 数据类型。结构体的定义格式如下:

struct 结构体名称
{数据类型1 成员变量1;数据类型2 成员变量2;...
} 创建的同类型结构体名称;

         有了结构体类型,我们就可以描述复杂对象的特征。例如,要描述一个家庭的成员的信息,可以:

        创建结构体类型Person——包含变量字符串型name, 整型age, 双精度浮点型heignt;

        创建结构体类型Family——包含结构体类型Person, 字符串型location;

 代码如下:

​//声明Person结构体类型struct Person {char name[20];int age;double height;
};//声明Family结构体类型struct Family
{struct Person;char location[10];
}Family_of_my;​

从上述代码可以得出:

        结构体名称 根据具体的实际意义来决定。

        成员变量可以有多个,每个成员变量的数据类型可以是任意数据类型,包括简单数据类型和复杂数据类型。也就是说,一个结构体内部可以再嵌套包含一个结构体类型。

 

        总结,结构体内部的数据类型多样,可以是简单类型,也可以是复杂类型。

 

创建和初始化

        结构体的初始化可以按照不同方式进行:

        1.按照结构体内部元素顺序

        2.按照指定顺序

#include <stdio.h>
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号};int main()
{//按照结构体成员的顺序初始化struct Stu s = { "张三", 20, "男", "20230818001" };printf("name: %s\n", s.name);printf("age : %d\n", s.age);printf("sex : %s\n", s.sex);printf("id : %s\n", s.id);//按照指定的顺序初始化struct Stu s2 = { .age = 18, .name = "lisi", .id = "20230818002", .sex = "⼥printf("name: %s\n", s2.name);printf("age : %d\n", s2.age);printf("sex : %s\n", s2.sex);printf("id : %s\n", s2.id);return 0;}

        总结, 初始化可以按照正常顺序,还可按照指定顺序。

 内部元素的使用;

        要用到结构体操作符。 

​
​
#include <stdio.h>
struct Stu
{char name[20];//名字int age;//年龄char sex[5];//性别char id[20];//学号};int main()
{//按照结构体成员的顺序初始化struct Stu s = { "张三", 20, "男", "20230818001" };printf("name: %s\n", s.name);printf("age : %d\n", s.age);printf("sex : %s\n", s.sex);printf("id : %s\n", s.id);//按照指定的顺序初始化struct Stu* s2 = &s;printf("name: %s\n", s2->name);printf("age : %d\n", s2->age);printf("sex : %s\n", s2->sex);printf("id : %s\n", s2->id);return 0;}​​

 

         总结,要使用结构体内部元素,要用到结构体操作符:

.  和 ->

用例:

结构体.成员名

结构体指针->成员名

特殊声明:

        在声明结构的时候,可以不完全的声明。也就是,可以省略结构体的标签。

例如:

//匿名结构体类型
struct
{
int a;
char b;
float c;
}x;struct
{
int a;
char b;
float c;
}a[20], *p;

        但是,这两个匿名的类型完全一致的结构体是同一个结构体类型吗?

也就是说:

p = &x;

        这段代码合法吗?

        经过测试,编译器会把上面的两个声明当成完全不同的两个类型,所以是⾮法的。
匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使⽤⼀次。
 

 

结构体在内存中的对齐

        现在,我们已经对结构体有了一个基本的认识,那么,结构体的大小是如何计算的呢?


 

        我们先看一个实例:

计算下列两个结构体的大小:


//练习1
struct S1
{
char c1;
int i;
char c2;
};
printf("%d\n", sizeof(struct S1));//练习2
struct S2
{
char c1;
char c2;
int i;
};
printf("%d\n", sizeof(struct S2));

        我们可以先猜测:

        既然两个结构体内部的变量类型和数目完全一致,那么我们有理由猜测两个结构体大小相同,但是,实际情况并不是这样:

 

        其实,结构体在内存中的存储有一套自己的规则:

 首先,引入一个概念:

        对齐数

        对齐数=编译器默认的⼀个对齐数   和 该成员变量长度的较小值

        默认对齐数 :

VS 中默认的值为 8 


Linux中gcc没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

结构体的基本对齐方法是:

        1.结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
        2.其他成员变量要对齐到对齐数的整数倍的地址处

结构体大小的计算:


        1.结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。


        2.如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

 

        这样一来,我们分析上两个问题:


 

        对于s1,先放入char类型,由于char类型是1个字节,小于8(VS默认值),所以直接放在0地址处。

        值得一提,长度是1的char无论何时都不会浪费空间,它会直接顶着上一个数据的存储空间放置。

        int型,大小4字节,小于8(VS默认值),所以放在sizeof(int)的整数倍的地址处,也就是4开始,到7结束。

        char 紧跟着int型;


        由于最大对齐数是4,此时结构体的最小大小大于8,要满足4的整数倍,那么这个结构体的大小向上取 是12。

        对于s2 :

        放入两个char,一个int后的结构体最小大小正好是8,那么结构体总大小就是8。

 

 


练习: 

int main(int argc, char* argv[])
{struct tagTest1{short a;char d; long b;   long c;   };struct tagTest2{long b;   short c;char d;long a;   };struct tagTest3{short c;long b;char d;   long a;   };struct tagTest1 stT1;struct tagTest2 stT2;struct tagTest3 stT3;printf("%d %d %d", sizeof(stT1), sizeof(stT2), sizeof(stT3));return 0;

        结果: 


 完~

未经作者同意禁止转载

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

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

相关文章

京东商品详情数据在数据分析行业中的重要性

京东商品详情数据在数据分析行业中具有重要作用。这些数据提供了丰富的信息&#xff0c;可以帮助企业了解市场趋势、消费者需求、产品表现以及运营策略等多个方面。 首先&#xff0c;京东商品详情数据可以为企业提供市场趋势分析的依据。通过观察商品的销售量、销售额、价格等…

c语言:理解和避免野指针

野指针的定义&#xff1a; 野指针是指一个指针变量存储了一个无效的地址&#xff0c;通常是一个未初始化的指针或者指向已经被释放的内存地址。当程序尝试使用野指针时&#xff0c;可能会导致程序崩溃、内存泄漏或者其他不可预测的行为。因此&#xff0c;在编程中需要特别注意…

Pandas中DataFrame对象的创建与常用属性方法(第2讲)

Pandas中DataFrame对象的创建与常用属性方法(第2讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔…

智能优化算法应用:基于孔雀算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于孔雀算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于孔雀算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.孔雀算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

[足式机器人]Part2 Dr. CAN学习笔记-数学基础Ch0-2 特征值与特征向量

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-数学基础Ch0-2 特征值与特征向量 1. 定义1.1 线性变换1.2 求解特征值&#xff0c;特征向量1.3 应用&#xff1a;对角化矩阵——解耦Decouple 2. Summary 1. 定义 A v ⃗ λ v ⃗ A\vec{v}\lambd…

【网络奇缘】- 计算机网络|深入学习物理层|网络安全

​ &#x1f308;个人主页: Aileen_0v0&#x1f525;系列专栏: 一见倾心,再见倾城 --- 计算机网络~&#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 回顾链接&#xff1a;http://t.csdnimg.cn/ZvPOS 这篇文章是关于深入学习原理参考模型-物理层的相关知识点&…

Linux权限命令详解

Linux权限命令详解 文章目录 Linux权限命令详解一、什么是权限&#xff1f;二、权限的本质三、Linux中的用户四、linux中文件的权限4.1 文件访问者的分类&#xff08;人&#xff09;4.2 文件类型和访问权限&#xff08;事物属性&#xff09; 五、快速掌握修改权限的做法【第一种…

实战1-python爬取安全客新闻

一般步骤&#xff1a;确定网站--搭建关系--发送请求--接受响应--筛选数据--保存本地 1.拿到网站首先要查看我们要爬取的目录是否被允许 一般网站都会议/robots.txt目录&#xff0c;告诉你哪些地址可爬&#xff0c;哪些不可爬&#xff0c;以安全客为例子 2. 首先测试在不登录的…

Docker Network(网络)——8

目录&#xff1a; Docker 为什么需要网络管理Docker 网络架构简介 CNMLibnetwork驱动常见网络类型 bridge 网络host 网络container 网络none 网络overlay 网络docker 网络管理命令 docker network createdocker network inspectdocker network connectdocker network disconne…

class072 最长递增子序列问题与扩展【算法】

class072 最长递增子序列问题与扩展【算法】 code1 300. 最长递增子序列 // 最长递增子序列和最长不下降子序列 // 给定一个整数数组nums // 找到其中最长严格递增子序列长度、最长不下降子序列长度 // 测试链接 : https://leetcode.cn/problems/longest-increasing-subsequen…

你知道MySQL中 group by 怎么优化吗

更好的阅读体验&#xff0c;请点击 YinKai s Blog。 ​ 在 MySQL 中 group by 用于按照一个或多个列对结果集进行分组。在讨论 group by 怎么优化之前&#xff0c;我们先来看看 group by 的执行流程&#xff0c;这样我们才能对症下药。 group by 执行流程 ​ 我们先用下面的 …

Ubuntu 18.04使用Qemu和GDB搭建运行内核的环境

安装busybox 参考博客&#xff1a; 使用GDBQEMU调试Linux内核环境搭建 一文教你如何使用GDBQemu调试Linux内核 ubuntu22.04搭建qemu环境测试内核 交叉编译busybox 编译busybox出现Library m is needed, can’t exclude it (yet)的解释 S3C2440 制作最新busybox文件系统 https:…

2024年网络安全竞赛-Web安全应用

Web安全应用 (一)拓扑图 任务环境说明: 1.获取PHP的版本号作为Flag值提交;(例如:5.2.14) 2.获取MySQL数据库的版本号作为Flag值提交;(例如:5.0.22) 3.获取系统的内核版本号作为Flag值提交;(例如:2.6.18) 4.获取网站后台管理员admin用户的密码作为Flag值提交…

udp多播组播

import socket ,struct,time# 组播地址和端口号 MCAST_GRP 239.0.0.1 MCAST_PORT 8888 # 创建UDP socket对象 sock socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # 绑定socket对象到本地端口号 # sock.bind((MCAST_GRP, MCAST_PORT)) …

【4】PyQt输入框

1. 单行文本输入框 QLineEdit控件可以输入单行文本 from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QVBoxLayout from PyQt5.QtCore import * from PyQt5.QtGui import QIcon import sysdef init_widget(w: QWidget):# 修改窗口标题w.setWindowTitle(单行输…

前端面试——CSS面经(持续更新)

1. CSS选择器及其优先级 !important > 行内样式 > id选择器 > 类/伪类/属性选择器 > 标签/伪元素选择器 > 子/后台选择器 > *通配符 2. 重排和重绘是什么&#xff1f;浏览器的渲染机制是什么&#xff1f; 重排(回流)&#xff1a;当增加或删除dom节点&…

【面试经典150 | 二叉树】从中序与后序遍历序列构造二叉树

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;递归 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及到的数据结构等内容…

Android : Room 数据库的基本用法 —简单应用

1.Room介绍&#xff1a; Android Room 是 Android 官方提供的一个持久性库&#xff0c;用于在 Android 应用程序中管理数据库。它提供了一个简单的 API 层&#xff0c;使得使用 SQLite 数据库变得更加容易和方便。 以下是 Android Room 的主要特点&#xff1a; 对象关系映射…

9.MySQL 索引

目录 ​​​​​​​概述 概念&#xff1a; 单列索引 普通索引 创建索引 查看索引 删除索引 唯一索引 创建唯一索引 删除唯一索引 主键索引 组合索引 创建索引 全文索引 概述 使用全文索引 空间索引 内部原理 相关算法&#xff1a; hash算法 二叉树算法 …