深入理解计算机各种类型大小(sizeof)

 
  1. // Example of the sizeof keyword
  2. size_t  i = sizeof( int );
  3. struct align_depends {
  4. char c;
  5. int i;
  6. };
  7. size_t size = sizeof(align_depends);  // The value of size depends on
  8. //  the value set with /Zp or
  9. //  #pragma pack
  10. int  array[] = { 1, 2, 3, 4, 5 };     // sizeof( array ) is 20
  11. // sizeof( array[0] ) is 4
  12. size_t  sizearr =                        // Count of items in array
  13. sizeof( array ) / sizeof( array[0] );
// Example of the sizeof keyword
size_t i = sizeof( int ); struct align_depends {
char c;
int i;
};
size_t size = sizeof(align_depends); // The value of size depends on
// the value set with /Zp or
// #pragma pack int array[] = { 1, 2, 3, 4, 5 }; // sizeof( array ) is 20
// sizeof( array[0] ) is 4
size_t sizearr = // Count of items in array
sizeof( array ) / sizeof( array[0] );

<!----> <script src="ms-its:dsmsdn.chm::/html/msdn_footer.js"></script> <!---->

1. 用法

1.1 sizeof和new、delete等一样,是关键字,不是函数或者宏。

1.2 sizeof返回内存中分配的字节数,它和操作系统的位数有关。例如在常见的32位系统中,int类型占4个字节;但是在16位系统中,int类型占2个字节。

1.3 sizeof的参数可以是类型,也可以是变量,还可以是常量。对于相同类型,以上3中形式参数的sizeof返回值相同。

  1. int a;
  2. sizeof(a); // = 4
  3. sizeof(int); // = 4
  4. sizeof(1); // = 4
int a;
sizeof(a); // = 4
sizeof(int); // = 4
sizeof(1); // = 4

1.4 C99标准规定,函数、不能确定类型的表达式以及位域(bit-field)成员不能被计算s 
izeof值,即下面这些写法都是错误的。

  1. void fn() { }
  2. sizeof(fn); // error:函数
  3. sizeof(fn()); // error:不能确定类型
  4. struct S
  5. {
  6. int a : 3;
  7. };
  8. S sa;
  9. sizeof( sa.a ); // error:位域成员
void fn() { }
sizeof(fn); // error:函数
sizeof(fn()); // error:不能确定类型
struct S
{
int a : 3;
};
S sa;
sizeof( sa.a ); // error:位域成员

1.5 sizeof在编译阶段处理。由于sizeof不能被编译成机器码,所以sizeof的参数不能被编译,而是被替换成类型。

  1. int a = -1;
  2. sizeof(a=3); // = sizeof(a) = sizeof(int) = 4
  3. cout<<a<<endl; // 输出-1。由于“=”操作符返回左操作数的类型,赋值操作没有执行。
int a = -1;
sizeof(a=3); // = sizeof(a) = sizeof(int) = 4
cout<<a<<endl; // 输出-1。由于“=”操作符返回左操作数的类型,赋值操作没有执行。

2. 在32位系统中不同类型的内存分配

2.1 基本类型

  1. sizeof(int);        // = 4
  2. sizeof(double);     // = 8
  3. sizeof(char);       // = 1
  4. sizeof(bool);       // = 1
  5. sizeof(short);      // = 2
  6. sizeof(float);      // = 4
  7. sizeof(long);       // = 4
sizeof(int);        // = 4
sizeof(double); // = 8
sizeof(char); // = 1
sizeof(bool); // = 1
sizeof(short); // = 2
sizeof(float); // = 4
sizeof(long); // = 4

2.2 指针

指针在32位系统中占4个字节。

  1. sizeof(int *);         // = 4
  2. sizeof(double *);      // = 4
  3. sizeof(char *);        // = 4
sizeof(int *);         // = 4
sizeof(double *); // = 4
sizeof(char *); // = 4

2.3 数组

