C语言的那些小秘密之堆栈
明白了上面的内容,那么我们就可以实现题目的要求了。代码如下所示:
本文引用地址:https://www.eepw.com.cn/article/270300.htm #include
using namespace std;
void print()
{
//这里进行排序,print不准传参数
unsigned int _ebp;
__asm{
mov _ebp,ebp
}
int *p=(int *)(*(int *)_ebp-4-4-4-4-8-7*4);
for(int i=0;i<7;i++)
cout<
}
int main()
{
int s=0;
int ss=0;
char *str="fdsafdsafdsafdsafdsafdsafdsa";
char fdsa='f';
char srt[8];
int arr[]={32,43,3,567,987,21,56};//数值随即
print();
return 0;
}
其中用红色标记的部分是一个重点,用汇编语句mov _ebp,ebp;来获得ebp寄存器的值,存放在_ebp中,ebp存放当前函数栈底的地址,就是说ebp可以看做一个指针,指向栈顶,因为 pushl %ebp,所以栈顶存放的数据就是上一个函数的ebp的值,即就是main函数的栈底。由此可知*(int*)_ebp意思就是取出栈顶的数据,即main函数的栈底。栈中数据的存储方式是根据声明的先后顺序来的,所以了为了能够对arr数组进行打印,我们要计算出数组首地址的存储地址,由于变量的压栈方式默认是四字节对齐,所以我们使用一句int *p=(int *)(*(int *)_ebp-4-4-4-4-8-7*4);来得到数组的首地址,有人可能很疑惑了,为什么这句 char *str="fdsafdsafdsafdsafdsafdsafdsa";声明的变量只是占了四字节呢?!这里要注意了,因为指针变量的地址在32位的计算机中占用四个字节,他的内容并不存储在栈中,而是在堆中,栈中仅仅是保存了指向堆的指针。讲到这儿基本上都豁然开朗了吧。看看下面的运行结果吧!

为了强调默认的字节对齐概念,我们再来修改下代码得到的运行结果可以上面得做一个比较。
代码如下,红色部分为修改代码。
#include
using namespace std;
void print()
{
//这里进行排序,print不准传参数
unsigned int _ebp;
__asm{
mov _ebp,ebp
}
int *p=(int *)(*(int *)_ebp-4-4-4-4-8-7*4);
for(int i=0;i<7;i++)
cout<
}
int main()
{
int s=0;
int ss=0;
char *str="fdsafdsafdsafdsafdsafdsafdsa";
char fdsa='f';
char srt[6];
int arr[]={32,43,3,567,987,21,56};//数值随即
print();
return 0;
}
运行结果为:

如果我们修改了 char srt[6];之后去把int *p=(int *)(*(int *)_ebp-4-4-4-4-8-7*4);修改为int *p=(int *)(*(int *)_ebp-4-4-4-4-6-7*4);,注意红色部分的对比,运行结果就变为了:

显然对比可知运行结果出错了。在此多次一举的给出对比无非是为了大家能够对字节的对齐方式加以重视。当然以上内容难免有错,毕竟c语言博大精深,如果有不正确的地方,请纠正。
c语言相关文章:c语言教程
评论