今天碰到一个有趣的问题,在debug模式下两个int
类型的变量其地址间相差12字节,例如:
int main()
{
int i1 = 0;
int i2 = 10;
int tmp = (&i1 - &i2) * sizeof(int);
printf("&i = %p\n", &i1);
printf("&i = %p\n", &i2);
printf("整型变量 i1 和整型变量 i2 相差的字节:%d(OCT), %0X(HEX)\n\n", tmp, tmp);
return 0;
}
结果如下:
可以看到,i1
的地址为0x00CFF9AC
,i2
的地址为0x00CFF9A2
,两个变量之间的地址并不连续。既然这是debug模式下,那么release模式下是什么情况呢?
可以清楚的看到在release模式下,这两个整型变量的地址是连续的,为了进一步拓展,拓展为其他类型:
int main()
{
int i1 = 0;
int i2 = 10;
int tmp = (&i1 - &i2) * sizeof(int);
printf("&i = %p\n", &i1);
printf("&i = %p\n", &i2);
printf("整型变量 i1 和整型变量 i2 相差的字节:%d(OCT), %0X(HEX)\n\n", tmp, tmp);
char str1 = 'a';
char str2 = 'b';
tmp = (&str1 - &str2) * sizeof(char);
printf("&str1 = %p\n", &str1);
printf("&str2 = %p\n", &str2);
printf("字符变量 str1 和字符变量 str2 相差的字节:%d(OCT), %0X(HEX)\n\n", tmp, tmp);
double d1 = 'a';
double d2 = 'b';
tmp = (&d1 - &d2) * sizeof(double);
printf("&str1 = %p\n", &d1);
printf("&str2 = %p\n", &d2);
printf("整型变量 i 和整型变量 j 相差的字节:%d(OCT), %0X(HEX)\n\n", tmp, tmp);
return 0;
}
结果如下:
观察结果可以得知,int
类型的变量在地址上相差12个字节,char
类型的变量在地址上相差12个字节,double
类型的变量在地址上相差16个字节,同样的观察release模式下的结果:
结果为负先忽略负号,想知道为啥,可以了解,debug和release的入栈机制;
可以看到和预料的结果是一样的,在release模式下,变量之间是连续的。
为什么在debug下变量之间的地址不连续?
在debug模式下对这段代码进行调试:
可以看到,在内存视图中,有一大段内容为CCCCCCCC的地址,这段地址称为栈,因为i1
比i2
先定义,所以i1
入栈,可以看到i2
与i1
之间的距离为0x00D7FC80
-0x00D7FC74
= 12;继续看char
类型:
可以看到str1和str2之间的距离也是12个字节,我们知道在C语言中,char
类型为1个字节,那为什么相差的距离为12?内存对齐,char
类型由原来的1个字节被当作4字节来处理;接下来是double
类型:
可以看到d1和d2之间的距离是16个字节;
为什么每个类型之间地址的距离会有不同?
一个原因为自身类型的原因,可以看如下的图:
可以清楚的看到每个类型在内存中的布局,根据这张图可以知道,debug模式下一个类型在内存中占用的大小为(自身类型所占字节数 + 一前一后共8个字节)。
对于int
,int
类型占8个字节大小,,所以对于int
类型来说,debug模式下占用内存大小为12个字节;至于为什么两个int
类型的变量之间的距离为12个字节,自然就一目了然了;对于char
类型,前面说过,存在内存对齐,所以和int
类型一样占12个字节;
重点:变量地址中前一个和后一个地址所占的四个字节是干什么用的?根据上面的图,可以看到,每个变量的两则都有一段4字节大小的变量CCCCCCCC
,为什么需要这个,改变这个值会发生什么?
#include <stdio.h>
int main()
{
int i1 = 0;
int i2 = 10;
int* ptr = &i1;
ptr--;
*ptr = 11;
return 0;
}
上面这段代码仅仅在debug模式下会有问题。对这行代码进行运行:
可以看到,三个变量按照正确的方式入栈,其中指针ptr
指向i1
的地址,接下来我们对指针ptr
进行操作,使其指向i1
的前一个地址,也就是图中i1
的上面CCCCCCCC,使其被覆盖掉:
可以看到,原来存储CCCCCCCC的内容被覆盖了,接下来继续运行
)]
当程序结束时,程序出现异常:
Run-Time Check Failure #2 - Stack around the variable 'i1' was corrupted.
运行时检查失败#2 -围绕变量“i1”的堆栈已损坏
程序出现异常:
Run-Time Check Failure #2 - Stack around the variable 'i1' was corrupted.
运行时检查失败#2 -围绕变量“i1”的堆栈已损坏
当我们改变i1周围的地址时,程序出现了报错,所以可以猜到,变量地址前后存在程序或函数退出时栈的检查,着称为栈检查机制,通过检查变量一前一后的是否为CCCCCCCC,来判断程序是否栈溢出、修改其他变量的值。而在release下,是没有栈检查的,数据之间是紧密联系的,看似毫无影响的修改,肯会对程序的运行造成影响。同样数字的越界也是一样的道理!!!