2.3.1 数组的sizeof返回整个数组所占的字节数,即(数组元素个数×每个元素所占字节)。

  1. int ai[] = {1, 2};
  2. sizeof(ai);          // = 2*4 = 8
int ai[] = {1, 2};
sizeof(ai); // = 2*4 = 8

2.3.2 常量字符串与字符数组的内存分配方式相同。

  1. char ac[] = "abcd";  //注意数组末尾的字符串终结符'\0'
  2. sizeof(ac);          // = 5*1 = 5
  3. sizeof("abcd");      // = 5*1 = 5
char ac[] = "abcd";  //注意数组末尾的字符串终结符'\0'
sizeof(ac); // = 5*1 = 5
sizeof("abcd"); // = 5*1 = 5

2.3.3 数组和指针所占的字节数不同,应注意区分。

  1. int *pi = new int[10]; //这是指针
  2. sizeof(pi);            // = 4
  3. int ai[10];
  4. int *p = ai;           //这还是指针
  5. sizeof(p);             // = 4
  6. double* (*a)[3][6];    //看成(double *) (*a)[3][6],即一个3×6的二维数组,数组元素为指针,指向double类型。
  7. sizeof(a);             // = 4,a为指向上述二维数组的指针
  8. sizeof(*a);            // = sizeof(double *)*3*6 = 72,*a表示上述二维数组
  9. sizeof(**a);           // = sizeof(double *)*6 = 24,**a即*(*a),表示double*[6],是元素为double指针的一维数组。
  10. sizeof(***a);          // = sizeof(double *) = 4,表示上述一维数组中的第一个元素,元素类型为double指针。
  11. sizeof(****a);         // = sizeof(double) = 8,表示上述数组首元素指向的double类型。
int *pi = new int[10]; //这是指针
sizeof(pi); // = 4 int ai[10];
int *p = ai; //这还是指针
sizeof(p); // = 4 double* (*a)[3][6]; //看成(double *) (*a)[3][6],即一个3×6的二维数组,数组元素为指针,指向double类型。
sizeof(a); // = 4,a为指向上述二维数组的指针
sizeof(*a); // = sizeof(double *)*3*6 = 72,*a表示上述二维数组
sizeof(**a); // = sizeof(double *)*6 = 24,**a即*(*a),表示double*[6],是元素为double指针的一维数组。
sizeof(***a); // = sizeof(double *) = 4,表示上述一维数组中的第一个元素,元素类型为double指针。
sizeof(****a); // = sizeof(double) = 8,表示上述数组首元素指向的double类型。

2.3.4 函数形式参数中的数组会蜕变为指针,原因是数组参数“传址调用”,调用者只需将实参的地址传递过去。有一种情况例外,那就是参数是指向数组的指针。

  1. void acf(char p[3])     //参数类型是int[],表示指向int的指针
  2. {
  3. sizeof( p );        // = 4
  4. }
  5. void aif(int p[])       //参数类型是int[],表示指向int的指针
  6. {
  7. sizeof( p );        // = 4
  8. }
  9. void pif(int (*p)[6])   //参数类型是int (*)[6],表示指向int数组的指针
  10. {
  11. sizeof( p);         // = 4
  12. sizeof( *p );       // = sizeof(int)*6 = 24
  13. }
  14. void ppf(int *p[6])     //参数类型是int *[],表示指向int指针的指针
  15. {
  16. sizeof( p );        // = 4
  17. sizeof( *p );       // = 4
  18. }
void acf(char p[3])     //参数类型是int[],表示指向int的指针
{
sizeof( p ); // = 4
}
void aif(int p[]) //参数类型是int[],表示指向int的指针
{
sizeof( p ); // = 4
}
void pif(int (*p)[6]) //参数类型是int (*)[6],表示指向int数组的指针
{
sizeof( p); // = 4
sizeof( *p ); // = sizeof(int)*6 = 24
}
void ppf(int *p[6]) //参数类型是int *[],表示指向int指针的指针
{
sizeof( p ); // = 4
sizeof( *p ); // = 4
}

