[C++]sizeof各种情况详细分析+内存补齐机制

sizeof操作符的作用是返回一个对象或类型名的长度,返回值的类型为size_t,长度的单位是字节。sizeof表达式的结果是编译时常量。

测试环境:win10 64位 dev c++

  • 先上几个基本数据类型
short a;
int b;
float c;
long d;
double e;
long long f;
cout<<sizeof(a)<<endl;//2
cout<<sizeof(b)<<endl;//4
cout<<sizeof(c)<<endl;//4
cout<<sizeof(d)<<endl;//4
cout<<sizeof(e)<<endl;//8
cout<<sizeof(f)<<endl;//8
  • 再看看数组和字符串这类
char a;
string b;
int c[10];
char d[]="abc";
cout<<sizeof(a)<<endl;//1
cout<<sizeof(b)<<endl;//8
cout<<sizeof(c)<<endl;//40  10个int 4字节=40
cout<<sizeof(d)<<endl;//4 还要算上末尾的NULL 3+1=4
  • 再扯上指针
int f[10];
int *p=f;
cout<<sizeof(p)<<" "<<sizeof(*p)<<endl;
//指针大小我这里由于是64位 输出是8 ,输出4也是有可能的
//后面是输出指针指向的int型对象,即等于sizeof(int)=4
  • 再试试看普通函数
int fun(){return 0;}
int main(){
    cout<<sizeof(fun())<<endl;//4 这里也是根据int型是4字节 得到的
    ...
}
  • 该试试看类了,开始变得有趣了哦~
class classTest{    
    public:	
	int i;//4
        char c;//1
	short s;//2		 
};
int main(){
    cout<<sizeof(classTest)<<endl;//8
    ...
}

这里就单独拉出来分析吧,求类的大小的时候,会对类里的成员大小进行求和,根据我们上面做过的测试,我们可以很快得到int型4字节,char型1字节,short型2字节,那么这里为什么得到的是8字节呢?
先别急,我们接着再看一下对上式的稍稍改变

class classTest{    
	public:	
	    char c;//1
	    int i;//4
	    short s;//2		 
};
int main(){
    cout<<sizeof(classTest)<<endl;//12
    ...
}

咦?为什么改变了一下类成员i和c的位置,类占用内存大小怎么变成12了,这里我们就要牵扯到内存补齐的概念了。

在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的数据单元。在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。

上述的结构struct同样适用于class,我们现在重新对我们上面的代码进行分析

class classTest{    
	public:	
	    int i;//4
	    char c;//1
	    short s;//2		 
};

第一个成员i为int型,其自然对界为4,假设起始地址为0000,占据了4个字节,则现在使用了占用了0000-0003的地址。第二个成员c为char型,其自然对界为2,小于int型的自然对界,占据1个字节,所以占用了0004的地址。第三个成员s为short型,其自然对界为1,依旧小于最大的int型,即占用了0005-0006的地址,并且s和c还处于int型的自然对界地址上(2+1<4),然后在成员s后补一个空字节,补齐了字节对界 所以最后占用了0000~0007一共8个字节

再看第二段代码

class classTest{    
	public:	
	    char c;//1
	    int i;//4
	    short s;//2		 
};
int main(){
    cout<<sizeof(classTest)<<endl;//12
    ...
}

这里的第一个成员c是char型,其自然对界为1,假设起始地址为0000,占据了1个字节。第二个成员i是int型,其自然对界为4,大于char型的了,那大的就当老大了呗,int型就跟char型说你得按照我的格式来,所以char型没办法,地址0001-0003就被空字节补齐了,int型就从0004-0007占用了4个字节。第三个成员s是short型,其自然对界为2,还是没有int型大,所以地址从0008-0009是其两个字节的占用,0010-0011则用空字节补齐了。最后一共占用12个字节

我们来看看之前CSDN一道很火的题目

struct s1{
    short a;
    long b;
};
struct s2{
    char c;
    s1 d;
    long long e;
};

问题来了 sizeof(s2)答案是多少呢?
我们来逐步分析一下,首先对于sizeof(s1)如果你用之前的自然对界方法去内存补齐的话得到的结果将是8字节,然后我们再来专心看结构s2中。
首先c是char型占1个字节,其次我们由结构体s1声明了一个对象,它是8字节的,对于结构来说,它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个
所以我们找到s1中最大的是long型 4字节,所以c会被自动补全至4个字节,在c和d之间存在3个空字节,截止到d这样就已经占用了12个字节了,接着e是long long型的占8个字节,因此自然对界就应该以8字节看齐,d和e之间就需要补齐4个字节,所以最终是24个字节占用

  • 好像有点深入…我们回过头来,再继续对类中函数进行测试吧
