Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1477772
  • 博文数量: 842
  • 博客积分: 12411
  • 博客等级: 上将
  • 技术积分: 5772
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-14 14:43
文章分类

全部博文(842)

文章存档

2013年(157)

2012年(685)

分类: LINUX

2012-06-06 10:59:15

Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-qformat:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; mso-bidi-font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-font-kerning:1.0pt;}

判断栈的生长方向:

不妨回想一下,函数如何调用。执行一个函数时,这个函数的相关信息都会出现栈之中,比如参数、返回地址和局部变量。当它调用另一个函数时,在它栈信息保持不变的情况下,会把它调用那个函数的信息放到栈中。

似乎发现了什么,没错,两个函数的相关信息位置是固定的,肯定是先调用的函数其信息先入栈,后调用的函数其信息后入栈。那接下来,问题的答案就浮出了水面。

比如,设计两个函数,一个作为调用方,另一个作为被调用方。被调用方以一个地址(也就是指针)作为自己的入口参数,调用方传入的地址是自己的一个局部变量的地址,然后,被调用方比较这个地址和自己的一个局部变量地址,由此确定栈的增长方向。

为什么一个函数解决不了这个问题。前面这个大概解释了函数调用的过程,我们提到,函数的相关信息会一起送入栈,这些信息就包括了参数、返回地址和局部变量 等等,在计算机的术语里,有个说法叫栈帧,指的就是这些与一次函数调用相关的东西,而在一个栈帧内的这些东西其相对顺序是由编译器决定的,所以,仅仅在一 个栈帧内做比较,都会有对编译器的依赖。就这个问题而言,参数和局部变量,甚至包括返回地址,都是相同的,因为它们在同一个栈帧内,它们之间的比较是不能 解决这个问题的,而它们就是一个函数的所有相关信息,所以,一个函数很难解决这个问题。

好了,既然有了这个了解,显然可以扩展一下前面的解决方案,可以两个栈帧内任意的东西进行比较,比如,各自的入口参数,都可以确定栈的增长方向。

思想test()中调用test_2(),因此,test()中的变量必先进栈;

void test();
void test_2();
int * t;
int * k;
int main(void)
{
    test();
    if (t         printf("stack grow up");
    }else{
        printf)"stack grow down");
    }
    return (1);
}
void test();
{
        int t_test=1;
        t=(int *)&t_test;      //
保存t_test地址到t
        test_2();       
}
void test_2()
{
        int k_test=1;    
        k=(int *)&k_test;     //
保存k_test地址 k
}

以上的方法只适合变量存在栈中的编译    

判断堆栈增长方向

alloca.cfind_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
函数使用函数递归的方法
第一次进入,由于addrNULL,所以将字符变量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);
}
  
这段代码只所以能够判断栈的增长方向,这和调用函数时栈的使用方法是分不开的,系统在调用函数时,函数内部的局部变量会入栈(入栈顺序和函数内的变量申明顺序可能不一样, 这和编译器有一定的关系,所以不能通过一个函数内的多个变量在栈中的地址来判断栈的增长方向),这里涉及两个函数调用,第一个函数func1中申明了一个变量,然后将指向其的指针传递给函数func2, 然后再在func2中申明一个变量,打印出两个变量的地址,比较地址的大小,就可知道栈的增长方向了,如果func1里的变量地址比func2里的变量地 址大,则说明栈的增长方向是由高地址向低地址增长的,反之,则说明栈的增长方向是由低地址向高地址增长的,以上程序要我的机器上运行的结果如下(程序在不 同时刻运行,会有不同的输出,所以大家运行的结果会和下面的结果不同,不过这没有关系,大家只要比较两个输出值的大小,就能进行方向判定了):
bfdbf0ac
bfdbf07c
Press [Enter] to close the terminal ...

由输出可知,func1中的变量地址大于func2中的变量地址,可知我的电脑中栈的增长方向是由高地址向低地址方向增长的。

阅读(1745) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~