【C语言进阶深度学习记录】二十 结构体大小计算与结构体内存布局的详细方法

  • 结构体大小的计算往往是面试笔试常考的知识。对于简单的结构体,可以一眼看出来,对于复杂的结构体,该如何计算结构体占用内存的大小呢?
  • 本文学习所使用的编译器是gcc 4.4.5 使用其他编译器或者使用Windows 上的编译器有可能不太一样。

文章目录

    • 1 什么是内存对齐?
    • 2 struct占用的内存大小如何计算
      • 2.1 struct结构体大小计算案例分析-结构体中没有结构体
      • 2.2 struct结构体大小计算案例分析-结构体中有一个结构体成员
    • 3 总结

1 什么是内存对齐?

先来加单的说一下为什么需要内存对齐:

  1. CPU对内存的读取不是连续的,而是分成块读取的,块的大小只能是1,2,4,8,16…字节
  2. 当读取的数据未对齐,则需要两次总线周期来访问内存才能将整个数据读完,这样会降低CPU性能
  3. 某些硬件平台只能从规定的相对地址处读取特定类型的数据,否则产生硬件异常

什么是内存对齐?

  • 不同类型的数据在内存中按照一定的规则排列
  • 但是不一定是顺序的一个接一个的排列

例如下图中的两个结构体的大小是不一样的,因为它们的内存布局是不一样的:

在这里插入图片描述

它们的内存布局如下图:

在这里插入图片描述

拓展: #pragma pack 用于指定内存的对齐方式。用于修改编译器的默认对齐方式
一般来讲,在Linux系统中,编译器的默认对我方式是4字节对齐。下图中的代码,可以将对齐方式修改为1字节对齐:
在这里插入图片描述

2 struct占用的内存大小如何计算

对于不同的内存对齐方式,上面的结构体在内存中的布局是不一样的,那么我们如何来计算不同的对齐方式在内存中的布局是什么样的呢?

需要根据以下三点:

  1. 第一个struct成员永远起始于 0偏移处

  2. 每个成员按其类型大小和pack参数中较小的一个 进行对齐

    2.1 偏移地址必须能被对齐参数整除
    2.2 如果一个结构体中有一个变量也是结构体,那么这个内部的结构体成员的对齐大小就按照其内部最大的数据成员作为其大小来计算。(这里有点绕,看最后的一个例子就会明白)

  3. 结构体总长度,必须为所有对齐参数的整数倍

  • 只需要按照上述三点计算方法,就可以计算出所有结构体的大小以及内存布局的样式

2.1 struct结构体大小计算案例分析-结构体中没有结构体

如下面的代码:

  • 24-3.c
#include <stdio.h>#pragma pack(2)
struct Test1
{char  c1;short s;char  c2;int   i; 
};
#pragma pack()#pragma pack(4)
struct Test2
{char  c1;char  c2;short s;int   i;
};
#pragma pack()int main()
{printf("sizeof(Test1) = %d\n", sizeof(struct Test1));printf("sizeof(Test2) = %d\n", sizeof(struct Test2));return 0;
}
  • 编译运行结果为:

sizeof(Test1) = 10
sizeof(Test2) = 8

  • 结果分析1–Test1:

对于结构体Test1,2字节对齐,按照上述三个计算条件有:

struct Test1
{              //对齐方式             大小         起始地址                占用的内存地址位置      char  c1;       2          大于     1            0                            0            short  s;       2          等于     2            2(被对齐参数2整除)            2~3           char  c2;       2          大于     1(对齐参数)   4                            4            int   i;        2(对齐参数) 小于     4            6(被对齐参数2整除)            6~9
};  
  1. Test1的对齐方式是2字节。

然后根据:

  1. 每个成员按其类型大小和pack参数中较小的一个 进行对齐。比如上面的起始地址计算那里,都是被对齐参数2整除,这个2是类型大小和pack参数中较小的一个

    2.1 偏移地址必须能被对齐参数整除

这一条规则计算每个成员的起始地址。如上面的计算。

  1. 最后再看总体大小是否是对齐参数的整数倍。上面Test1 大小是10,是对齐参数2的整数倍
  • 结果分析2–Test2

对于结构体Test2,4字节对齐,按照上述三个计算条件有:

struct Test2
{              //对齐方式         大小           起始地址                   占用的内存地址位置      char  c1;       4      大于    1(对齐参数)       0                             0            char  c2;       4      大于    1(对齐参数)       1(被对齐参数1整除)             1           short s;        4      大于    2(对齐参数)       2(被对齐参数2整除)             2~3          int   i;        4      等于    4(对齐参数)       4(被对齐参数4整除)             5~7
};
  1. Test2 的对齐方式是4字节对齐

然后再根据:

  1. 每个成员按其类型大小和pack参数中较小的一个 进行对齐。比如上面的起始地址计算那里,对齐参数分别为1,1,2,4

    2.1 偏移地址必须能被对齐参数整除

