您的位置:首页 > 编程语言 > C语言/C++

从sizeof剖析C++中的内存分配

2011-10-17 09:43 260 查看
sizeof Operator

sizeof expression

The sizeof keyword gives the amount of storage, in bytes, associated with a variable or a type (including aggregate types). This keyword returns a value of type size_t .

The expression is either an identifier or a type-cast expression (a type specifier enclosed in parentheses).

When applied to a structure type or variable, sizeof returns the actual size, which may include padding bytes inserted for alignment. When applied to a statically dimensioned array, sizeof returns the size of the entire array. The sizeof operator cannot return
the size of dynamically allocated arrays or external arrays.

For related information, see Operators .

Cpp代码

// 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] );

// 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返回值相同。

Cpp代码

int a;

sizeof(a); // = 4

sizeof(int); // = 4

sizeof(1); // = 4

int a;

sizeof(a); // = 4

sizeof(int); // = 4

sizeof(1); // = 4

1.4 C99标准规定,函数、不能确定类型的表达式以及位域(bit-field)成员不能被计算s

izeof值,即下面这些写法都是错误的。

Cpp代码

void fn() { }

sizeof(fn); // error:函数

sizeof(fn()); // error:不能确定类型

struct S

{

int a : 3;

};

S sa;

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的参数不能被编译,而是被替换成类型。

Cpp代码

int a = -1;

sizeof(a=3); // = sizeof(a) = sizeof(int) = 4

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

int a = -1;

sizeof(a=3); // = sizeof(a) = sizeof(int) = 4

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

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

2.1 基本类型

Cpp代码

sizeof(int); // = 4

sizeof(double); // = 8

sizeof(char); // = 1

sizeof(bool); // = 1

sizeof(short); // = 2

sizeof(float); // = 4

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个字节。

Cpp代码

sizeof(int *); // = 4

sizeof(double *); // = 4

sizeof(char *); // = 4

sizeof(int *); // = 4

sizeof(double *); // = 4

sizeof(char *); // = 4

2.3 数组

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

Cpp代码

int ai[] = {1, 2};

sizeof(ai); // = 2*4 = 8

int ai[] = {1, 2};

sizeof(ai); // = 2*4 = 8

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

Cpp代码

char ac[] = "abcd"; //注意数组末尾的字符串终结符'\0'

sizeof(ac); // = 5*1 = 5

sizeof("abcd"); // = 5*1 = 5

char ac[] = "abcd"; //注意数组末尾的字符串终结符'\0'

sizeof(ac); // = 5*1 = 5

sizeof("abcd"); // = 5*1 = 5

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

Cpp代码

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类型。

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

Cpp代码

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

}

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 空类或空结构体占一个字节。

Cpp代码

class CEmpty { };

sizeof(CEmpty); // = 1

struct SEmpty { };

sizeof(SEmpty); // = 1

class CEmpty { };

sizeof(CEmpty); // = 1

struct SEmpty { };

sizeof(SEmpty); // = 1

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

Cpp代码

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;

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)。

Cpp代码

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

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字节对齐方式。

Cpp代码

#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

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

Cpp代码

struct SBit1

{

char a : 3;

char b : 4;

char c : 5;

};

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 bytesSBit1:| a × 3+ b × 4 + # × 1 | c × 5 + # × 3 |

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

Cpp代码

struct SBit2

{

char a : 3;

char : 0; // 空域

char b : 4;

char c : 5;

};

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 bytesSBit2:| a ×3 + # × 5 | b × 4 + # × 4 | c × 5 + # × 3 |

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

Cpp代码

struct SBit3

{

char a : 3;

short b : 4;

char c : 5;

};

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(不压缩):| a ×3 | # ×8 |b × 4 + # ×4 | # ×8 | c ×5 + # ×3 | # ×8 |

SBit3(压缩):| a×3 + b ×4 + # ×1 | c ×5 + # ×3 |

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

Cpp代码

struct SBit4

{

int a : 3;

int b : 4;

int c;

};

sizeof(SBit4); // = 8 bytes

struct SBit4

{

int a : 3;

int b : 4;

int c;

};

sizeof(SBit4); // = 8 bytes

SBit4:| a×3 + b ×4 + # ×1 | # ×8 | # ×8 | # ×8 | c ×8 | c ×8 | c ×8 | c ×8 |

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

Cpp代码

struct SBit5

{

int a : 3;

int b;

int c : 5;

};

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 | b ×8 | b ×8 | b ×8 | b ×8 | c ×5 + # ×3 | # ×8 | # ×8 | # ×8 |

2.5 联合

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

Cpp代码

union U

{

int i;

char c;

double d;

};

sizeof(U); // = Max(sizeof(i), sizeof(c), sizeof(d)) = sizeof(d) = 8

转自http://www.iteye.com/topic/194299
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: