user-0 提问: 栈空间的生长方式为从高地址到低地址 但是在栈空间申请一个数组 地址生长方式为递增  我也知道数组名是数组地址 +1为下一个元素

回复:
user: user-1
content: C，C++堆栈有这么几种用途，很多其它语言也这么用 1）为局部变量，包括数组，分配内存，以及初始化他们（如果需要） 2）某些临时数据暂存在堆栈，  3）由于寄存器数量有限，一些中间结果要及时压栈处理 4）函数调用时，存放将来函数返回时候，要执行的下一条指令的地址。 以及函数返回的退栈处理。包括。函数入口 ,函数出口的ebp,esp调整。 这个其实本来是自动执行的，不过C,C++会利用这个机制做些安排。  5）当用堆栈传递参数的时候（参数可能用寄存器传递，不一定都是堆栈传递），        复制参数到堆栈中（就是初始化函数的形式参数） 6）异常处理中的堆栈操作 7）longjmp 函数的堆栈操作  8）其他用途，和C,C++这种具体的编程语言无关， 中断时候的状态寄存器自动入栈处理， 中断位置的自动记录处理，以及中断返回的自动处理。 以及操作系统的堆栈切换处理 例如，线程上下文切换等等。

user: user-1
content: 函数调用通常会有数据压栈 但不是总是如此 最重要的是，数组的内容，通常不会压栈，而是仅仅传递一个指针 struct 倒是会压栈的，struct 内部的数组，也会压栈的 不过所谓压栈其实也就是数据复制，可能用 压栈指令， 也可能只是直接的复制，例如使用memcpy 函数或者说，rep movs 指令 像 float,double 入栈， 则是把数据，装载到浮点寄存器中，然后写入堆栈 一般整型数据，采用压栈指令 其它数据跟类型有关， 总之，就是在堆栈中，分配个位置给所有非寄存器参数，然后一一装入堆栈中。 一些参数用寄存器传递，此时跟堆栈无关 寄存器传递的参数，没啥好说的，按照位置，以及某种约定，直接传入寄存器中

user: user-2
content: 引用 16 楼 lm_whales 的回复:[quote=引用 15 楼 u010956619 的回复:] 一年多过去了，今天我搜到了这个帖子。正好我看到的一本书里面是这样说的： 默认的函数内部变量的压栈操作为：从上到下、从左到右，采用4字节对齐。数组的压栈方法略有不同，即从最后一个元素开始，直到起始元素为止，即采用从右向左的方法压栈。 书名是 《C语言进阶：重点、难点与疑点》 内部变量，只是空间安排，不需要压栈，只有初始化的问题。 初始化未必是压栈操作，实际上只需要向这个地址空间，填入初始化的数据，就可以了 初始化函数内部的数组，要么是个填充操作,典型的如 int a[]=｛0｝； 要么是个复制操作例如： int a[]={1,2,3,4,5,6,7,8,9,10} 而函数参数，数组只需要传递地址，不需要数据压栈。所以并不需要特殊处理。 所以，你看到的这个，可能只是某个具体的实现。未必都是如此 [/quote] 忘了说了，这段话是在函数调用的章节中提到的，函数调用的时候会有压栈操作

user: user-2
content: 一年多过去了，今天我搜到了这个帖子。正好我看到的一本书里面是这样说的： 默认的函数内部变量的压栈操作为：从上到下、从左到右，采用4字节对齐。数组的压栈方法略有不同，即从最后一个元素开始，直到起始元素为止，即采用从右向左的方法压栈。 书名是 《C语言进阶：重点、难点与疑点》