这一条规则计算每个成员的起始地址。如上面的计算。

  1. 最后再看总体大小是否是对齐参数的整数倍。上面Test2 大小是8,是对齐参数4的整数倍

经过上面的两个结构体大小的计算,可以很容易的画出其内存图

2.2 struct结构体大小计算案例分析-结构体中有一个结构体成员

再看一个复杂的结构体大小的计算

  • 代码 24-4.c
#include <stdio.h>struct S1
{short a;long b;
};struct S2
{char c;struct S1 d;double e;
};int main()
{printf("sizeof(struct S1) = %d\n", sizeof(struct S1));printf("sizeof(struct S2) = %d\n", sizeof(struct S2));return 0;
}

运行结果为:

sizeof(struct S1) = 8
sizeof(struct S2) = 20

S1很好分析。下面我们分析S2

struct S2         //对齐方式          大小                     起始地址    占用的内存地址位置
{char c;           4      大于     1(对齐参数)                0                0 struct S1 d;      4      等于     4(这个是S1中最大参数大小)   4(4整除)       4~11(S1实际大小为8double e;         4(对齐)小于     8                         12(4整除)      12~19 };

注意: 上面S2中的S1实际大小是8,那个4是作为与对齐方式比较时(选取较小的作为对齐参数)选取S1中最大的数据成员大小。

  1. S2 的对齐方式是4字节对齐

然后再根据:

  1. 每个成员按其类型大小和pack参数中较小的一个 进行对齐。比如上面的起始地址计算那里,对齐参数分别为0,4,12

    2.1 偏移地址必须能被对齐参数整除
    2.2 如果一个结构体中有一个变量也是结构体,那么这个内部的结构体成员的对齐大小就按照其内部最大的数据成员作为其大小来计算

这一条规则计算每个成员的起始地址。如上面的计算。

