C 堆栈是向上增长还是向下增长?

C 堆栈是向上增长还是向下增长?,c,memory,stack,C,Memory,Stack,我有一段c语言的代码: int q = 10; int s = 5; int a[3]; printf("Address of a: %d\n", (int)a); printf("Address of a[1]: %d\n", (int)&a[1]); printf("Address of a[2]: %d\n", (int)&a[2]); printf("Address of q: %d\n", (int)&q); printf("Address of

我有一段c语言的代码:

int q = 10;
int s = 5;
int a[3];

printf("Address of a: %d\n",    (int)a);
printf("Address of a[1]: %d\n", (int)&a[1]);
printf("Address of a[2]: %d\n", (int)&a[2]);
printf("Address of q: %d\n",    (int)&q);
printf("Address of s: %d\n",    (int)&s);
输出为:

Address of a: 2293584
Address of a[1]: 2293588
Address of a[2]: 2293592
Address of q: 2293612
Address of s: 2293608
因此,我看到从
a
a[2]
,内存地址每增加4个字节。 但是从
q
s
,内存地址减少了4个字节

我想知道两件事:

  • 堆栈是向上还是向下?(在这种情况下,我觉得两者都一样)
  • a[2]
    q
    内存地址之间发生了什么?为什么会有很大的内存差异?(20字节)

  • 注意:这不是家庭作业问题。我对stack的工作原理很好奇。谢谢你的帮助。

    我认为事情不是这样的。数组似乎在“增长”,因为应该连续分配内存。然而,由于q和s之间根本不相关,编译器只是将它们分别粘贴在堆栈中的任意可用内存位置,可能是最适合整数大小的位置

    a[2]和q之间发生的事情是,q位置周围的空间不够大(即不大于12字节),无法分配一个3整数数组。

    在x86上,堆栈帧的内存“分配”只是从堆栈指针中减去必要的字节数(我相信其他体系结构也类似)。从这个意义上说,我猜堆栈会“向下”生长,因为地址会随着堆栈调用的深入而逐渐变小(但我总是设想内存从左上角的0开始,随着向右移动和向下移动,地址会变大,所以在我的脑海中,堆栈会逐渐长大……)。声明变量的顺序可能与它们的地址没有任何关系——我相信标准允许编译器对它们进行重新排序,只要它不会引起副作用(如果我错了,请有人纠正我)。当从堆栈指针中减去字节数时,它们被卡在已用地址的某个空白处


    阵列周围的间隙可能是某种填充,但对我来说很神秘。

    标准中根本没有规定如何在堆栈上组织事物。事实上,您可以构建一个完全不在堆栈上的相邻元素上存储数组元素的一致性编译器,前提是它仍然能够正确地执行数组元素算术(例如,它知道a离a[0]有1K的距离,并且可以对此进行调整)

    您可能会得到不同结果的原因是,虽然堆栈可能会向下扩展以向其中添加“对象”,但数组是单个“对象”,并且它可能具有相反顺序的升序数组元素。但依赖这种行为是不安全的,因为方向可以改变,变量可以因各种原因交换,包括但不限于:

    • 优化
    • 对齐
    • 编译器的堆栈管理部分中人员的突发奇想
    请参阅我关于堆栈方向的优秀论文:-)

    在回答您的具体问题时:

  • 堆叠是向上还是向下?
    这一点都不重要(就标准而言),但是,既然您提出了要求,它可以在内存中增长或下降,具体取决于实现
  • a[2]和q内存地址之间会发生什么情况?为什么会有很大的内存差异?(20字节)?
    这一点都不重要(就标准而言)。可能的原因见上文

  • 堆栈增长的方向是特定于体系结构的。这就是说,我的理解是,只有极少数硬件体系结构具有成长的堆栈


    堆栈的增长方向和单个对象的布局无关。因此,虽然堆栈可能会增长,但数组不会(即&array[n]将始终<&array[n+1])

    这实际上是两个问题。一个是关于分配新框架的方式,另一个是关于变量在特定函数框架中的布局

    两者都不是C标准规定的,但答案有点不同:


    • 分配新帧时堆栈以哪种方式增长?如果函数f()调用函数g(),则
      f
      的帧指针将大于或小于
      g
      的帧指针?这可以是任意一种方式——这取决于特定的编译器和体系结构(查找“调用约定”),但是在给定的平台中总是一致的(除了一些奇怪的例外,请参见注释)。向下比较常见;x86、PowerPC、MIPS、SPARC、EE和Cell SPU就是这样
    • 函数的局部变量在堆栈框架内是如何布局的?这是不确定的,完全不可预测;编译器可以自由安排其局部变量,但它希望得到最有效的结果

    我的堆栈似乎向编号较低的地址延伸

    如果我使用不同的编译器调用,在另一台计算机上可能会有所不同,甚至在我自己的计算机上也可能会有所不同。。。或者编译器muigt选择根本不使用堆栈(内联所有内容(函数和变量,如果我没有获取它们的地址))

    $cat stack.c
    #包括
    整数堆栈(整数x){
    printf(“级别%d:x处于%p\n”,x,(void*)&x);
    如果(x==0)返回0;
    返回栈(x-1);
    }
    内部主(空){
    堆栈(4);
    返回0;
    }
    
    $/usr/bin/gcc-Wall-Wextra-std=c89-pedantic stack.c 美元/年 级别4:x位于0x7FFF778119C 3级:x处于0x7fff778118ec 二级:x为0x7fff778118cc 1级:x为0x7FFF77818AC 级别0:x位于0x7FFF778118C
    编译器可以在本地堆栈框架上的任何位置自由分配本地(自动)变量,因此不能完全从中可靠地推断堆栈的增长方向。您可以通过比较嵌套堆栈帧的地址来推断堆栈增长方向,即将函数堆栈帧内的局部变量的地址与i进行比较
    $ cat stack.c
    #include <stdio.h>
    
    int stack(int x) {
      printf("level %d: x is at %p\n", x, (void*)&x);
      if (x == 0) return 0;
      return stack(x - 1);
    }
    
    int main(void) {
      stack(4);
      return 0;
    }
    
    $ /usr/bin/gcc -Wall -Wextra -std=c89 -pedantic stack.c $ ./a.out level 4: x is at 0x7fff7781190c level 3: x is at 0x7fff778118ec level 2: x is at 0x7fff778118cc level 1: x is at 0x7fff778118ac level 0: x is at 0x7fff7781188c
    #include <stdio.h>
    int f(int *x)
    {
      int a;
      return x == NULL ? f(&a) : &a - x;
    }
    
    int main(void)
    {
      printf("stack grows %s!\n", f(NULL) < 0 ? "down" : "up");
      return 0;
    }
    
    direction of     |                                 |
      growth of      +---------------------------------+ 
       stack         | Parameters passed by fn1(caller)|
    from higher addr.|                                 |
    to lower addr.   | Direction of growth is opposite |
          |          |   to direction of stack growth  |
          |          +---------------------------------+ <-- SP on entry to fn2
          |          | Return address from fn2(callee) | 
          V          +---------------------------------+ 
                     | Callee saved registers being    | 
                     |   used in the callee function   | 
                     +---------------------------------+
                     | Local variables of fn2          |
                     |(Direction of growth of frame is |
                     | same as direction of growth of  |
                     |            stack)               |
                     +---------------------------------+ 
                     | Arguments to functions called   |
                     | by fn2                          |
                     +---------------------------------+ <- Current SP after stack 
                                                            frame is allocated
    
    // C program to check whether stack grows 
    // downward or upward. 
    #include<stdio.h> 
    
    void fun(int *main_local_addr) 
    { 
        int fun_local; 
        if (main_local_addr < &fun_local) 
            printf("Stack grows upward\n"); 
        else
            printf("Stack grows downward\n"); 
    } 
    
    int main() 
    { 
        // fun's local variable 
        int main_local; 
    
        fun(&main_local); 
        return 0; 
    }