user: user-3
content: 所谓 栈空间的生长方式由高到低，指的是（以 X86 体系结构为例），一些默认使用 sp (stack pointer)寄存器的指令，如 push, pop, call, ret 等，在入栈的时候 sp 会减小，出栈的时候 sp 增加。  其实栈空间也是一段普通的内存，可以任意的方式访问；sp 也可以不通过 push pop 直接操作。  以上是机器语言实现层面的问题。在这里，没有所谓数组的概念。  在访问函数参数与局部变量的时候，几乎一定是不会用 push 和 pop 的。 开辟空间的时候，参数入栈经常不使用 push ，局部变量开辟空间几乎一定不会用 push （而是直接 sub sp）。  编译器生成代码用 sub sp 预留一段空间给局部变量用，然后把那一段给那个变量用，编译器就可以自行决定了。 由于这只是一段普通的内存，当然怎么用都可以。  以下说 C 语言。  然而 C 语言本身并没有栈的概念。 C 语言只规定了一些特殊情况下的指针比较结果，如在两个指针指向同一数组中的元素时（序号大的大于序号小的），指向同一个结构体对象的成员时（后定义的成员大于先定义的成员），等等。但是对于指向同一个函数的不同局部变量的指针之间的比较结果并没有规定。也就是说，编译器可以随便安排他们的顺序。  =================== 这里应该是将不同层面的问题混淆在了一起。

user: user-1
content: 引用 15 楼 u010956619 的回复:一年多过去了，今天我搜到了这个帖子。正好我看到的一本书里面是这样说的： 默认的函数内部变量的压栈操作为：从上到下、从左到右，采用4字节对齐。数组的压栈方法略有不同，即从最后一个元素开始，直到起始元素为止，即采用从右向左的方法压栈。 书名是 《C语言进阶：重点、难点与疑点》 内部变量，只是空间安排，不需要压栈，只有初始化的问题。 初始化未必是压栈操作，实际上只需要向这个地址空间，填入初始化的数据，就可以了 初始化函数内部的数组，要么是个填充操作,典型的如 int a[]=｛0｝； 要么是个复制操作例如： int a[]={1,2,3,4,5,6,7,8,9,10} 而函数参数，数组只需要传递地址，不需要数据压栈。所以并不需要特殊处理。 所以，你看到的这个，可能只是某个具体的实现。未必都是如此

user: user-0
content: 引用 13 楼 aa2650 的回复:[quote=引用 9 楼 yusiguyuan 的回复:] [quote=引用 7 楼 aa2650 的回复:] 就像4楼说的, 数组的方向和栈的方向为什么要相同呢？ 因为栈是用数组实现的, 所以lz产生了数组增长方向一定得和栈方向一致的下意识的错觉吧 这种错觉就好比, 物理内存增长方向还是从低地址向高地址的呢, 那么堆的方向对了, 栈的方向错啦, 怎么能开口向上呢?(这么比喻是不是感觉到你的那种错觉很无理了?) 数组无非是连续的char而已, 一个int其实也是4个连续的char, 如果必须按栈的方向来, 就没有大小端的问题存在了.我是这么想的，首先申请地 数组属于栈空间内，既然这个变量在栈空间内，那么就总得符合栈空间的一些规范吧，比如栈空间的生长方式是由从高地址到低地址，但是数组中的元素地址并非这样，但是申请别的变量又符合这种规范，就觉得有什么蹊跷不懂的地方[/quote] 啥叫栈空间的规范? 就是栈的"后进先出"特性而已. 你所谓的"申请别的变量符合", 那是因为你把别的变量当作一个整体看了; 你所谓的"数组不符合", 那是因为你把其它变量都从整体看, 却偏偏"歧视性"地把数组变量从微观去看.(为啥int是4个字节你把这4个字节从整体来看, 数组是n个字节你却不把这n个字节当成一个整体看???)  从整体看, "别的变量"和数组变量, 都是符合栈的LIFO规范(从整体看!!! 整个数组是一个变量, 别一看数组又从微观去看) 从微观看, 数组增长方向和栈开头方向没必要一致; 你所谓的"别的变量", 比如一个int是4个字节, 也不一定和栈的开口方向一致(大小端总有一种情况是不一样的吧?)  我觉得解释的够细了, 你还不理解的话, 只能说你钻牛角尖没真正看进去大家的回复.[/quote] 首先，谢谢，说句你懂我，你真的懂我的意思，我可能就是钻到那个牛角尖了----从整体来看（把数组当做一个变量而不是多个变量）确实符合栈的规范。 但是，数组也是多个4字节组成的，为啥就没按照栈的规范呢（可能我有牛角尖了，呵呵呵），你所说的除最后一句话 我都很清楚，但是有一句不是很明白。      从微观看, 数组增长方向和栈开头方向没必要一致; 你所谓的"别的变量", 比如一个int是4个字节, 也不一定和栈的开口方向一致(大小端总有一种情况是不一样的吧?)     这句该咋想呢？谢谢啊！！！