class Base{            
	public:
		void normalprint(){}//函数不占用内存 
	private:
		int a;//4字节
};

对于上面的类sizeof(Base)结果是4,因为普通函数在类中不占用内存
有个结论就是:对象的大小是它的数据成员所占存储空间之和
那肯定有人会问那空类的大小是不是就是0了呢?显然,当类里没有任何真正的成员变量,所以大小应该是0,但0大小不好在内存中定位一个地址,所以就规定它大小为0的对象要占一字节空间,以便让它拥有一个合法的地址。

我们接着添加下派生类再试试

class Derived:public Base{
	public:
	private:
		int b; //4字节
};

sizeof(Derived)结果是8 这里是因为派生类大小=基类成员大小+派生类成员大小。(当然同样适用于内存补齐机制)

  • 那扯上虚函数呢?!我们修改一下之前的基类
class Base{            
	public:
		virtual void print(){} //存在虚函数指针 占用8字节(系统原因也有可能是4字节)
		void normalprint(){}//函数不占用内存 
	private:
		int a;
};

sizeof(Base)答案是多少呢?之前说过普通函数在类中不占用字节,但是虚函数就不一样了,类里的虚函数其实是由一个指向虚函数表的vptr指针构成的,指针的大小我这里是8字节,所以这个虚函数大小就相等于存储了一个指针 所以一共是12字节咯?!!如果你认为是12字节那就说明之前讲的内存补齐机制没认真看!!!这里结果当然是16字节,详细解释自己翻一下上面介绍。
再看看派生类的情况呢?

class Derived:public Base{
	public:
		void print(){}
		virtual void d(){}
	private:
		int b; 

};

sizeof(Derived)答案是多少呢?你会不会这样算呢?首先派生类继承自Base的16字节,而派生类中虚函数d占用8个字节,再加上b的int型占用4个字节,如果我算上内存补齐b补全至8个字节,那答案就是32个字节咯!
很遗憾,答案是错的,首先有一个错误犯得很严重,派生类的虚函数指针其实是继承自基类的,在派生类中添加虚函数,并不表示添加了虚函数指针,而是给虚函数表上增添了一个地址。
其次第一句话派生类继承自Base的16个字节,其实就是错误的,我们知道派生类大小=基类成员大小+派生类成员大小,所以根据这一公式我们可以得到所含的是基类虚函数指针(8字节)+基类a(4字节)+派生类b(4字节)=16字节,当然这里由于正好内存补齐是16字节,如果派生类b是double类型的结果就又变了,这里大家可以自行去测试一下,结果应该是24。

总结

经过这次分析,你应该对sizeof有了深入了解了吧,我们还扩充了内存补齐机制和虚函数的相关知识!学习其实就应该像这样对一个问题的各种情况进行全面剖析,并且对于产生的原因进行更深层次的去探索,这样才能真正了解原理,今天就到这了,我们下次再见!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C语言中,sizeof是一个操作符,用来获取一个数据类型或变量的字节大小。它返回的结果是一个size_t类型的值,表示字节数。引用提到了一些关于C语言中sizeof的特殊情况,针对32位机而言,下面是几个例子: 1. 如果我们定义了一个结构体struct test,其中包含了8个int类型的成员变量a、b、c、d、e、f、g和h。那么使用sizeof(test)获取到的结果是32,因为每个int类型占据4个字节,所以8个int类型的成员变量总计占据32个字节。 2. 如果我们创建了一个test类型的引用r,指向一个test类型的对象。那么使用sizeof(r)获取到的结果也是32,因为引用r所指向的对象是test类型,而sizeof一个引用得到的结果是sizeof一个被引用的对象的大小,即sizeof(test)的结果。 3. 如果我们尝试使用sizeof获取一个数组的大小,比如数组arrayA,那么编译会出错,因为在C语言中,sizeof不能获取数组的大小,而是返回数组元素类型的大小。 综上所述,在C语言中,sizeof是用来获取数据类型或变量的字节大小的操作符。它返回的结果是一个size_t类型的值,表示字节数。在一些特殊情况下,sizeof的行为可能会有所不同,比如结构体和引用的处理。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++ sizeof 实例解析](https://download.csdn.net/download/weixin_38569515/12814125)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [C/C++ | sizeof()函数](https://blog.csdn.net/weixin_47187147/article/details/123470258)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [C/C++中 sizeof 详解](https://blog.csdn.net/HellowWorld001/article/details/119303406)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值