VS2010 C各类型变量在内存中的存放研究
2016-11-16 18:20
155 查看
这边文章主要用于研究各种数据类型的变量,如char,int变量,数组在内存中的存储方式,同时探讨下windows系统内存存放方式。
测试方法:定义一组 char,int等数据类型变量,同时定义数组,查看内存地址。
测试代码如下:
void stack_var_mem_test(void)
{
char cVar1=0,cVar2=0;
short sVar1=0,sVar2=0;
int iVar1=0,iVar2=0;
long lVar1=0,lVar2=0;
long long llVar1=0,llVar2=0;
printf("stack_var_mem_test\n");
printf("char mem addr : %8x, %8x\n",&cVar1,&cVar2);
printf("short mem addr : %8x, %8x\n",&sVar1,&sVar2);
printf("Int mem addr : %8x, %8x\n",&iVar1,&iVar2);
printf("long mem addr : %8x, %8x\n",&lVar1,&lVar2);
printf("long long mem addr : %8x, %8x\n",&llVar1,&llVar2);
}
void stack_var_array_mem_test(void)
{
char cVar_array[10];
short sVar_array[10];
int iVar_array[10];
long lVar_array[10];
long long llVar_array[10];
printf("stack_var_array_mem_test\n");
printf("char array mem addr: %8x, %8x\n",&cVar_array[0],&cVar_array[9]);
printf("short array mem addr: %8x, %8x\n",&sVar_array[0],&sVar_array[9]);
printf("int array mem addr: %8x, %8x\n",&iVar_array[0],&iVar_array[9]);
printf("long array mem addr: %8x, %8x\n",&lVar_array[0],&lVar_array[9]);
printf("longlong array mem addr: %8x, %8x\n",&llVar_array[0],&llVar_array[9]);
}
输出结果如下:
从上表中可以看到
1.局部变量在内存中是存放在栈空间中,存放方式是从高地址往低地址走,即栈顶指针指向低地址,具有先进先出的特点。
2.数组变量按照下标,首地址存放于低地址中,尾地址存放于高地址中,存放方式是从低地址往高地址走。
3.char变量一个字节,short变量2个字节,int和long变量4个字节,longlong变量8个字节。
以上测试方法存在一个问题,未考虑到大小端模式对数据在栈空间存放的影响。
小端模式:数据的高字节存放于高地址中。
直观的表示如下:
定义
union Ta{
int a;
char b[4];
} gua;
gua.a = 0x12345678;
gua.b[0]~gua.b[4]的值则如下:
1.数组在内存中存放的方式是从低地址到高地址。
2.存放于内存中的变量(short,int..)存放方式根据系统的大小端模式而定。
3.对于多维数组而言,数据按照下标顺序从低地址到高地址顺序存放。
原则1:数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
原则2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
原则3:收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。
例1:struct{
short a1;
short a2;
short a3;
}A;
struct{
long a1;
short a2;
}B;
sizeof(A) = 6; 这个很好理解,三个short都为2。
sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。
例2:struct A{
int a;
char b;
short c;
};
struct B{
char b;
int a;
short c;
};
sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。
sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。
深究一下,为什么是这样,我们可以看看内存里的布局情况。
a b c
A的内存布局:1111, 1*, 11
b a c
B的内存布局:1***, 1111, 11**
其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。
B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。
再看一个结构中含有结构成员的例子:
例3:struct A{
int a;
double b;
float c;
};
struct B{
char e[2];
int f;
double g;
short h;
struct A i;
};
sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。
sizeof(B) = 48; 看看B的内存布局。
e f g h i
B的内存布局:11* *, 1111, 11111111, 11 * * * * * *,1111* * * *, 11111111, 1111 * * * *
考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体 系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据 的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。
最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间。
1.内存地址排列
测试目的:测试stack内存中局部变量存放时地址是由大到小还是由小到大。测试方法:定义一组 char,int等数据类型变量,同时定义数组,查看内存地址。
测试代码如下:
void stack_var_mem_test(void)
{
char cVar1=0,cVar2=0;
short sVar1=0,sVar2=0;
int iVar1=0,iVar2=0;
long lVar1=0,lVar2=0;
long long llVar1=0,llVar2=0;
printf("stack_var_mem_test\n");
printf("char mem addr : %8x, %8x\n",&cVar1,&cVar2);
printf("short mem addr : %8x, %8x\n",&sVar1,&sVar2);
printf("Int mem addr : %8x, %8x\n",&iVar1,&iVar2);
printf("long mem addr : %8x, %8x\n",&lVar1,&lVar2);
printf("long long mem addr : %8x, %8x\n",&llVar1,&llVar2);
}
void stack_var_array_mem_test(void)
{
char cVar_array[10];
short sVar_array[10];
int iVar_array[10];
long lVar_array[10];
long long llVar_array[10];
printf("stack_var_array_mem_test\n");
printf("char array mem addr: %8x, %8x\n",&cVar_array[0],&cVar_array[9]);
printf("short array mem addr: %8x, %8x\n",&sVar_array[0],&sVar_array[9]);
printf("int array mem addr: %8x, %8x\n",&iVar_array[0],&iVar_array[9]);
printf("long array mem addr: %8x, %8x\n",&lVar_array[0],&lVar_array[9]);
printf("longlong array mem addr: %8x, %8x\n",&llVar_array[0],&llVar_array[9]);
}
输出结果如下:
type | addr1 | addr2 | size-1 |
char | 40f7e7 | 40f7db | 12(C) |
short | 40f7cc | 40f7c0 | 12(C) |
int | 40f7b4 | 40f7a8 | 12(C) |
long | 40f79c | 40f790 | 12(C) |
long long | 40f780 | 40f770 | 16(10) |
array | addr0 | addr9 | |
char | 40f7d8 | 40f7e1 | 9 |
short | 40f7bc | 40f7ce | 18 |
int | 40f78c | 40f7b0 | 36 |
long | 40f75c | 40f780 | 36 |
long long | 40f704 | 40f74c | 72 |
1.局部变量在内存中是存放在栈空间中,存放方式是从高地址往低地址走,即栈顶指针指向低地址,具有先进先出的特点。
2.数组变量按照下标,首地址存放于低地址中,尾地址存放于高地址中,存放方式是从低地址往高地址走。
3.char变量一个字节,short变量2个字节,int和long变量4个字节,longlong变量8个字节。
以上测试方法存在一个问题,未考虑到大小端模式对数据在栈空间存放的影响。
2.大小端模式对各个类型变量的影响
大端模式:数据的高字节存放于低地址中。小端模式:数据的高字节存放于高地址中。
直观的表示如下:
定义
union Ta{
int a;
char b[4];
} gua;
gua.a = 0x12345678;
gua.b[0]~gua.b[4]的值则如下:
type | addr | mode | big-edion | little-edion |
b0 | d1 | 低 | 12 | 78 |
b1 | d2 | 34 | 56 | |
b2 | d3 | 56 | 34 | |
b4 | d4 | 78 | 12 |
2.存放于内存中的变量(short,int..)存放方式根据系统的大小端模式而定。
3.对于多维数组而言,数据按照下标顺序从低地址到高地址顺序存放。
3.结构体内存排列方式
结构体对齐原因有很大部分是因为计算机扫描的内存单元个数,也就是数据总线的大小。原则1:数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
原则2:结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
原则3:收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。
例1:struct{
short a1;
short a2;
short a3;
}A;
struct{
long a1;
short a2;
}B;
sizeof(A) = 6; 这个很好理解,三个short都为2。
sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。
例2:struct A{
int a;
char b;
short c;
};
struct B{
char b;
int a;
short c;
};
sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。
sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。
深究一下,为什么是这样,我们可以看看内存里的布局情况。
a b c
A的内存布局:1111, 1*, 11
b a c
B的内存布局:1***, 1111, 11**
其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。
B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。
再看一个结构中含有结构成员的例子:
例3:struct A{
int a;
double b;
float c;
};
struct B{
char e[2];
int f;
double g;
short h;
struct A i;
};
sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。
sizeof(B) = 48; 看看B的内存布局。
e f g h i
B的内存布局:11* *, 1111, 11111111, 11 * * * * * *,1111* * * *, 11111111, 1111 * * * *
考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体 系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据 的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。
最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间。
相关文章推荐
- 变量在内存中的存放位置
- 局部变量,静态局部变量,全局变量,静态全局变量在内存中的存放区别(转)
- 局部变量,静态局部变量,全局变量,静态全局变量在内存中的存放区别
- 《转载》什么变量在堆内存里存放,什么变量在栈内存里存放
- 局部变量,静态局部变量,全局变量,静态全局变量在内存中的存放区别(转)
- C++中的引用变量在内存中存放的位置
- 局部变量,静态局部变量,全局变量,静态全局变量在内存中的存放区别
- vs2008、vs2010中查看变量在内存中的内容的方法
- 什么变量在堆内存里存放,什么变量在栈内存里存放
- 什么变量在堆内存里存放,什么变量在栈内存里存放
- C语言中局部变量和全局变量在内存中的存放位置
- 变量在内存中的存放
- float 变量在内存当中的存放形式
- 局部变量,静态局部变量,全局变量,静态全局变量在内存中的存放区别(转)
- 局部变量,静态局部变量,全局变量,静态全局变量在内存中的存放区别(转)
- c中的变量在内存中存放的位置
- 局部变量,静态局部变量,全局变量,静态全局变量在内存中的存放区别
- java类中成员变量初始化后存放在堆内存中还是栈内存中?
- 局部变量,静态局部变量,全局变量,静态全局变量在内存中的存放区别
- 21:什么变量在堆内存里存放,什么变量在栈内存里存放