C/C++ sizeof函数解析——解决sizeof求结构体大小的问题
C/C++中不同数据类型所占用的内存大小
32位 64位
char 1 1
int 4 大多数4,少数8
short 2 2
long 4 8
float 4 4
double 8 8
指针 4 8
(单位都为字节)
结构体(struct):比较复杂,对齐问题。
联合(union):所有成员中最长的。
枚举(enum):根据数据类型。
sizeof计算单层结构体大小
运算符sizeof可以计算出给定类型的大小,对于32位系统来说,sizeof(char) = 1; sizeof(int) = 4。基本数据类型的大小很好计算,我们来看一下如何计算构造数据类型的大小。
C语言中的构造数据类型有三种:数组、结构体和共用体。
数组是相同类型的元素的集合,只要会计算单个元素的大小,整个数组所占空间等于基础元素大小乘上元素的个数。
结构体中的成员可以是不同的数据类型,成员按照定义时的顺序依次存储在连续的内存空间。和数组不一样的是,结构体的大小不是所有成员大小简单的相加,需要考虑到系统在存储结构体变量时的地址对齐问题。看下面这样的一个结构体:
struct stu1
{
int i;
char c;
int j;
};
用sizeof求该结构体的大小,发现值为12。int占4个字节,char占1个字节,结果应该是9个字节才对啊,为什么呢?
先介绍一个相关的概念——偏移量。偏移量指的是结构体变量中成员的地址和结构体变量地址的差。结构体大小等于最后一个成员的偏移量加上最后一个成员的大小。显然,结构体变量中第一个成员的地址就是结构体变量的首地址。因此,第一个成员i的偏移量为0。第二个成员c的偏移量是第一个成员的偏移量加上第一个成员的大小(0+4),其值为4;第三个成员j的偏移量是第二个成员的偏移量加上第二个成员的大小(4+1),其值为5。
然而,在实际中,存储变量时地址要求对齐,编译器在编译程序时会遵循两条原则:
(1)结构体变量中成员的偏移量必须是成员大小的整数倍(0被认为是任何数的整数倍)
(2)结构体大小必须是所有成员大小的整数倍,也即所有成员大小的公倍数。
上面的例子中前两个成员的偏移量都满足要求,但第三个成员的偏移量为5,并不是自身(int)大小的整数倍。编译器在处理时会在第二个成员后面补上3个空字节,使得第三个成员的偏移量变成8。结构体大小等于最后一个成员的偏移量加上其大小,上面的例子中计算出来的大小为12,满足要求。
再来看另外一个例子:
struct stu2
{
int k;
short t;
};
成员k的偏移量为0;成员t的偏移量为4,都不需要调整。但计算出来的大小为6,显然不是成员k大小的整数倍。因此,编译器会在成员t后面补上2个字节,使得结构体的大小变成8从而满足第二个要求。
由此可见,结构体类型需要考虑到字节对齐的情况,不同的顺序会影响结构体的大小。
对比下面两种定义顺序:
struct stu3
{
char c1;
int i;
char c2;
}
struct stu4
{
char c1;
char c2;
int i;
}
虽然结构体stu3和stu4中成员都一样,但sizeof(struct stu3)的值为12而sizeof(struct stu4)的值为8。
sizeof计算嵌套的结构体大小
对于嵌套的结构体,需要将其展开。对结构体求sizeof时,上述两种原则变为:
(1)展开后的结构体的第一个成员的偏移量应当是被展开的结构体中最大的成员的整数倍。
(2)结构体大小必须是所有成员大小的整数倍,这里所有成员计算的是展开后的成员,而不是将嵌套的结构体当做一个整体。
看下面的例子:
struct stu5
{
short i;
struct
{
char c;
int j;
} ss;
int k;
}
结构体stu5的成员ss.c的偏移量应该是4,而不是2。整个结构体大小应该是16。
下述代码测试原则2:
struct stu5
{
char i;
struct
{
char c;
int j;
} ss;
char a;
char b;
char d;
char e;
char f;
}
结构体ss单独计算占用空间为8,而stu5的sizeof则是20,不是8的整数倍,这说明在计算sizeof(stu5)时,将嵌套的结构体ss展开了,这样stu5中最大的成员为ss.j,占用4个字节,20为4的整数倍。如果将ss当做一个整体,结果应该是24了。
另一个特殊的例子是结构体中包含数组,其sizeof应当和处理嵌套结构体一样,将其展开,如下例子:
struct ss
{
float f;
char p;
int adf[];
};
其值为20。float占4个字节,到char p时偏移量为4,p占一个字节,到int adf[3]时偏移量为5,扩展为int的整数倍,而非int adf[3]的整数倍,这样偏移量变为8,而不是12。结果是8+12=20,是最大成员float或int的大小的整数倍。
如何给结构体变量分配空间由编译器决定,以上情况针对的是Linux下的GCC。在Windows下的VC平台也是这样,至于其他平台,可能会有不同的处理。
致谢:感谢您的耐心阅读!
C/C++ sizeof函数解析——解决sizeof求结构体大小的问题的更多相关文章
- sizeof进行结构体大小的判断
typedef struct{ int a; char b;}A_t;typedef struct{ int a; char b; char c;}B_t;typedef ...
- PHP json_decode 函数解析 json 结果为 NULL 的解决方法
在做网站 CMS 模块时,对于模块内容 content 字段,保存的是 json 格式的字符串,所以在后台进行模块内容的编辑操作 ( 取出保存的数据 ) 时,需要用到 json_decode() 函数 ...
- C语言中的sizeof函数总结
sizeof函数的结果: 变量:变量所占的字节数. ; printf( 数组:数组所占的字节数. ,,,,}; ] = {,,,,}; printf("size_arr1=%d\n" ...
- 函数strlen()和sizeof的区别
函数strlen()和sizeof的区别: #include<stdio.h> #include<stdlib.h> #include<string.h> #def ...
- 数据类型、位运算、sizeof()函数
数据精度,依次升高.(负数必须使用有符号类型) 不同精度的数据间运算,所得结果为高精度类型. 数据类型详细信息如下图: 整型数据的数制:十进制(32).八进制(032,以0开头).十六进制(0x32, ...
- 含有虚函数的类sizeof大小
#include <iostream> using namespace std; class Base1{ virtual void fun1(){} virtual void fun11 ...
- 数组名作为函数参数以及sizeof用法
来源:https://blog.csdn.net/jay_zhouxl/article/details/51745518 int f(int *p,char *a) { printf("p[ ...
- C sizeof函数
#include<stdio.h> int main() { struct stu { union { ]; ]; } cls; ]; float cj; } xc; printf(&qu ...
- PHP sizeof() 函数
实例 返回数组中元素的数目: <?php$cars=array("Volvo","BMW","Toyota");echo sizeof ...
随机推荐
- redis主从 以及认证配置
以前用redis用的很多,各种数据类型用的飞起,算是用得很溜了.不过那都是封装好的方法,自己直接调用.以前的公司比较规范,开发只是开发,很少去做跟运维相关的事情. 换了一份工作,不过这边项目刚开始起步 ...
- TreeSet集合深入了解--------攻击原理
Set接口Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false.(无序,不可重复 )Set判断两个对象相同不是使用==运算符,而是根据equals方法.也就是说 ...
- PCL -语法错误:“::” error C2589: “(”:“::”右边的非法标记
1.错误原因:系统函数与pcl中的max函数冲突导致的 2.两种解决办法: 1)错误中max和min函数用括号括起来,例如"std::Max"修改为“(std::Max)”. 2) ...
- Bootstrap左侧下拉三级菜单
在线实例 效果一 效果二 效果三 使用方法 <script src="/api/jq/BootstrapCaidan/js/metismenu.js"></scr ...
- 安装运行mariadb时错误:gtid_slave_pos
精简windows zip包后出现错误: Failed to load slave replication state from table mysql.gtid_slave_pos: 1932: T ...
- 利用IIS导出,导入快速部署 web站点
部署负载均衡站点的时候会创建多个站点拷贝.用脚本可以提高效率,并且减少错误 1 以管理员身份运行CMD 2 Cd C:\Windows\System32\inetsrv 3 导出指定的应用程序池 ap ...
- 图解Android触摸事件分发
Android中触摸事件传递过程中最重要的是dispatchTouchEvent().onInterceptTouchEvent()和onTouchEvent()方法. View和Activity有d ...
- swift学习之UI控件(一)
// // ViewController.swift // test // // Created by chuangqu on 15/7/23. // Copyright (c) 2015年 ...
- Swift开发第二篇——extension及fatalError
本篇分两部分: 一.extension在 Swift 中的使用 二.Swift 中的 fatalError 一.extension在 Swift 中的使用 在 swift 中我们可以通过 extens ...
- View的onSaveInstanceState和onRestoreInstanceState过程分析
为什么要介绍这2个方法呢?这是因为在我们的开发中最近遇到了一个很诡异的bug.大体是这样的:在我们的ViewPager中 有2页的root view都是ScrollView,我们在xml里面都用了an ...