sizeof与strlen的区别和#pragma pack的具体含义(非常详细)
char str[20]="0123456789";
int a=strlen(str); // a=10;strlen 计算字符串的长度,以'\0'为字符串结束标记,长度不包括'\0'。
int b=sizeof(str); // b=20;sizeof 计算的则是分配的数组str[20] 所占的内存空间的大小,不受里面存储的内容影响。
#pragma pack的具体含义
用途: 设定变量以n字节对齐方式(n的合法的数值分别是1、2、4、8、16)。
规则: 在内存中,编译器按照成员列表顺序分别为每个结构体变量成员分配内存,当存储过程中需要满足边界对齐的要求时,编译器会在成员之间留下额外的内存空间。如果想确认结构体占多少存储空间,则使用关键字sizeof,如果想得知结构体的某个特定成员在结构体的位置,则使用offsetof宏(定义于stddef.h)。
1. 结构(struct),联合(union),或者类(class)的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度中,比较小的那个进行。也就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。
2.结构整体的对齐,则按照结构体中最大的数据成员 和 #pragma pack指定值之间,较小的那个进行。
举例如下:
例1:
#pragma pack(4)
class TestB
{
public:
int aa; //第一个成员,放在[0,3]偏移的位置,
char a; //第二个成员,自身长为1,#pragma pack(4),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。
short b; //第三个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[6,7]的位置。
char c; //第四个,自身长为1,放在[8]的位置。
};
这个类实际占据的内存空间是9字节
类之间的对齐,是按照类内部最大的成员的长度,和#pragma pack规定的值之中较小的一个对齐的。
所以这个例子中,类之间对齐的长度是min(sizeof(int),4),也就是4。
9字节按照4字节对齐圆整的结果是12,所以sizeof(TestB)是12。
如果:
#pragma pack(2)
class TestB
{
public:
int aa; //第一个成员,放在[0,3]偏移的位置,
char a; //第二个成员,自身长为1,#pragma pack(2),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。
short b; //第三个成员,自身长2,#pragma pack(2),取2,按2字节对齐,所以放在偏移[6,7]的位置。
char c; //第四个,自身长为1,放在[8]的位置。
};
//可以看出,这个类实际占据的内存空间也是9字节 ,只是类之间改为按2字节对齐,9字节按2圆整的结果是10。
//所以 sizeof(TestB)是10。
如果:
#pragma pack(1)
class TestB
{
public:
int aa; //第一个成员,放在[0,3]偏移的位置,
char a; //第二个成员,自身长为1,#pragma pack(1),取小值,也就是1,所以这个成员按一字节对齐,放在偏移[4]的位置。
short b; //第三个成员,自身长2,#pragma pack(1),取1,按1字节对齐,所以放在偏移[5,6]的位置。
char c; //第四个,自身长为1,放在[7]的位置。
};
//可以看出,这个类实际占据的内存空间也是8字节 ,只是类之间改为按1字节对齐,结果是8。
//所以 sizeof(TestB)是8。
最后看原贴:
现在去掉第一个成员变量为如下代码:
#pragma pack(4)
class TestC
{
public:
char a;//第一个成员,放在[0]偏移的位置,
short b;//第二个成员,自身长2,#pragma pack(4),取2,按2字节对齐,所以放在偏移[2,3]的位置。
char c;//第三个,自身长为1,放在[4]的位置。
};
//整个类的内存空间大小是5字节,按照min(sizeof(short),4)字节对齐,也就是2字节对齐,结果是6
//所以sizeof(TestC)是6。
例2:
C编译器的缺省字节对齐方式(自然对界)
在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。
在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储(成员之间可能有插入的空字节),第一个成员的地址和整个结构的地址相同。
C编译器缺省的结构成员自然对界条件为“N字节对齐”,N即该成员数据类型的长度。如int型成员的自然对界条件为4字节对齐,而double类型的结构成员的自然对界条件为8字节对齐。若该成员的起始偏移不位于该成员的“默认自然对界条件”上,则在前一个节面后面添加适当个数的空字节。
C编译器缺省的结构整体的自然对界条件为:该结构所有成员中要求的最大自然对界条件。若结构体各成员长度之和不为“结构整体自然对界条件的整数倍,则在最后一个成员后填充空字节。
和变量定义先后顺序有关:
struct
{
char a;
int b;
short c;
char d;
}Test;
//sizeof(Test )为12
但
struct
{
int b;
short c;
char d;
char a;
}Test;
//sizeof(Test )为8
例子1(分析结构各成员的默认字节对界条界条件和结构整体的默认字节对界条件):
struct Test
{
char x1; // 成员x1为char型(其起始地址必须1字节对界),其偏移地址为0
char x2; // 成员x2为char型(其起始地址必须1字节对界,其偏移地址为1
float x3; // 成员x3为float型(其起始地址必须4字节对界),编译器在x2和x3之间填充了两个空字节,其偏移地址为4
char x4; // 成员x4为char型(其起始地址必须1字节对界),其偏移地址为8
};
因为Test结构体中,最大的成员为float x3,因些此结构体的自然对界条件为4字节对齐。则结构体长度就为12字节,内存布局为1100 1111 1000。
例子2:
#include <stdio.h>
//#pragma pack(2)
typedef struct
{
int aa1; //4个字节对齐 1111
char bb1;//1个字节对齐 1
short cc1;//2个字节对齐 011
char dd1; //1个字节对齐 1
} testlength1;
int length1 = sizeof(testlength1); //4个字节对齐,占用字节1111 1011 1000,length = 12
typedef struct
{
char bb2;//1个字节对齐 1
int aa2; //4个字节对齐 0001111
short cc2;//2个字节对齐 11
char dd2; //1个字节对齐 1
} testlength2;
int length2 = sizeof(testlength2); //4个字节对齐,占用字节1000 1111 1110,length = 12
typedef struct
{
char bb3; //1个字节对齐 1
char dd3; //1个字节对齐 1
int aa3; //4个字节对齐 001111
short cc23//2个字节对齐 11
} testlength3;
int length3 = sizeof(testlength3); //4个字节对齐,占用字节1100 1111 1100,length = 12
typedef struct
{
char bb4; //1个字节对齐 1
char dd4; //1个字节对齐 1
short cc4;//2个字节对齐 11
int aa4; //4个字节对齐 1111
} testlength4;
int length4 = sizeof(testlength4); //4个字节对齐,占用字节1111 1111,length = 8
改变缺省的对界条件(指定对界)
· 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
· 使用伪指令#pragma pack (),取消自定义字节对齐方式,通常缺省值是8。
注:VS等编译器#pragma pack ()缺省是8,gcc缺省是4,并且gcc只支持1,2,4对齐。
例3:
需要注意一点,当结构体中包含一个子结构体时,结构体中的子结构成员按照#pragma pack指定的数值和子结构最大数据成员长度中,比较小的那个进行进行对齐。例子如下:
#pragma pack(8)
struct s1{
short a;
long b; //假设long为4字节
};
struct s2{
char c;
s1 d;
long long e;
};
sizeof(s2)的结果为24。S1的内存布局为1100 1111,S2的内存布局为1000 1100 1111 0000 1111 1111。
另外,还有如下的一种方式:
· __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。
· __attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。
例4:
#include <iostream>
using namespace std;
//1、空类
class empty_class
{
};
class empty_class1
{
public:
empty_class1();
~empty_class1();
};
//2、虚函数
class virtual_class
{
public:
virtual_class();
virtual ~virtual_class();
};
class example1_class
{
private:
int a;
};
class example2_class
{
public:
example2_class();
virtual ~example2_class(); //若去掉virtual关键字,则sizeof(example2_class)结果为1
int count(int num) { a = num; } //普通成员函数,为各实例公有,不归入sizeof统计
private:
char a;
};
class example3_class
{
public:
example3_class();
virtual ~example3_class();
virtual int count(int num) { a = num; } //虚函数不记入sizeof统计
private:
int a;
};
class example4_class
{
public:
virtual int count(int num) {} //虚函数记入sizeof统计
private:
int a;
};
class example5_class
{
private:
float a;
//double b;
//int c;
public:
void fun(void);//不记入sizeof统计
};
class example6_class
{
void func_1() {}
void func_2() {}
virtual void func_3() {}
virtual void func_4() {}
};
//3、继承
class inherit_class :public example2_class
{
public:
inherit_class();
~inherit_class();
private:
char ch;
char *pch;
const float f;
static double d; //不计入sizeof计算
};
class A
{
private:
int a;
int b;
virtual void func_4() {}
};
class B :private A
{
};
int main()
{
cout << "sizeof(B) = " << sizeof(B) << endl;
cout << "sizeof(empty_class) = " << sizeof(empty_class) << endl;
cout << "sizeof(empty_class1) = " << sizeof(empty_class1) << " with constructer and deconstructer" << endl;
cout << "sizeof(virtual_class) = " << sizeof(virtual_class) << " with virtual" << endl;
cout << "sizeof(example1_class) = " << sizeof(example1_class) << endl;
cout << "sizeof(example2_class) = " << sizeof(example2_class) << endl;
cout << "sizeof(example3_class) = " << sizeof(example3_class) << endl;
cout << "sizeof(example4_class) = " << sizeof(example4_class) << endl;
cout << "sizeof(example5_class) = " << sizeof(example5_class) << endl;
cout << "sizeof(example6_class) = " << sizeof(example6_class) << endl;
cout << "sizeof(inherit_class) = " << sizeof(inherit_class) << " with inherit" << endl;
return 0;
}
输出:
VS2015:
g++:
例5:
#include <iostream>
using namespace std;
#pragma pack(2)
class BU
{
int number;
union UBffer
{
char buffer[13];
int number;
}ubuf;
void foo() {}
typedef char*(*f)(void*); // 声明f为指向函数的指针类型,该函数返回char*,类型不占内存
enum { hdd, ssd, blueray }disk; // 枚举变量的大小,实质是常数所占内存空间的大小,等于sizeof(int)
}bu;
int main()
{
cout << "sizeof(bu) = " << sizeof(bu) << endl;
return 0;
}
输出:
sizeof(bu) = 22
分析:一个Class对象占用内存空间为非静态成员变量总和+ 数据对齐处理+虚函数。int=4,union=13+1=14,enum=4(内存中当成int来存储),4+14+4=22,f只是一个定义,不是实际上的指针,没有占字节。另外 union的大小取决于它所有的成员中,占用空间最大的一个成员的大小,并且需要内存对齐,这里因为#pragma pack(2),所以union的大小为14,如果不写#pragma pack(2),那么union大小为16【因为与sizeof(int)=4对齐】。
例6:
#include <iostream>
#include <vector>
using namespace std;
class A
{
public:
int a;
int *p = (int*)malloc(200); //p在栈区分配了4字节,指向堆区分配的200字节
virtual ~A()
{
cout <<"A::~A" <<endl;
}
};
int main()
{
cout << sizeof(A) << endl;
return 0;
}
VS2015输出:
12