user: user-4
content: 引用 9 楼 yusiguyuan 的回复:[quote=引用 7 楼 aa2650 的回复:] 就像4楼说的, 数组的方向和栈的方向为什么要相同呢？ 因为栈是用数组实现的, 所以lz产生了数组增长方向一定得和栈方向一致的下意识的错觉吧 这种错觉就好比, 物理内存增长方向还是从低地址向高地址的呢, 那么堆的方向对了, 栈的方向错啦, 怎么能开口向上呢?(这么比喻是不是感觉到你的那种错觉很无理了?) 数组无非是连续的char而已, 一个int其实也是4个连续的char, 如果必须按栈的方向来, 就没有大小端的问题存在了.我是这么想的，首先申请地 数组属于栈空间内，既然这个变量在栈空间内，那么就总得符合栈空间的一些规范吧，比如栈空间的生长方式是由从高地址到低地址，但是数组中的元素地址并非这样，但是申请别的变量又符合这种规范，就觉得有什么蹊跷不懂的地方[/quote] 啥叫栈空间的规范? 就是栈的"后进先出"特性而已. 你所谓的"申请别的变量符合", 那是因为你把别的变量当作一个整体看了; 你所谓的"数组不符合", 那是因为你把其它变量都从整体看, 却偏偏"歧视性"地把数组变量从微观去看.(为啥int是4个字节你把这4个字节从整体来看, 数组是n个字节你却不把这n个字节当成一个整体看???)  从整体看, "别的变量"和数组变量, 都是符合栈的LIFO规范(从整体看!!! 整个数组是一个变量, 别一看数组又从微观去看) 从微观看, 数组增长方向和栈开头方向没必要一致; 你所谓的"别的变量", 比如一个int是4个字节, 也不一定和栈的开口方向一致(大小端总有一种情况是不一样的吧?)  我觉得解释的够细了, 你还不理解的话, 只能说你钻牛角尖没真正看进去大家的回复.

user: user-0
content: 引用 11 楼 yusiguyuan 的回复:[quote=引用 10 楼 my_live_123 的回复:] 和栈的增长方向没有关系 谁给你说的a》b》c？估计你没有仔细看跑的结果吧？！  这是我在UNBUNTU下的结果，其实栈的增长方式是我提前假设的，在unbuntu下这个结果页很让我好奇，最起码咱两的规律就不一样，你的是一只递增，我的并不是[/quote] 这是我在VC6.0下的结果，这个结果就是我想讨论的依据，暂且依据这个讨论，a b c 的地址按照栈的生长方式来，但是到了数据的内部元素就不再按照这种原则了，就怀疑编译器做了什么  就想搞明白怎么回事

user: user-0
content: 引用 10 楼 my_live_123 的回复:和栈的增长方向没有关系 谁给你说的a》b》c？估计你没有仔细看跑的结果吧？！  这是我在UNBUNTU下的结果，其实栈的增长方式是我提前假设的，在unbuntu下这个结果页很让我好奇，最起码咱两的规律就不一样，你的是一只递增，我的并不是

user: user-4
content: 就像4楼说的, 数组的方向和栈的方向为什么要相同呢？ 因为栈是用数组实现的, 所以lz产生了数组增长方向一定得和栈方向一致的下意识的错觉吧 这种错觉就好比, 物理内存增长方向还是从低地址向高地址的呢, 那么堆的方向对了, 栈的方向错啦, 怎么能开口向上呢?(这么比喻是不是感觉到你的那种错觉很无理了?) 数组无非是连续的char而已, 一个int其实也是4个连续的char, 如果必须按栈的方向来, 就没有大小端的问题存在了.

user: user-5
content: 和栈的增长方向没有关系 谁给你说的a》b》c？估计你没有仔细看跑的结果吧？！