  1. 最后再看总体大小是否是对齐参数的整数倍。上面S2 大小是20,是对齐参数4的整数倍

我所使用的编译器gcc 4.4.5 不支持8字节对齐方式

3 总结

注意好好理解上述计算结构体大小的计算方法。然后自己画画内存图。

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

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

相关文章

linux下vi编辑器常用命令

文本编辑器是所有计算机系统中最常用的一种工具。UNIX下的编辑器有ex,sed和vi等&#xff0c;其中&#xff0c;使用最为广泛的是vi&#xff0c;而vi命令繁多&#xff0c;论坛里好像这方面的总结不多&#xff0c;以下稍做总结&#xff0c;以资共享&#xff01;渴望更正和补充&…

【C语言进阶深度学习记录】二十二 指针的本质分析

在C语言中&#xff0c;最难的也就是指针了。如果我们了解了指针的本质&#xff0c;它就会变得简单 文章目录1 回顾&#xff1a;什么是变量&#xff1f;1.1 *号的意义1.2 指针使用示例2 传值调用与传址调用2.1 利用指针交换两个变量3 const与指针的配合3.1 const指针代码分析4 总…

前端学习(284):纯css实现翻书效果

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>纯css3实现翻书效果</title><link href"reset.css" rel"stylesheet" type"text/css"><style>body {…

DP之二

//sicily 1037. Decorations//两个字符串ch[i],ch[j],长度都一样为len,如果ch[i][1..len-1]ch[j][0..len-2],则表示连接,ch[i]->ch[j]//把每个输入的字符串当作顶点,字符串之间若是连接则建立一条边,由此得到初始图,//问题转化成求 顶点数 l-len1 的路径总数,可以用DP求解#i…

【C语言进阶深度学习记录】二十三 数组的本质分析

文章目录1 数组的概念1.1 数组的大小1.2 数组的初始化2 数组的地址与数组名3 数组名不能作为左值使用4 总结1 数组的概念 数组是相同类型的变量的有序集合数组中的元素没有名字 如下图是一个数组&#xff1a; 1.1 数组的大小 数组的大小是数组元素个数乘以元素的数据类型数组…

UBOOT问题收集(1)--balignl 16, 0xdeadbeef

.balignl 16,0xdeadbeef 是uboot起始文件下的start.S文件57行. 因为好奇这个代码的含义,所以百度了下: (http://haoyeren.blog.sohu.com/84511571.html) 先要弄明白.balignl的意思&#xff0c;这个其实应该算是一个伪操作符&#xff0c;伪操作符的意思就是机器码里&#xff0c…

【C语言进阶深度学习记录】二十四 指针与数组的本质分析一

文章目录1 回顾--数组的本质2 指针的运算2.1 指针运算代码案例分析3 指针的比较3.1 指针运算的应用代码案例分析4 总结1 回顾–数组的本质 在之前的文章&#xff0c;已经学习了数组的本质分析。下面再来回顾一下&#xff1a; 数组是一段连续的内存空间数组名可以看做是指向数…

【C语言进阶深度学习记录】二十五 指针与数组的本质分析二

文章目录1 数组的访问方式1.1 数组的访问方式代码分析2 数组和指针不同3 a 和 &a 的区别3.1 指针运算的经典代码案例分析4 数组作为函数的参数4.1 数组作为函数参数的代码案例分析5 总结开题问&#xff1a;数组名可以当做常量指针使用。那么指针是否可以当做数组来使用? …

设计模式_第二篇_策略模式

本文是我通过学习《Head First 设计模式》而写。 作为我要描述的第一个模式&#xff0c;首先要说什么是设计模式&#xff0c;然后&#xff0c;用一个实例&#xff0c;并对这个实例不断的改进&#xff0c;引出策略模式。 与其空泛地给出一堆描述&#xff0c;倒不如给出通过一个实…

【C语言进阶深度学习记录】二十六 C语言中的字符串与字符数组的详细分析

之前有一篇文章是学习了字符和字符串的&#xff0c;可以与之结合学习&#xff1a;【C语言进阶深度学习记录】十二 C语言中的&#xff1a;字符和字符串 文章目录1 字符串的概念1.1 字符串与字符数组1.2 字符数组与字符串代码分析2 字符串字面量2.1 字符串字面量的本质的代码分析…

.NET C# I/O 操作

本文内容 流 Stream 和基本操作 用于 I/O 的类 通用 I/O Stream 类 I/O 与安全 演示 如何向文本文件写入字符串如何从文本文件读取字符串如何读取数据文件如何向字符串写入字符如何从字符串读取字符参考资料修改记录2008 年毕业时&#xff0c;在解决问题时&#xff0c;第一反映…

【C语言进阶深度学习记录】二十七 C语言中字符串的相等比较

文章目录1 字符串的相等比较1.1 代码分析1 字符串的相等比较 如果有字符串s1 “Hello”; s2 “Hello” ; 在我们看来s1与s2相等。但是如果使用 “” 来判断是不准确的。因为在代码中s1与s2是是不同的字符串&#xff0c;它们位于不同的内存空间&#xff08;当然&#xff0c;…

3天搞定的小型B/S内部管理类软件定制开发项目【软件开发实战10步骤详解】

2010-10-07 21:39 by 通用权限组件源码, 16580 visits, 收藏, 编辑 十一休假&#xff0c;杭州西湖边逛了一圈只能用人山人海来形容&#xff0c;浙大紫金港校区也逛了一圈风景如画&#xff0c;建设得真不错很棒&#xff0c;假期就去了这2个地方&#xff0c;然后在家里陪老婆、看…

【C语言进阶深度学习记录】二十八 数组指针与指针数组的分析

数组指针与指针数是非常重要的概念。面试中也是经常会被问到的 文章目录1 数组的类型1.1 定义数组的类型2 数组指针2.1 数组类型和数组指针的代码分析3 指针数组3.1 指针数组代码案例分析4 总结1 数组的类型 C语言中的数组有自己特定的类型。比如 int a[5]&#xff1b; 数组a…

(运维日志)在win7安装Oracle并部署Oracle数据库

部署环境说明&#xff1a; 操 作 系 统&#xff1a;window 7 Oracle 管理应用: pl sql 1 选择支持win7的Oracle版本,下载 目前完全支持win7 操作的oracle 版本为11.2 g 补充说明:Oracle 10g版本发布时间为2003年 Oracle 11g 版本发布时间为2007年 这两个版本在win7操作系统中安…

【C语言进阶深度学习记录】二十九 main函数与命令行参数

文章目录1 main函数的返回值2 main函数的参数2.1 main函数的参数的代码案例分析3 main函数不一定是程序中第一个执行的函数4 总结1 main函数的返回值 main函数是操作系统调用的函数操作系统总是将main函数的返回值作为程序的退出状态main函数的返回值正常来说是0&#xff0c;如…

【C语言进阶深度学习记录】三十 二维数组与二维指针

文章目录1 二维指针&#xff08;指向指针的指针&#xff09;2 二维数组3 二维数组的类型3.2 如何动态申请二维数组4 总结1 二维指针&#xff08;指向指针的指针&#xff09; 指针的本质是变量指针的指针是保存指针变量的地址。如下面的代码&#xff1a; 为什么需要指向指针的存…

Windows Phone 7 开发积累_04

关于产生错误 “The as operator must be used with a reference type or nullable type (System.DateTime is a non-nullable value type) ” 今天写数据转换器&#xff0c;需要将按照时间值显示不同的时间格式字符串。 结果在Convert里发现这么写报错。 public object Conve…

【C语言进阶深度学习记录】三十一 数组作为函数参数时退化为指针

之前的学习数组的文章中&#xff0c;已经知道一维数组作为函数参数的时候&#xff0c;最终会被编译器编译为指针。今天来看看二维数组的情形 文章目录1 为什么C语言中的数组作为函数参数会退化为指针&#xff1f;2 二维数组作为函数参数如何退化2.1 代码案例分析&#xff08;传…