全部博文(842)
分类: LINUX
2012-06-06 10:59:15
判断栈的生长方向:
不妨回想一下,函数如何调用。执行一个函数时,这个函数的相关信息都会出现栈之中,比如参数、返回地址和局部变量。当它调用另一个函数时,在它栈信息保持不变的情况下,会把它调用那个函数的信息放到栈中。
似乎发现了什么,没错,两个函数的相关信息位置是固定的,肯定是先调用的函数其信息先入栈,后调用的函数其信息后入栈。那接下来,问题的答案就浮出了水面。
比如,设计两个函数,一个作为调用方,另一个作为被调用方。被调用方以一个地址(也就是指针)作为自己的入口参数,调用方传入的地址是自己的一个局部变量的地址,然后,被调用方比较这个地址和自己的一个局部变量地址,由此确定栈的增长方向。
为什么一个函数解决不了这个问题。前面这个大概解释了函数调用的过程,我们提到,函数的相关信息会一起送入栈,这些信息就包括了参数、返回地址和局部变量 等等,在计算机的术语里,有个说法叫栈帧,指的就是这些与一次函数调用相关的东西,而在一个栈帧内的这些东西其相对顺序是由编译器决定的,所以,仅仅在一 个栈帧内做比较,都会有对编译器的依赖。就这个问题而言,参数和局部变量,甚至包括返回地址,都是相同的,因为它们在同一个栈帧内,它们之间的比较是不能 解决这个问题的,而它们就是一个函数的所有相关信息,所以,一个函数很难解决这个问题。
好了,既然有了这个了解,显然可以扩展一下前面的解决方案,可以两个栈帧内任意的东西进行比较,比如,各自的入口参数,都可以确定栈的增长方向。
思想test()中调用test_2(),因此,test()中的变量必先进栈;
void test();
void test_2();
int * t;
int * k;
int main(void)
{
test();
if (t
}else{
printf)"stack grow down");
}
return (1);
}
void test();
{
int t_test=1;
t=(int *)&t_test; //
test_2();
}
void test_2()
{
int k_test=1;
k=(int *)&k_test; //保存k_test地址 到k
}
以上的方法只适合变量存在栈中的编译器
判断堆栈增长方向
在alloca.c中find_stack_direction函数,用来判断堆栈的增长方向,记录下来。
#define ADDRESS_FUNCTION(arg) &(arg)
/* Define STACK_DIRECTION if you know the direction of
stack
growth for your system; otherwise it will be automatically
deduced at run-time.
STACK_DIRECTION > 0 => grows toward
higher addresses
STACK_DIRECTION < 0 => grows toward lower addresses
STACK_DIRECTION = 0 => direction of growth unknown */
#ifndef STACK_DIRECTION
#define STACK_DIRECTION 0
/* Direction unknown. */
#endif
#if STACK_DIRECTION != 0
#define STACK_DIR STACK_DIRECTION /* Known at compile-time. */
#else /* STACK_DIRECTION == 0; need run-time code. */
static int
stack_dir; /* 1 or -1 once known. */
#define STACK_DIR stack_dir
static void
find_stack_direction ()
{
static char *addr = NULL; /* Address of first `dummy', once
known. */
auto char dummy; /* To get stack
address. */
if (addr == NULL)
{
/* Initial entry. */
addr = ADDRESS_FUNCTION (dummy);
find_stack_direction
(); /* Recurse once. */
}
else
{
/* Second entry. */
if (ADDRESS_FUNCTION (dummy) > addr)
stack_dir = 1; /*
Stack grew upward. */
else
stack_dir = -1; /*
Stack grew downward. */
}
}
#endif /* STACK_DIRECTION == 0 */
find_stack_direction函数使用函数递归的方法
第一次进入,由于addr为NULL,所以将字符变量dummy的地址赋值给静态变量addr
第二次进入,由于静态变量addr已赋了值,所以进入
"Second entry."
接着,将第二次进入的dummy地址和第一次进入的dummy地址相比较
如果值为正,则堆栈向高地址增长;否则,堆栈向低地址增长
为什么要这么复杂呢,不能再一个函数里面写两个局部变量比较器地址大小来确定栈的方向?其实问题很简单,一个函数中的所有东西压入栈之后,其里面的 局部变量和参数变量的地址依赖于编译器,而,如果通过一个函数调用另外一个函数就能确保比较的地址不依赖与编译器了,其实一种做法是在一个函数中调用另外 一个函数,将调用函数中的局部变量地址传递到被调用函数中,和被调用函数中的局部变量的地址进行比较,如果大的话,则增长方向为由高地址向低地址生长,反 之则反,最好的方式是声明局部变量的时候声明为violate类型,这样才能确保编译器不去处理violate这个变量,而这篇文章中的方法非常巧妙。
大家可能都知道,i386系列机器的栈增长方向都是由高地址向低地址方向增长的,也就是说,先入栈的变量地址要高于后入栈的变量的地址,那么对于任何系列的机器如何判断其栈的增长方向呢,这里我将用一段简单的C程序来教大家如何判断自己机器上栈的增长方向,代码如下:
#include
#include
void func1();
void func2(int *a);
int main(int argc, char** argv) {
func1();
return (EXIT_SUCCESS);
}
void func1(){
int a = 0;
func2(&a);
}
void func2(int *a){
int b = 0;
printf("%x\n%x\n",a,&b);
}
bfdbf0ac
bfdbf07c
Press [Enter] to close the terminal ...
由输出可知,func1中的变量地址大于func2中的变量地址,可知我的电脑中栈的增长方向是由高地址向低地址方向增长的。