C语言结构体大小分析

title: C语言结构体大小分析
author: saopigqwq233
date: 2022-04-05

C语言结构体大小分析

一,基本类型

C语言自带的数据类型大小如下

数据类型 大小(字节)
char 1
short 2
int 4
long 4或8
float 4
double 8
long double 16

二,自定义类型---struct

C语言除了以上这些基本类型,还支持用户自己定义数据类型

类似于一下形式:

struct Student{
    char name[10];//学生姓名
    int age;//学生年龄
};

这里自定义了一个struct Student 类型的数据类型,包含有字符数组整形两种内容。

三,自定义结构体大小分析

1.错误示范

如果直接按照结构体内的成员类型相加,那么如上struct Student类型的大小应该是

10+4=14

似乎没什么问题,但是,当我们用以下代码做测试时,却发现出了问题

#include"stdio.h"
#include"stdlib.h"
struct Student{
    char name[10];//学生姓名
    int age;//学生年龄
};
int main()
{
    printf("%d\n",sizeof(struct Student));//测试struct Student大小
    system("pause");
    return 0;
}

运行结果
C语言结构体大小分析

可以发现,结构体大小并非结构体成员大小简单相加

为什么会出现这样的结果?这是因为C语言中存在一种行为叫结构体内存对齐。

2.内存对齐

结构体成员在内存存放时,编译器会在结构体成员之间添加填充字节,以保证结构体成员的对齐要求。

2.1对齐规则

1)结构体第一个成员永远放在0偏移处

在C语言中,结构体第一个成员的地址和整个结构体的初始地址是相同的,也就是说,结构体的第一个成员始终位于结构体的初始地址处。

可以用以下代码证明:

#include"stdio.h"
#include"stdlib.h"
struct student {
    char name[20];
    int age;
    float score;
};
int main()
{
    struct student stu;
    printf("%p\n%p\n",&(stu),&(stu.name));
    system("pause");
    return 0;
}

运行结果如下(不同设备上运行的数值可能不相同,但是一台设备上两行的数据相同):
C语言结构体大小分析

可以看到,结构体变量的地址和结构体变量第一个成员的地址是相同的。

2)从第二个成员开始,以后的每个成员的地址距离都要对齐到某个对齐数的整数倍处,这个对齐数是:(成员自身大小和默认对齐数)的较小值

这句话是什么意思呢,让我们先看一个例子:

#include"stdio.h"
struct S
{
    char a;
    int b;
    char c;
    long long d;
}s;//创建结构体变量s
int main()
{
    printf("结构体大小:%d\n",sizeof(struct S));
    printf("各个成员的地址:\n");
    printf("%p char a\n",&(s.a));
    printf("%p int b\n",&(s.b));
    printf("%p char c\n",&(s.c));
    printf("%p long long d",&(s.d));
    return 0;
}

运行结果如下:

C语言结构体大小分析

需要注意的是,%p是以16进制的格式进行输出,最后一个long long 型数据d的大小为8字节,则其结束地址应该是7d057

因此,结构体大小:7d057(16)-7d039(16)=18(16)=24(10)

接下来我们将以excel表格代表内存空间,分析每个成员在内存的分布

其中,D列的0到23代表每个地址距离起始地址的偏移量

C语言结构体大小分析

@1 首先是 char a,已知结构体第一个成员永远放在0偏移处,且char 只占1字节,那么a在内存的分布暂时是这样的

C语言结构体大小分析

@2 接下来是int b,第二个成员要对齐到对齐数的整数倍,也就是说,它的起始地址的偏移量必须是对齐数的整数倍。

对齐数:1)数据类型自身的对齐数:char型数据自身对齐值为1字节,short型数据为2字节,int/float型为4字节,double型为8字节。

2)默认对齐数:VS2013默认对齐数为8,或#pragma pack (value)时的指定对齐值value。

3)数据成员、结构体的有效对齐数:自身对齐值和指定对齐值中较小者,即有效对齐值=min{自身对齐值,当前指定的pack值}。

*需要注意的是gcc无默认对齐数

由于我使用的是gcc编译器,则int b的对齐数是其自身对齐值4,如果使用的是Visual Studio这个IDE,那么其对齐数为min(4,8)=4

因此,编译器将会在成员char a后再填补3个字节,使int b对齐,内存分布如下:

C语言结构体大小分析

@3 接下来是char c,其对齐数是min(1,8)=1,大小是1字节,那么,直接接在偏移量为8的地方

C语言结构体大小分析

@4 最后一个成员是long long d,对齐数是8,大小8字节,则需要对齐到8*2=16这个偏移量的地址,并在char c占用内存后填补7个字节,内存分布如下:

C语言结构体大小分析

3)结构体大小是所有成员对齐数中最大对齐数的整数倍