2.4. 类和结构体的内存分配。

2.4.1 空类或空结构体占一个字节。

  1. class CEmpty { };
  2. sizeof(CEmpty); // = 1
  3. struct SEmpty { };
  4. sizeof(SEmpty); // = 1
class CEmpty { };
sizeof(CEmpty); // = 1 struct SEmpty { };
sizeof(SEmpty); // = 1

2.4.2 非空类和结构体所占字节为所有成员占字节的和,但是不包括成员函数和静态成员所占的空间。

  1. class CInt : public CEmpty {
  2. int i;
  3. };
  4. sizeof(CInt); // = 4;
  5. class CFunc {
  6. void f() {}
  7. };
  8. sizeof(CFunc); // = 1;
  9. struct SInt : SEmpty {
  10. static int i;
  11. };
  12. sizeof(SInt); // = 1;
class CInt : public CEmpty {
int i;
};
sizeof(CInt); // = 4; class CFunc {
void f() {}
};
sizeof(CFunc); // = 1; struct SInt : SEmpty {
static int i;
};
sizeof(SInt); // = 1;

2.4.3 字节对齐

为了加快计算机的取数速度,编译器默认对内存进行字节对齐。对结构体(包括类)进行字节对齐的原则是:

1)结构体变量的首地址能够被其最宽基本类型成员的大小所整除;

2)结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);

3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。

  1. struct SByte1
  2. {
  3. double d;    // 偏移量0~7
  4. char j;      // 偏移量8
  5. int a;       // 偏移量12~15,由于9不能整除4,故先填充9~11
  6. };
  7. sizeof(SByte1);  // = 16
  8. struct SByte2
  9. {
  10. char j;      // 偏移量0
  11. double d;    // 偏移量8~15,由于1不能整除8,故先填充1~7
  12. int a;       // 偏移量16~19
  13. };
  14. sizeof(SByte2);  // = 24,为了凑成8的倍数,填充20~23
struct SByte1
{
double d; // 偏移量0~7
char j; // 偏移量8
int a; // 偏移量12~15,由于9不能整除4,故先填充9~11
};
sizeof(SByte1); // = 16 struct SByte2
{
char j; // 偏移量0
double d; // 偏移量8~15,由于1不能整除8,故先填充1~7
int a; // 偏移量16~19
};
sizeof(SByte2); // = 24,为了凑成8的倍数,填充20~23

另外,可以通过#pragma pack(n)来设定变量以n字节对齐方式。

  1. #pragma pack(push) //保存对齐状态
  2. #pragma pack(4)    //设定为4字节对齐
  3. class CByte
  4. {
  5. char c;        //偏移量0
  6. double d;      //偏移量4~11,由于1不能整除4,故先填充1~3
  7. int i;         //偏移量12~15
  8. };
  9. #pragma pack(pop)  //恢复对齐状态
  10. sizeof(CByte); // = 16
#pragma pack(push) //保存对齐状态
#pragma pack(4) //设定为4字节对齐
class CByte
{
char c; //偏移量0
double d; //偏移量4~11,由于1不能整除4,故先填充1~3
int i; //偏移量12~15
};
#pragma pack(pop) //恢复对齐状态
sizeof(CByte); // = 16

2.4.4 位域

有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域, 并说明每个区域的位数。

2.4.4.1 位域以比特位作为单位,其长度不能大于一个字节。一个位域必须存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。

  1. struct SBit1
  2. {
  3. char a : 3;
  4. char b : 4;
  5. char c : 5;
  6. };
  7. sizeof(SBit1); // = (3+4+1+5+3) bits = 2 bytes
struct SBit1
{ char a : 3;
char b : 4;
char c : 5;
};
sizeof(SBit1); // = (3+4+1+5+3) bits = 2 bytes

SBit1:| a × 3+ b × 4 + × | c × × 3 |