user: user-0
content: 引用 7 楼 aa2650 的回复:就像4楼说的, 数组的方向和栈的方向为什么要相同呢？ 因为栈是用数组实现的, 所以lz产生了数组增长方向一定得和栈方向一致的下意识的错觉吧 这种错觉就好比, 物理内存增长方向还是从低地址向高地址的呢, 那么堆的方向对了, 栈的方向错啦, 怎么能开口向上呢?(这么比喻是不是感觉到你的那种错觉很无理了?) 数组无非是连续的char而已, 一个int其实也是4个连续的char, 如果必须按栈的方向来, 就没有大小端的问题存在了.我是这么想的，首先申请地 数组属于栈空间内，既然这个变量在栈空间内，那么就总得符合栈空间的一些规范吧，比如栈空间的生长方式是由从高地址到低地址，但是数组中的元素地址并非这样，但是申请别的变量又符合这种规范，就觉得有什么蹊跷不懂的地方

user: user-0
content: 我是这么想的，首先申请地 数组属于栈空间内，既然这个变量在栈空间内，那么就总得符合栈空间的一些规范吧，比如栈空间的生长方式是由从高地址到低地址，但是数组中的元素地址并非这样，但是申请别的变量又符合这种规范，就觉得有什么蹊跷不懂的地方

user: user-6
content: 数组的方向和栈的方向为什么要相同？

user: user-7
content: 引用 楼主 yusiguyuan 的回复:int main()   {       int a;       int b;       int c;       int p[5];           printf("a  %p\n",&a);       printf("b  %p\n",&b);       printf("c  %p\n",&c);       printf("p0    %p\n",&p[0]);       printf("p1    %p\n",&p[1]);       printf("p2    %p\n",&p[2]);       printf("p3    %p\n",&p[3]);       printf("p4    %p\n",&p[4]);  	return 0; 	} 在上述代码中，a的地址比b的地址大 b低地址比c的地址大， 这正好说明了栈的生长方式是从高地址到低地址 但是数组内的元素却是依次递增 这样好像违背了栈的生长方式    这是为什么？ 编译器做了什么？ 编译器怎么处理定长数组？ 数组在内存中如何分配的 其解释！！谢谢 空增/减栈 满增/减栈  说实在这还得看编译器怎么玩

user: user-8
content: 1 C语言保证 数组中 下标小的元素必然在下标大是元素前面  2 "栈的生长方式"和变量内存分配布局 没有必然联系

user: user-9
content: 计算机组成原理→DOS命令→汇编语言→C语言（不包括C++）、代码书写规范→数据结构、编译原理、操作系统→计算机网络、数据库原理、正则表达式→其它语言（包括C++）、架构……  对学习编程者的忠告： 眼过千遍不如手过一遍！ 书看千行不如手敲一行！ 手敲千行不如单步一行！ 单步源代码千行不如单步对应汇编一行！  VC调试时按Alt+8、Alt+7、Alt+6和Alt+5,打开汇编窗口、堆栈窗口、内存窗口和寄存器窗口看每句C对应的汇编、单步执行并观察相应堆栈、内存和寄存器变化，这样过一遍不就啥都明白了吗。 对VC来说，所谓‘调试时’就是编译连接通过以后，按F10或F11键单步执行一步以后的时候，或者在某行按F9设了断点后按F5执行停在该断点处的时候。 （Turbo C或Borland C用Turbo Debugger调试,Linux或Unix下用GDB调试时,看每句C对应的汇编并单步执行观察相应内存和寄存器变化。）

user: user-1
content: 看看反汇编就知道了， 1）分配内存空间的动作，(X86,Win32 MSVC,就是对SP，esp 这样的堆栈指针寄存器，做一下减法)。 2）为变量确定地址，每个变量，根据需要安排一个位置 。 (X86,Win32 MSVC，一般是相对ebp 的位置偏移量，如果做了栈帧优化，则可能会是相对esp 的置偏移量） 编译器就做了这些 至于数组，只需要最低地址，和数组空间大小，两个数据就可以了 空间大小，就是第一步要减去的数值，地址，其实只是相对偏移量由于每个函数的某一次调用中，ebp,esp 尤其是 ebp 内容是确定的，所以 ebp 的内容加上相对偏移量，就是地址了。  PS： 编译后的代码中，不必存在变量名字。

user: user-10
content: 高端的问题，没有考虑过呵呵