当最后一个结构体成员存放后,如果结构体大小不是所有成员的对齐数中最大对齐数的整数倍,那么会在结构体最后的成员后补字节。

#include"stdio.h"
struct S{
    int a;
    char c;
}s;
int main()
{
    printf("%d struct S\n",sizeof(struct S));
    printf("%p int a\n",&(s.a));
    printf("%p char c\n",&(s.c));
    return 0;
}
return 0;
}

接下来我们分析结构体成员是如何分布在内存中的

@1 首先是int a,对齐数为4,大小是4个字节,由于是第一个成员,直接放在0偏移处

C语言结构体大小分析

@2 其次是char c,对齐数为1,大小是一个字节,则可以存放在偏移量为4的地方

C语言结构体大小分析

@3 我们发现,如果结构体到这里就分配结束,那么结构体大小应该为5,但是实际情况却是结构体大小为8。实际上结构体也要进行内存对齐。

C语言结构体大小分析

此结构体中int a和char c的对齐数分别为4和1,结构体对齐数是成员对齐数中的最大对齐数,则此结构体对齐数大小MAX(4,1)=4,那么,就需要在char c后填补字节到结构体大小为8.

4)嵌套结构体中子结构体对齐到子结构体自己成员的最大对齐数的整数倍

@1 offsetof(type, member-designator) 求偏移量宏

此库宏需要包含头文件“stddef.h”,会生成一个类型为size_t的整形数,代表该成员在内存中距离起始地址的偏移量。

实例可参考:C 库宏 – offsetof() | 菜鸟教程 (runoob.com)

话不多说,上代码:

#include"stdio.h"
#include"stddef.h"
struct stu{
    int name;
    double grades;
};
struct team{
    char name[6];
    struct stu Students;
    int num_stu;
};
int main()
{
    struct team Team;
    printf("%d struct stu\n",sizeof(struct stu));
    printf("%d struct team\n",sizeof(struct team));
    printf("%p char name[10]\n"
    "%p struct stu Students\n"
    "%p int num_stu\n",(Team.name),&(Team.Students),&(Team.num_stu));
    printf("%d struct stu Students\n%d int num_stu\n",offsetof(struct team,Students),offsetof(struct team,num_stu));
    return 0;
}

运行结果如下:

C语言结构体大小分析

@2 根据上面三个规律,可以得出struct stu的大小是16,接下来我们看看struct team的成员是怎么分布的

@3 首先是char name[6],第一个成员直接占用6字节,我知道你们都懂?

C语言结构体大小分析

@4 然后来到了我们的重头戏,struct stu,首先,这个子结构体成员最大的对齐数是8(double),所以,需要对齐到偏移量为8的地方,符合offsetof(struct team,Students) 返回值为8的结果。因此,还需要在第一个成员后补两个字节,子结构体大小为16,占用16个字节
C语言结构体大小分析

@5 最后一个成员int num_stu,占用4个字节,对齐数为4,24=4*6,所以对齐到24偏移量处。最后一个成员放进去后,结构体大小为28,不等于结构体对齐数(成员的最大对齐数)8的整数倍,所以,需要再最后一个成员后补上4个字节,让结构体大小为32=8*4.

C语言结构体大小分析

2.2对齐数的设置

编译时的默认对齐数可以通过#pragma pack(n) 来设置,它会影响结构体中每个成员的有效对齐值,有效对齐值是编译器的对齐数和成员大小中较小的那个。例如,如果编译器的对齐数是4,而结构体中有一个double类型的成员(占8字节),那么该成员的有效对齐值是4,而不是8。

上代码:

@1 首先我们看看不带#pragma pack(n)的:

#include"stdio.h"
#include"stdlib.h"
struct stu{
	int age;
	double grades;
};
int main()
{
	printf("%d\n",sizeof(struct stu));
	return 0;
}

先分析一下,int age占用四个字节,double grades对齐数为8,则需要补4个字节到int age,double grades自己占用8个字节,所以结构体大小为16个字节,看看运行结果吧:

C语言结构体大小分析

果然如此,那加上prama pack(4) 呢。

@2 带pragma pack(4)