2.4.4.2 使用空域可以有意使某位域从下一单元开始,但是空域不能使用。

  1. struct SBit2
  2. {
  3. char a : 3;
  4. char   : 0;   // 空域
  5. char b : 4;
  6. char c : 5;
  7. };
  8. sizeof(SBit2); // = (3+4+1+5+3) bits = 3 bytes
struct SBit2
{
char a : 3;
char : 0; // 空域
char b : 4;
char c : 5;
};
sizeof(SBit2); // = (3+4+1+5+3) bits = 3 bytes

SBit2:| a ×3 + × 5 | × × | c × × 3 |

2.4.4.3 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式。

  1. struct SBit3
  2. {
  3. char  a : 3;
  4. short b : 4;
  5. char  c : 5;
  6. };
  7. sizeof(SBit3); // = 6 bytes,由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。
struct SBit3
{
char a : 3;
short b : 4;
char c : 5;
};
sizeof(SBit3); // = 6 bytes,由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。

SBit3(不压缩):| ×3 ×8 |b × ×4 ×8 ×5 ×3 ×8 

SBit3(压缩):| a×3 ×4 ×1 |   ×5 ×3 |

2.4.4.4 如果位域字段之间穿插着非位域字段,则不进行压缩。

  1. struct SBit4
  2. {
  3. int a : 3;
  4. int b : 4;
  5. int c;
  6. };
  7. sizeof(SBit4); // = 8 bytes
struct SBit4
{
int a : 3;
int b : 4;
int c;
};
sizeof(SBit4); // = 8 bytes

SBit4:| a×3 ×4 ×1 ×8 ×8 ×8 ×8 ×8 ×8 ×8 |

2.4.4.5 整个结构体的总大小为最宽基本类型成员大小的整数倍。

  1. struct SBit5
  2. {
  3. int a : 3;
  4. int b;
  5. int c : 5;
  6. };
  7. sizeof(SBit5); // = 12 bytes
struct SBit5
{
int a : 3;
int b;
int c : 5;
};
sizeof(SBit5); // = 12 bytes

SBit5:| a×3 ×5 ×8 ×8 ×8 ×8 ×8 ×8 ×8 ×5 ×3 ×8 ×8 #×8 |

2.5 联合

联合表示若干数据成员取其一,故以叠加方式分配内存,所占字节数为最大数据成员所占的字节数。

  1. union U
  2. {
  3. int i;
  4. char c;
  5. double d;
  6. };
  7. sizeof(U); // = Max(sizeof(i), sizeof(c), sizeof(d)) = sizeof(d) = 8

