一、最基础的介绍(下面是关于位个字节的介绍,已经了解的可以跳过)
为全面的记录,这里从计算机的存储开始讲起吧,计算机存储是二进制的,以位为单位,一个0或者1就是1位,而8个位是一个字节,若干个字节是一个字。
详细如下:
字节
字节是指一小组相邻的二进制数码。通常是8位作为一个字节。它是构成信息的一个小单位,并作为一个整体来参加操作,比字小,是构成字的单位。
在微型计算机中,通常用多少字节来表示存储器的存储容量。
例如,在C++的数据类型表示中,通常char为1个字节,int为4个字节,double为8个字节。
理解编码的关键,是要把字符的概念和字节的概念理解准确。这两个概念容易混淆,我们在此做一下区分:
概念描述 举例
字符人们使用的记号,抽象意义上的一个符号。 '1', '中', 'a', '$', '¥' ……
字节计算机中存储数据的单元,一个8位的二进制数,是一个很具体的存储空间。0x01, 0x45, 0xFA……
字
在计算机中,一串数码作为一个整体来处理或运算的,称为一个计算机字,简称字。字通常分为若干个字节(每个字节一般是8位)。在存储器中,通常每个单元存储一个字,因此每个字都是可以寻址的。字的长度用位数来表示。
在计算机的运算器、控制器中,通常都是以字为单位进行传送的。字出现在不同的地址其含义是不相同。例如,送往控制器去的字是指令,而送往运算器去的字就是一个数。
二、基本数据类型的大小(以字节为单位,下同)
这里计算大小的方法,我使用的是sizeof()函数,相信大家都知道,这里我作为学习的目的,对它也有详细的解释。
sizeof是C/C++中的一个操作符(operator),简单的说其作用就是返回一个对象或者类型所占的内存字节数。
首先我们列一下我们常见的数据类型吧
整型 int 无符号整型 unsigned int 短整型 short int 无符号短整型 unsigned short int 长整形 long int
无符号长整形 unsigned long int 字符型 char 无符号字符型 unsigned char 单精度型 float 双精度型 double
长双精度型 long double 布尔型 bool
代码:
#include<iostream>
#include<iomanip>
using namespace std;
int main()
{
cout.flags(ios::left);
cout<<setw(14)<<"整型占"<<setw(4)<<sizeof(int)<<setw(4)<<"字节"<<endl;
cout<<setw(14)<<"无符号整型"<<setw(4)<<sizeof(unsigned int)<<setw(4)<<"字节"<<endl;
cout<<setw(14)<<"短整型"<<setw(4)<<sizeof(short int)<<setw(4)<<"字节"<<endl;
cout<<setw(14)<<"无符号短整型"<<setw(4)<<sizeof(unsigned short int)<<setw(4)<<"字节"<<endl;
cout<<setw(14)<<"长整型"<<setw(4)<<sizeof(long int)<<setw(4)<<"字节"<<endl;
cout<<setw(14)<<"无符号长整型"<<setw(4)<<sizeof(unsigned long int)<<setw(4)<<"字节"<<endl;
cout<<setw(14)<<"字符型"<<setw(4)<<sizeof(char)<<setw(4)<<"字节"<<endl;
cout<<setw(14)<<"无符号字符型"<<setw(4)<<sizeof(unsigned char)<<setw(4)<<"字节"<<endl;
cout<<setw(14)<<"单精度型"<<setw(4)<<sizeof(float)<<setw(4)<<"字节"<<endl;
cout<<setw(14)<<"双精度型"<<setw(4)<<sizeof(double)<<setw(4)<<"字节"<<endl;
cout<<setw(14)<<"长双精度型"<<setw(4)<<sizeof(long double)<<setw(4)<<"字节"<<endl;
cout<<setw(14)<<"布尔型"<<setw(4)<<sizeof(bool)<<setw(4)<<"字节"<<endl;
system("pause");
return 0;
}
输出结果:
这里我发现,加了long和没加long并没有什么不同,查了教科书,表示的范围也是相同大小,很奇怪,没有用干嘛加,百度,得知结论如下:
加long和不加long
(1)在VC下没有区别。两种类型均用4个字节存放数据。
(2)VC是后出的编译器,之前有很多早期的C编译器,在早期编译器下long int占4个字节,int占2个字节。
(3)之所以有“整型”和“长整形”两种不同类型,是C语言在诞生时发明者规定好的,前者存储的整数的值域小于后者。
这个问题不用牵肠挂肚,在VC下用谁都可以
三、其他类型的内存分配(指针、结构体、数组)
3.1 指针
代码:
cout<<setw(30)<<"指向整型的指针占"<<setw(4)<<sizeof(int *)<<setw(4)<<"字节"<<endl;
cout<<setw(30)<<"指向布尔型的指针占"<<setw(4)<<sizeof(bool *)<<setw(4)<<"字节"<<endl;
cout<<setw(30)<<"指向长双精度型的指针占"<<setw(4)<<sizeof(long double *)<<setw(4)<<"字节"<<endl;
结果:
可以看到,这个和数据类型没有关系,其实也很简单,指针就是地址,只和操作系统的位数有关,有人奇怪,我的是64位操作系统为何是4字节应该是8字节,这是因为我的VS2010是win32平台的,我装起来就是这样,我也不知道为什么,如图:
3.2 数组(这里吧string看作是数组来对待)
int a[]={1,2,3};
char b[]="123";
int a2[3];
char b2[3];
string s="a";
string s2="a1";
string s3="a12";
string s4="a123456789123456789123456789123456789000000000000000000000000000000000000000000000000000000000000000";
cout<<setw(30)<<"整型数组a[]={1,2,3};"<<setw(4)<<sizeof(a)<<setw(4)<<"字节"<<endl;
cout<<setw(30)<<"整型数组int a2[3];"<<setw(4)<<sizeof(a2)<<setw(4)<<"字节"<<endl;
cout<<setw(30)<<"字符型数组char b[]=\"123\""<<setw(4)<<sizeof(b)<<setw(4)<<"字节"<<endl;
cout<<setw(30)<<"字符型数组b2[3]"<<setw(4)<<sizeof(b2)<<setw(4)<<"字节"<<endl;
cout<<setw(30)<<"字符串string s=\"a\""<<setw(4)<<sizeof(s)<<setw(4)<<"字节"<<endl;
cout<<setw(30)<<"字符串string s=\"a1\""<<setw(4)<<sizeof(s2)<<setw(4)<<"字节"<<endl;
cout<<setw(30)<<"字符串string s=\"a12\""<<setw(4)<<sizeof(s3)<<setw(4)<<"字节"<<endl;
cout<<setw(30)<<"字符串string s=\"a1234...\""<<setw(4)<<sizeof(string)<<setw(4)<<"字节"<<endl;
结果:
可以看到,一般的就是单个数据长度乘以数组个数。特别的对于
char b[]="123";
在末尾会有NULL终止符。
对于
string s="a";
string s2="a1";
string s3="a12";
string s4="a123456789123456789123456789123456789000000000000000000000000000000000000000000000000000000000000000";
无论你给string赋值多么长,它永远都是32个字节,与赋值是无关的,他的长度是看编译器的,我的VS2010是32,其他的可能就不是,可能是4、16等。
结构体
/*struct test1
{
int a;
char b;
};
struct test2
{
char b;
double a;
};
struct test3
{
char b;
test1 a;
double c;
char d;
double f;
};
*/
cout<<setw(60)<<"struct test1{int a;char b;}"<<setw(4)<<sizeof(test1)<<setw(4)<<"字节"<<endl;
cout<<setw(60)<<"struct test2{char a;double b;}"<<setw(4)<<sizeof(test2)<<setw(4)<<"字节"<<endl;
cout<<setw(60)<<"struct test3{char b;test1 b;double c;char d;double f}"<<setw(4)<<sizeof(test3)<<setw(4)<<"字节"<<endl;
结果:
结构体里面可不是单纯地单个数据类型大小相加减,这里面还要涉及到字节对齐,设置如下
#pragmapack(n)
n为字节对齐数,其取值为1、2、4、8、16,默认是8
并且结构体大小的计算满足如下准则:
1)
1)结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2)结构体每个成员相对于结构体首地址的偏移量(offset)都是该成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal padding);
3)结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
你可以根据这三个准则仔细推敲,我这里总结了一下,计算公式如下:
sizeof(结构体)=offsetof(最后一个元素)+sizeof(最后的元素)+sizeof(末尾填充的字节数)
其中offsetof(某元素)=min{n,sizeof(某元素)},n为设置的对齐字节数。
举个例子
对于
struct test3
{
char b;
test1 a;
double c;
char d;
double f;
};
首先看char b 它的偏移量是0,大小为1,当前大小如下
b |
然后test1 a,计算结构体内的结构体时,偏移量的计算是将其打散来看,即按照a.a(即int的大小)来算偏移量,所以a的大小为8,偏移量为1,但是要被(sizeof(a.a))整除,所以偏移量为4,所以在b后加三个填充字节:
b | 填充字节 | 填充字节 | 填充字节 | a.a | a.a | a.a | a.a | a.b | a.b |
a.b | a.b |
(请不要在意图的比例问题,每一个小格代表一个字节)
再看double c ,他的偏移量,12,不能被8整除,填充4个字节,偏移量变成16,结果如下
b | 填充字节 | 填充字节 | 填充字节 | a.a | a.a | a.a | a.a | a.b | a.b |
a.b | a.b | 填充字节 | 填充字节 | 填充字节 | 填充字节 | c | c | c | c |
c | c | c | c |
再看char d,偏移量为24,被1整除,结果如下:
b | 填充字节 | 填充字节 | 填充字节 | a.a | a.a | a.a | a.a | a.b | a.b |
a.b | a.b | 填充字节 | 填充字节 | 填充字节 | 填充字节 | c | c | c | c |
c | c | c | c | d |
再看double f ,偏移量为25,不能被8整除,加上7个填充字节,变成32,结果如下:
b | 填充字节 | 填充字节 | 填充字节 | a.a | a.a | a.a | a.a | a.b | a.b |
a.b | a.b | 填充字节 | 填充字节 | 填充字节 | 填充字节 | c | c | c | c |
c | c | c | c | d | 填充字节 | 填充字节 | 填充字节 | 填充字节 | 填充字节 |
填充字节 | 填充字节 | f | f | f | f | f | f | f | f |
最后得出sizeof(test3)=40;
套公式sizeof(test3)=offset(f)+sizeof(f)+sizeof(末尾的填充字节)
即32+8+0=40
相信看完这个例子,大家都会自己算结构体的大小了,要注意的是,offsetof(某元素)=min{n,sizeof(某元素)},n为设置的对齐字节数。而n是可以设置的!如果把n设置成2,那么sizeof(test1)=6,而不是8,读者自己去算。
顺便说一句
struct test4
{
};
sizeof(test4)=1;
四、总结
好了,相信看到这里,各位已经对内存分配的大小已经有了全新的认识,好累,第一次写博客,欢迎讨论!