C++ 内存空间总结

C++中的结构体所占内存空间总结

因为结构体有时候需要字节对齐。一般而言,struct 的 sizeof 是所有成员字节对齐后长度相加,而 union 的 sizeof 是取最大的成员长度。
在默认情况下,编译器为每一个变量或数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变默认的对界条件:
(1) 使用伪指令#pragma pack(n),C编译器将按照n个字节对齐。
(2) 使用伪指令#pragma pack(),取消自定义字节对齐方式。

字节对齐的细节和编译器实现相关,但一般而言,满足以下3个准则:
(1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除。
(2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节。
(3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
需要注意的是,基本类型是指前面提到的像 char、short、int、float、double 这样的内置数据类型。如果一个结构体中包含另外一个结构体成员,那么此时最宽基本类型成员不是该结构体成员,而是取基本类型的最宽值。
1、 默认对齐方式

#include <iostream>using namespace std;typedef struct
{char ch1;   // 1个字节,成员的大小为1,所以不用补齐,结构体大小就是1的整数倍,为3char ch2;char ch3;
}A1;typedef struct
{int num;   // 5个字节,补齐为8字节char ch;
}A2;typedef struct
{char a; // 1字节+补齐int b; // 8字节long long  c;  // 16字节 char d; // TotalSize= 17 ,但是不是成员c的倍数,所以+7 == 24,才是8的倍数
}A3;typedef struct
{long num; // 4字节 char * name; // 8字节short int data; // 10字节char ha; // 11字节+补齐,为12字节
}A4;int main(void)
{cout << "A1: " << sizeof(A1) << endl; // A1: 3cout << "A2: " << sizeof(A2) << endl; // A2: 8cout << "A3: " << sizeof(A3) << endl; // A3: 24cout << "A4: " << sizeof(A4) << endl; // A4: 12return 0;
}

2、位域方式

#include <iostream>using namespace std;typedef struct
{char ch1:1; // 1+3+3=7位,所以只占1个字节char ch2:3;char ch3:3;
}A1;typedef struct
{char ch1 : 5; // 5+5+3=13位,所以只占2个字节char ch2 : 5;char ch3 : 3;
}A2;typedef struct
{char ch1 : 5; // 5+5+4=14位,却占3个字节 【注意】char ch2 : 5;char ch3 : 4;
}A3;typedef struct
{char ch1 : 7; // 7+7=14位,却只占2个字节 【注意】char ch2 : 7;
}A4;typedef struct
{char ch1 : 1; // 偏移1个字节int a : 16; // 偏移5个字节int b : 16; // 偏移5个字节,因为16+16<=32,未超出“int”,所以只补齐为8个字节
}A5;typedef struct
{char ch1 : 1; // 偏移1个字节int a : 16; // 偏移5个字节int b : 17; // 偏移5个字节,因为16+17>33,超出“int”,所以补齐为12个字节
}A6;int main(void)
{cout << "A1: " << sizeof(A1) << endl; // A1: 1cout << "A2: " << sizeof(A2) << endl; // A2: 2cout << "A3: " << sizeof(A3) << endl; // A3: 3cout << "A4: " << sizeof(A4) << endl; // A4: 2cout << "A5: " << sizeof(A5) << endl; // A5: 8cout << "A6: " << sizeof(A6) << endl; // A6: 12return 0;
}

3、使用#pragma pack(n)

#include <iostream>
using namespace std;#pragma pack(1) // 要求补齐为“1”的倍数
typedef struct
{char ch1; // 1+0 字节,由于自定义了对齐方式,所以不再补齐int b; // 5 字节int a; // 9 字节
}A1;#pragma pack(2) // 要求补齐为“2”的倍数
typedef struct
{char ch1; // 1+1 字节,由于自定义了对齐方式,补齐为"2"的倍数int b; // 6 字节int a; // 10 字节
}A2;#pragma pack(4)  // 要求补齐为“4”的倍数
typedef struct
{char ch1; // 1+3 字节,由于自定义了对齐方式,补齐为"4"的倍数int b; // 8 字节int a; // 12 字节
}A3;int main(void)
{cout << "A1: " << sizeof(A1) << endl; // A1: 9cout << "A2: " << sizeof(A2) << endl; // A2: 10cout << "A3: " << sizeof(A3) << endl; // A3: 12return 0;
}

注意当 #pragma pack 的 n 值等于或超过所有数据成员长度的时候,这个 n 值的大小将不产生任何效果。

4、结构体中有结构体成员

#include "stdafx.h"
#include <iostream>using namespace std;typedef struct
{short i; // 2字节char c; // 3字节,由于后一个数据为int类型,故补齐为4字节int j; // 8字节int k; // 12字节
}A1;/*
如果结构体中的成员又是另外一种结构体类型时应该怎么计算呢?只需把其展开即可。
但有一点需要注意,展开后的结构体的第一个成员的偏移量应当是被展开的结构体中最大的成员的整数倍。
结构体stu5的成员ss.c的偏移量应该是4,而不是2。
*/
typedef struct
{short i; // 2+2字节 (因为展开后的结构体的第一个成员的偏移量为4)struct{char c; // 5字节,补齐为8int j; // 12字节}A;int k; // 16字节}A2;int main(void)
{cout << "A1: " << sizeof(A1) << endl; // A1: 12cout << "A2: " << sizeof(A2) << endl; // A2: 16return 0;
}

C++中的类所占内存空间总结

类所占内存的大小是由成员变量(静态变量除外)决定的,成员函数(这是笼统的说,后面会细说)是不计算在内的。
示例如下:
(一)

class CBase 
{ 
}; sizeof(CBase)=1

为什么空的类什么都没有是 1 呢?

c++要求每个实例在内存中都有独一无二的地址。空类也会被实例化,所以编译器会给空类隐含的添加一个字节,这样空类实例化之后就有了独一无二的地址了。所以空类的 sizeof 为 1。
(二)

class CBase 
{ int a; char p; 
}; sizeof(CBase)=8;

记得对齐的问题,这点和 struct 的对齐原则很像!int 占 4 字节,char 占一字节,补齐 3 字节。
(三)

class CBase 
{ 
public: CBase(void); virtual ~CBase(void); private: int  a; char *p; 
}; sizeof(CBase)=12

C++ 类中有虚函数的时候有一个指向虚函数的指针,在 32 位系统分配指针大小为 4 字节。无论多少个虚函数,只有这一个指针,4 字节。注意一般的函数是没有这个指针的,而且也不占类的内存。
(四)

class CChild : public CBase 
{ 
public: CChild(void); ~CChild(void); virtual void test();private: int b; 
}; sizeof(CChild)=16

可见子类的大小是本身成员变量的大小加上父类的大小。其中有一部分是虚函数表的原因,父类子类共享一个虚函数指针。
(五)

#include <iostream>class A {};class B : public A
{virtual void fun() = 0;
};// 共有继承,共用虚函数指针,没有虚基指针
class C : public B
{
};class D : public A, public B
{
};int main()
{std::cout << "sizeof(A)" << sizeof(A) << std::endl;std::cout << "sizeof(B)" << sizeof(B) << std::endl; // 空类A(0) + 虚函数指针(4)std::cout << "sizeof(C)" << sizeof(C) << std::endl; // 与B共用虚函数指针(4)std::cout << "sizeof(D)" << sizeof(D) << std::endl; // A(1+3) + 与B共用虚函数指针(4)return 0;
}/*
输出:
sizeof(A)1
sizeof(B)4
sizeof(C)4
sizeof(D)8
*/

共有继承,共用虚函数指针,没有虚基指针。
(六)

#include<iostream>/*  虚继承与继承的区别:1.多了一个虚基指针2.虚基类位于派生类存储空间的最末尾3.不会共用虚函数指针
*/class A
{char a[3];
public:virtual void fun1() {};
};// 测试一:单个虚继承,不带虚函数
class B : public virtual A
{char b[3];
};// 测试二:单个虚继承,带自己的虚函数
class C : public virtual A
{char c[3];
public:virtual void fun2() {};
};// 测试三:双重继承
class D : public virtual C
{char d[3];
public:virtual void fun3() {};
};int main()
{std::cout << sizeof(A) << std::endl; // 8std::cout << sizeof(B) << std::endl; // 8(A) + 8(B)【8 == (3+1)+虚基指针】std::cout << sizeof(C) << std::endl; // 8(A) + 12(C)【12 == (3+1)+自己的虚函数指针+虚基指针】std::cout << sizeof(D) << std::endl; // 8(A) + 12(C) + 12(D)return 0;
}/*
输出:
8
16
20
32
*/

注意,虚继承的时候 A B C D 四个类不仅不会共享虚基类指针,也不会共享虚函数指针,要和普通继承区分开来。
具体分析如下:

 class A size(8):+---0    | {vfptr}4    | a| <alignment member> (size=1)8    +---class B size(16):+---0    | {vfptr}4    | {vbptr}8    | b| <alignment member> (size=1)+---+--- (virtual base A)
12    | a| <alignment member> (size=1)
16    +---class C size(20):+---0    | {vfptr}4    | {vbptr}8    | b| <alignment member> (size=1)+---+--- (virtual base A)
12    | {vfptr}
16    | a| <alignment member> (size=1)
20    +---    class D size(32):+---0    | {vfptr}4    | {vbptr}8    | c| <alignment member> (size=1)+---+--- (virtual base A)
12    | {vfptr}
16    | a| <alignment member> (size=1)+---+--- (virtual base B)
20    | {vfptr}
24    | {vbptr}
28    | b| <alignment member> (size=1)
32    +---
  • 虚表(vftable)
  • 虚函数指针(vfptr)
  • 虚基指针(vbptr)

总 结

空的类是会占用内存空间的,而且大小是 1,原因是 C++ 要求每个实例在内存中都有独一无二的地址。

(一)类内部的成员变量:
普通的变量:是要占用内存的,但是要注意对齐原则(这点和 struct 类型很相似)。
static 修饰的静态变量:不占用内容,原因是编译器将其放在全局变量区。
(二)类内部的成员函数:
普通函数:不占用内存。
虚函数:有一个指向虚函数的指针,要占用 4 个字节,用来指定虚函数的虚拟函数表的入口地址。所以一个类的虚函数所占用的地址是不变的,和虚函数的个数是没有关系的。
(三)虚继承与继承的区别:
多了一个虚基指针。
虚基类位于派生类存储空间的最末尾。
不会共用虚函数指针。

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

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

相关文章

巧用@Conditional注解根据配置文件注入不同的bean对象

项目中使用了mq&#xff0c;kafka两种消息队列进行发送数据&#xff0c;为了避免硬编码&#xff0c;在项目中通过不同的配置文件自动识别具体消息队列策略。这里整理两种实施方案&#xff0c;仅供参考&#xff01; 方案一&#xff1a;创建一个工具类&#xff0c;然后根据配置文…

一个案例熟悉使用pytorch

文章目录 1. 完整模型的训练套路1.2 导入必要的包1.3 准备数据集1.3.1 使用公开数据集&#xff1a;1.3.2 获取训练集、测试集长度&#xff1a;1.3.3 利用 DataLoader来加载数据集 1.4 搭建神经网络1.4.1 测试搭建的模型1.4.2 创建用于训练的模型 1.5 定义损失函数和优化器1.6 使…

【Stm32-F407】Keil uVision5 的安装

文章内容如下&#xff1a; 1&#xff09;Keil uVision5 安装包的获取2&#xff09;Keil uVision5 的安装3&#xff09;Keil uVision5 中 Stm32-F407 芯片包的获取与安装4&#xff09;注册 Keil uVision5 1&#xff09;Keil uVision5 安装包的获取 Keil uVision5 安装包链接: h…

华硕平板k013me176cx线刷方法

1.下载adb刷机工具, 或者刷机精灵 2.下载刷机rom包 华硕asus k013 me176cx rom固件刷机包-CSDN博客 3.平板进入刷机界面 进入方法参考&#xff1a; ASUS (k013) ME176CX不进入系统恢复出厂设置的方法-CSDN博客 4.解压ME176C-CN-3_2_23_182.zip&#xff0c;把UL-K013-CN-3.2.…

java图书信息管理

一、项目概述 本图书信息管理系统旨在提供一个直观的用户界面&#xff0c;用于管理图书馆或书店的图书信息。系统包括图书添加、查询、借阅和归还等功能。 二、系统架构 系统采用JavaSwing作为前端UI框架&#xff0c;后端使用Java Servlet处理业务逻辑&#xff0c;数据存储在…

一键智能视频编辑与视频修复算法——ProPainter源码解析与部署

前言 视频编辑和修复确实是随着电子产品的普及变得越来越重要的技能。有许多视频编辑工具可以帮助人们轻松完成这些任务如&#xff1a;Adobe Premiere Pro&#xff0c;Final Cut Pro X&#xff0c;Davinci Resolve&#xff0c;HitFilm Express&#xff0c;它们都提供一些视频修…

ipaguard界面概览

ipaguard界面分左右2块&#xff1a;左边菜单导航栏&#xff0c;右边的功能区 左侧菜单&#xff1a;按模块分成启动界面&#xff0c;代码模块&#xff0c;文件模块&#xff0c;重签名与测试模块 右侧主功能区会随着功能变化&#xff0c;但是整体分3块&#xff0c;顶部显示过滤区…

数据指标体系

一、初识指标体系构建 1.1 什么是指标体系 指标体系是一个比较抽象的概念&#xff0c;指标体系是衡量、评估和监测特定领域或目标的系统化指标的集合。它由多个指标组成&#xff0c;这些指标可以用于衡量和评估所关注领域或目标的各个方面。指标体系通常被用于监测和评估业务绩…

柯桥生活口语学习,英语中初次见面,除了Nice to meet you,还能说什么?

第一印象非常重要。所以当你第一次见到某人时&#xff0c;留下一个好印象很重要&#xff0c;尤其是当你面对一个重要的工作或者面对某个对你来说可能非常特别的人时。 下面我列出了一些最常用的说“很高兴见到你”的表达方法&#xff0c;也包括对方的回答&#xff0c;除了nice …

Kaggle - LLM Science Exam上:赛事概述、数据收集、BERT Baseline

文章目录 一、赛事概述1.1 OpenBookQA Dataset1.2 比赛背景1.3 评估方法和代码要求1.4 比赛数据集1.5 优秀notebook 二、BERT Baseline2.1 数据预处理2.2 定义data_collator2.3 加载模型&#xff0c;配置trainer并训练2.4 预测结果并提交2.5 相关优化 前言&#xff1a;国庆期间…

用于数据增强的十个Python库

数据增强是人工智能和机器学习领域的一项关键技术。它涉及到创建现有数据集的变体&#xff0c;提高模型性能和泛化。Python是一种流行的AI和ML语言&#xff0c;它提供了几个强大的数据增强库。在本文中&#xff0c;我们将介绍数据增强的十个Python库&#xff0c;并为每个库提供…

springmvc-国际化中英文切换文件上传下载

1. 国际化 1.1 介绍 国际化(internationalization)&#xff0c; 简称国际化。一个产品支持国际化是指产品在无需做大的改变就能够适应不同的语言和地区的能力。i18n是指是一种让软件在开发阶段就支持多种语言的技术。 1.2 java.util.Locale 该类对象表示了特定的地理&#…

GET 和 POST的区别

GET 和 POST 是 HTTP 请求的两种基本方法&#xff0c;要说它们的区别&#xff0c;接触过 WEB 开发的人都能说出一二。 最直观的区别就是 GET 把参数包含在 URL 中&#xff0c;POST 通过 request body 传递参数。 你可能自己写过无数个 GET 和 POST 请求&#xff0c;或者已经看…

【Leetcode】 131. 分割回文串

给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。 回文串 是正着读和反着读都一样的字符串。 示例 1&#xff1a; 输入&#xff1a;s "aab" 输出&#xff1a;[["a","a"…

java Spring Boot 手动启动热部署

好 接下来 我们讲一个对开发非常重要的东西 热部署 因为 我们在开发过程中总会希望快点看到效果 或者 你的企业项目一般很大很复杂&#xff0c;重启是一件非常麻烦的事 或者你在和前端同事联调&#xff0c;有一点小问题 你改完就要重启 前端还得等你&#xff0c;非常不友好 那…

docker基础命令

目录 一、安装docker 1、查看是否已安装docker 2、如果系统中已经存在旧的Docker 3、配置Docker的yum库 4、安装成功后&#xff0c;执行命令&#xff0c;配置Docker的yum源 5、安装Docker 6、启动和校验 7、配置镜像加速器&#xff0c;阿里云镜像加速为例 7.1、在首页的…

minikube如何设置阿里云镜像以及如何解决dashboard无法打开的解决方案_已设置图床

minikube如何设置阿里云镜像以及如何解决dashboard无法打开的解决方案 minikube dashboard报错 considerconsider-Dell-G15-5511:~$ minikube dashboard &#x1f914; 正在验证 dashboard 运行情况 ... &#x1f680; 正在启动代理... &#x1f914; 正在验证 proxy 运行…

RDP协议流程详解(一)Connection Initiation阶段

Connetction Initiation是RDP连接的第一个阶段&#xff0c;具体包含两个消息RDP Negotiation Request和RDP Negotiation Response&#xff0c;下面结合协议数据包详细分析。 &#xff08;1&#xff09;RDP Negotiation Request 从数据包可以清晰看到此时的协议栈依次是TCP-TPKT…

swift加载h5页面空白

swift加载h5页面空白 problem 背景 xcode swift 项目&#xff0c;WebView方式加载h5页面本地h5地址是&#xff1a;http://localhost:5173/ 浏览器打开正常 Swift 加载h5&#xff1a; 百度官网 加载正常本地h5页面 加载空白&#xff0c;没有报错 override func viewDidLoad…

应力分析概要

1.概述 应力分析是一种用于结构件寿命分析的方法&#xff0c;最早的应用场景可能是路桥&#xff0c;因为西方提前我们两百年进入大工业时代。许多人工建筑的寿命是有限的&#xff0c;这类实际需求催生出寿命预测和诊断。结构件的失效&#xff0c;最根本的因素是因为应力的作用…