深入理解计算机各种类型大小(sizeof)的更多相关文章

  1. C语言各类型大小,结构体大小 sizeof(struct A)

    C语言类型大小总览 编译器pack指令 #pragma pack(n)——定义n字节对齐 C++固有类型的对齐取编译器对齐与自身大小中较小的一个 32位C++默认8字节对齐.gcc编译器默认4字节对齐 ...

  2. 类的大小——sizeof 的研究

    类的大小——sizeof 的研究(1) 先看一个空的类占多少空间? class Base { public: Base(); ~Base(); }; 注意到我这里显示声明了构造跟析构,但是sizeof ...

  3. C++类的大小——sizeof(class)

    第一:空类的大小 class CBase { }; 运行cout<<"sizeof(CBase)="<<sizeof(CBase)<<endl; ...

  4. C语言的类型大小

    C语言的类型大小 设计程序的时候我们一般会考虑的尽量的周全,尤其是像C这样的静态类型语言. 有一些溢出的问题就源于没有搞清楚变量的大小范围,所以我们编写的时候需要特别注意 C的整形(整数类型)大小 C ...

  5. 数组类型与sizeof与指针的引用

    以char类型为例: char a[100];     //a类型为char[100]    &a类型为 char (*)[100]    *a类型为char char *p = a;     ...

  6. 结构体,公用体,枚举类型的sizeof

    1)枚举类enum型空间计算 enum只是定义了一个常量集合,里面没有“元素”,而枚举类型是当做int来存储的,所以枚举类型的sizeof值都为4 enum color(red,pink,white, ...

  7. Linux内核设计(第一周)——从汇编语言出发理解计算机工作原理

    Linux内核设计(第一周)——从汇编语言出发理解计算机工作原理 计算机工作原理 汇编指令 C语言代码汇编分析 by苏正生 原创作品转载请注明出处 <Linux内核分析>MOOC课程htt ...

  8. Linux内核设计第一周 ——从汇编语言出发理解计算机工作原理

    Linux内核设计第一周 ——从汇编语言出发理解计算机工作原理 作者:宋宸宁(20135315) 一.实验过程 图1 编写songchenning5315.c文件 图2 将c文件汇编成32位机器语言 ...

  9. web前端对上传的文件进行类型大小判断的js自定义函数

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

随机推荐

  1. Agile.Net 组件式开发平台 - 组件开发示例

    所谓组件式开发平台,它所有的功能模块都是以组件的形式扩展的,下面我来演示一个简单的组件开发例程. Agile.Net开发管理平台项目,已经托管在开源中国码云平台(http://git.oschina. ...

  2. 动态磁盘恢复为基本磁盘--DiskGenius

    近日在老电脑中安装了Win8.1,想不到使用起来比Win7还流畅. 周末,手贱,由于C盘只有10GB,为主分区,D盘有40GB,为扩展分区,想要将C.D两个分区合二为一,在Win8.1的磁盘管理器中, ...

  3. SQLite学习心得

    SQLite是一款很有名气的小型开源跨平台数据库,作为目前最流行的开源嵌入式关系型数据库,在系统结构设计中正在扮演着越来越重要的角色. 本文主要沿着 http://www.cppblog.com/we ...

  4. 分类算法之贝叶斯(Bayes)分类器

    摘要:旁听了清华大学王建勇老师的 数据挖掘:理论与算法 的课,讲的还是挺细的,好记性不如烂笔头,在此记录自己的学习内容,方便以后复习.   一:贝叶斯分类器简介 1)贝叶斯分类器是一种基于统计的分类器 ...

  5. Linux C SMTP POP3 极简陋邮件客户端

    以前以为协议非常高神,但做了这个之后发现还好,没想象的那么艰难. 先要了解邮件的原理 再者就是上面2协议 可以先用telnet测试一下,以初步了解那2协议:http://hi.baidu.com/34 ...

  6. HTML5跨文档消息传递

    HTML5定义了一些javascript API,其中有一个就是跨文档消息传递(cross-document-messaging简称XDM). 现在XDM已经作为一个规范独立了出来,名字为:Web M ...

  7. qml自定义标题栏

    要实现自定义的标题栏只需在原来的窗口的基础上创建一个Rectangle并将其定位在窗口顶部即可,实现代码如下: ApplicationWindow { id: mainWindow visible: ...

  8. 《编写高质量代码-Web前端开发修改之道》笔记--第一章 从网站重构说起

    本章内容: 糟糕的页面实现,头疼的维护工作 Web标准--结构.样式和行为的分离 前端的现状 打造高品质的前端代码,提高代码的可维护性--精简.重用.有序 糟糕的页面实现,头疼的维护工作 工作中最大的 ...

  9. ASP.NET文件上传的三种基本方法

    ASP.NET依托.net framework类库,封装了大量的功能,使得上传文件非常简单,主要有以下三种基本方法. 方法一:用Web控件FileUpload,上传到网站根目录. <form i ...

  10. 斐波那契数 c 语言实现

    斐波那契数列,又称黄金数列,指的是这样一个数列:1.1.2.3.5.8.13.21.……在数学上,斐波纳契数列以如下被以递归的方法定义:F(1)=1,F(2)=1,F(n)=F(n-1)+F(n-2) ...