变量的入栈机制

今天碰到一个有趣的问题,在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;
}

结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3nfw7YeH-1573740488899)(D:\DOS\CPP\c++初入\C++程序设计\image\TIM截图20191107002405.jpg)]

可以看到,i1 的地址为0x00CFF9AC ,i2 的地址为0x00CFF9A2 ,两个变量之间的地址并不连续。既然这是debug模式下,那么release模式下是什么情况呢?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VIvX6WDE-1573740488900)(D:\DOS\CPP\c++初入\C++程序设计\image\TIM截图20191107002736.jpg)]

可以清楚的看到在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;
}

结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wpaFXetK-1573740488901)(D:\DOS\CPP\c++初入\C++程序设计\image\TIM截图20191107003054.jpg)]

观察结果可以得知,int类型的变量在地址上相差12个字节,char 类型的变量在地址上相差12个字节,double类型的变量在地址上相差16个字节,同样的观察release模式下的结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yQOTZa8N-1573740488901)(D:\DOS\CPP\c++初入\C++程序设计\image\TIM截图20191107003419.jpg)]

结果为负先忽略负号,想知道为啥,可以了解,debug和release的入栈机制;

可以看到和预料的结果是一样的,在release模式下,变量之间是连续的。

为什么在debug下变量之间的地址不连续?

在debug模式下对这段代码进行调试:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NRjklilF-1573740488901)(D:\DOS\CPP\c++初入\C++程序设计\image\TIM截图20191107004403.jpg)]

可以看到,在内存视图中,有一大段内容为CCCCCCCC的地址,这段地址称为栈,因为i1i2先定义,所以i1入栈,可以看到i2i1之间的距离为0x00D7FC80-0x00D7FC74 = 12;继续看char类型:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2b28mGXi-1573740488902)(D:\DOS\CPP\c++初入\C++程序设计\image\TIM截图20191107004922.jpg)]

可以看到str1和str2之间的距离也是12个字节,我们知道在C语言中,char 类型为1个字节,那为什么相差的距离为12?内存对齐char类型由原来的1个字节被当作4字节来处理;接下来是double类型:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7ZD9yCA6-1573740488903)(D:\DOS\CPP\c++初入\C++程序设计\image\TIM截图20191107005358.jpg)]

可以看到d1和d2之间的距离是16个字节;

为什么每个类型之间地址的距离会有不同?

一个原因为自身类型的原因,可以看如下的图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vbK9X1Hr-1573740488903)(D:\DOS\CPP\c++初入\C++程序设计\image\TIM截图20191107010443.jpg)]

可以清楚的看到每个类型在内存中的布局,根据这张图可以知道,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模式下会有问题。对这行代码进行运行:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-txuSLYJl-1573740488903)(D:\DOS\CPP\c++初入\C++程序设计\image\TIM截图20191107012211.jpg)]

可以看到,三个变量按照正确的方式入栈,其中指针ptr指向i1的地址,接下来我们对指针ptr进行操作,使其指向i1的前一个地址,也就是图中i1的上面CCCCCCCC,使其被覆盖掉:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yJdoISZE-1573740488904)(D:\DOS\CPP\c++初入\C++程序设计\image\TIM截图20191107012543.jpg)]

可以看到,原来存储CCCCCCCC的内容被覆盖了,接下来继续运行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZYVxnVN6-1573740488905)(C:\Users\大白菜\AppData\Roaming\Typora\typora-user-images\1573061213434.png)]

当程序结束时,程序出现异常:

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下,是没有栈检查的,数据之间是紧密联系的,看似毫无影响的修改,肯会对程序的运行造成影响。同样数字的越界也是一样的道理!!!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值