上代码(其实只是多了一个#pragma pack(4)而已?):

#pragma pack(4)
#include"stdio.h"
#include"stdlib.h"
struct stu{
	int age;
	double grades;
};
int main()
{
	printf("%d\n",sizeof(struct stu));
	return 0;
}

运行结果:

C语言结构体大小分析

现在编译器对齐数为4。首先,int age占用4个字节,然后,double grades 对齐数=MIN(自己大小8,编译器对齐数4)=4,所以不用在age后补字节,grades直接从偏移量为4的地方开始。两个成员放完后,结构体大小为12,结构体对齐数为MAX(成员们的对齐数)=4,12=4*3,则无需补字节。

C语言结构体大小分析


感谢你阅读我的博客,如果你对我的内容有任何的意见、建议或者问题,欢迎在评论区留言,我会尽快回复。如果你发现了我的错误或者疏漏,也请不吝指正,我会及时修改。希望我的博客能对你有所帮助,也期待与你的交流和分享。

原文链接:https://www.cnblogs.com/saopigqwq233/p/17288887.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言结构体大小分析 - Python技术站

(0)
上一篇 2023年4月17日
下一篇 2023年4月17日

相关文章

  • 解析C/C++中如何终止线程的运行

    解析C/C++中如何终止线程的运行 在C/C++中实现终止线程的运行涉及到线程的控制、同步及中断等方面。以下是终止线程的运行的完整攻略: 用共享变量来控制线程的运行 在线程运行期间,可以设置共享变量,利用共享变量来控制线程的运行。例如,将共享变量设置成一个flag,当flag=0时,线程继续运行;当flag=1时,线程退出。 示例1: #include &l…

    C 2023年5月23日
    00
  • VC6.0常用快捷键大全

    VC6.0常用快捷键大全 为什么需要快捷键? 在编程的过程中,我们需要频繁地进行复制、粘贴、撤销等操作。如果每次都使用鼠标进行操作,效率会非常低下。而快捷键的存在,可以极大地提高我们的工作效率。以下是VC6.0中的一些常用快捷键。 快捷键列表 常用快捷键 Ctrl + S 保存当前文件 Ctrl + C 复制选中内容 Ctrl + V 粘贴剪贴板内容 Ctr…

    C 2023年5月23日
    00
  • C++中异常机制的实现机制详解

    C++中异常机制的实现机制详解 异常(Exception)是指程序运行时出现的一些不可预知的错误,比如非法输入、内存分配失败等。异常处理机制可以让程序在遇到异常时不会立即崩溃,而是可以做一些处理,让程序能够在异常发生后继续执行。 C++中的异常处理机制分为三个部分:抛出异常、捕获异常和处理异常。下面我们来详细讲解它们的实现机制。 抛出异常 抛出异常使用thr…

    C 2023年5月22日
    00
  • C++如何将vector数字写入到txt文件中

    C++ 中可以使用 fstream 类来进行文件操作,包括读取和写入操作。在将 vector 数组写入文本文件中时,需要打开一个输出文件流,然后逐个将 vector 数组中的元素写入文件中即可。 以下是代码示例: 示例一 #include <fstream> #include <vector> #include <iostrea…

    C 2023年5月23日
    00
  • C++如何实现BCD码和ASCII码的相互转换

    BCD码是一种二进制编码方式,用来表示十进制数字。在每一个字节中,四位二进制数能够表示一个十进制位的数字。ASCII码则是一种字符编码方式,将每个字符映射为一个唯一的数字。 在C++中,将BCD码转换为ASCII码的一般方法是,将BCD码中的每个数字转换为对应的ASCII码数字。而将ASCII码转换为BCD码的一般方法是,将ASCII码中的每个数字转换为对应…

    C 2023年5月23日
    00
  • 浅析shellcode 反汇编模拟运行及调试方法

    让我来介绍一下关于“浅析shellcode 反汇编模拟运行及调试方法”的完整攻略。 1. 背景介绍 首先,让我们来了解一下什么是shellcode。shellcode指的是一个用于利用计算机系统的漏洞或开发后门的一段二进制代码。一般情况下,这个shellcode由黑客手动编写,并通过某个手段传送到受害机器上去执行。为了成功执行shellcode,黑客们通常会…

    C 2023年5月22日
    00
  • C语言使用广度优先搜索算法解决迷宫问题(队列)

    C语言使用广度优先搜索算法解决迷宫问题(队列)攻略 概述 本攻略主要介绍如何使用 C 语言中的广度优先搜索算法和队列解决迷宫问题。广度优先搜索算法是一种用于遍历或搜索树或图的算法,这里我们将其应用到迷宫问题中。迷宫问题是指在一个有障碍物和可通行区域的矩阵中,从起点到终点找到一条路径的问题。本攻略中,我们将使用队列来存储和处理迷宫问题中的节点。 算法流程 广度…

    C 2023年5月22日
    00
  • C++利用多态实现职工管理系统(项目开发)

    C++利用多态实现职工管理系统(项目开发)攻略 介绍 在本项目中,我们将使用C++多态机制来实现一个职工管理系统。对于不同类型的职工,我们将采用不同的数据结构进行存储。并且我们将使用纯虚函数和虚函数来实现基类和派生类之间的协作和交互,使得职工管理系统具有良好的扩展性和可维护性。 开发步骤 确定项目需求和功能 在开发项目之前,我们需要确定项目的需求和功能,这可…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部