Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4116740
  • 博文数量: 447
  • 博客积分: 1241
  • 博客等级: 中尉
  • 技术积分: 5786
  • 用 户 组: 普通用户
  • 注册时间: 2011-01-27 06:48
个人简介

读好书,交益友

文章分类

全部博文(447)

文章存档

2023年(6)

2022年(29)

2021年(49)

2020年(16)

2019年(15)

2018年(23)

2017年(67)

2016年(42)

2015年(51)

2014年(57)

2013年(52)

2012年(35)

2011年(5)

分类: C/C++

2013-08-07 10:47:23

发现这个问题纯属偶然,c语言判断计算机上栈的增长方式。
#include

void func(int a,int b)
{
  int c,d;
  printf("func:&a..%p  &b..%p\n",&a,&b);
  printf("func:&c..%p  &d..%p\n",&c,&d);
}

int main(void)
{
   int a,b;
   printf("main:&a..%p  &b..%p\n",&a,&b);
   func(1,2);
   return  0;
}

结果是
main:&a..0x7fff6724cc9c  &b..0x7fff6724cc98
func:&a..0x7fff6724cc6c  &b..0x7fff6724cc68
func:&c..0x7fff6724cc7c  &d..0x7fff6724cc78
其中第二行 是有问题的,因为栈是向低地址方向增长的,栈顶的地址小,栈底的地址大,b先于a入栈,b的地址大于a的地址,所以说第二行是错误的。
同样的程序 在windows下编译,运行
结果
main:&a..002EFB04  &b..002EFAF8
func:&a..002EFA20  &b..002EFA24
func:&c..002EFA10  &d..002EFA04
百思不得其解。
找了一台开发的编译机,同样的系统,Intel(R) Xeon(R) CPU           X3430  @ 2.40GHz,但是安装的是32位的centos,
运行结果
main:&a..0xbfdd48fc  &b..0xbfdd48f8
func:&a..0xbfdd48e0  &b..0xbfdd48e4
func:&c..0xbfdd48cc  &d..0xbfdd48c8
和windows结果相同
只好反编译了
func:
.LFB0:
 .cfi_startproc
 pushq %rbp
 .cfi_def_cfa_offset 16
 .cfi_offset 6, -16
 movq %rsp, %rbp
 .cfi_def_cfa_register 6
 subq $32, %rsp
 movl %edi, -20(%rbp)
 movl %esi, -24(%rbp)
 movl $.LC0, %eax
 leaq -24(%rbp), %rdx
 leaq -20(%rbp), %rcx
 movq %rcx, %rsi
 movq %rax, %rdi
 movl $0, %eax
 call printf
 movl $.LC1, %eax
 leaq -8(%rbp), %rdx
 leaq -4(%rbp), %rcx
 movq %rcx, %rsi
 movq %rax, %rdi
 movl $0, %eax
 call printf
 leave
 .cfi_def_cfa 7, 8
 ret
 .cfi_endproc
IA64平台传递参数的时候,没有使用堆栈。
我以前写的blog

这是google的面试题。在X86平台linux下,用malloc等函数分配的内存的地址是向上增长的。而堆栈段(注意,堆栈是Stack,堆是Heap,不是同一个东西)地址是向下增长的。
<- 堆栈增长方向
内存地址增长方向 ->
这是CISC类型的cpu,别的CPU例如:
PowerPC体系结构是RISC(精简指令集计算),PowerPC的堆栈结构和X86有很大不同,PowerPC没有类似X86里ebp这个指针,它只使用r1寄存器把整个堆栈构成一个单向链表,其增长方向是从高地址到低地址,而内存地址的增长方向也是从低地址到高地址的。

再举个ARM上ucos的例子
μC/OS-Ⅱ支持的处理器的堆栈既可以从上(高地址)往下(低地址)递减也可以从下往上递增。用户在调用OSTaskCreateExt()的时候必须知道堆栈是递增的还是递减的(参看用户所用处理器的OS_CPU.H中的OS_STACK_GROWTH),因为用户必须得把堆栈的栈顶传递给OSTaskCreateExt(),而栈顶可能是堆栈的最低地址(当OS_STK_GROWTH为0时),也可能是最高地址(当OS_STK_GROWTH为1时)。
这就迷惑了,到底是谁决定的呢?

还是好友谭航猫猫给出了答案,因为几乎所有的CPU都支持4种堆栈方式,因为堆栈有2大类 ,一个是递增一个是递减,但递增又分2类
一个是满递增,一个是满递减 ,递减也分2类一个是满递减,一个是空递减,所谓满就是你的堆栈指针一直指向最后压入的数据的那个位置 ,>空就是指向你下一次要压的数据的那个位置,
ads上开发就有一个堆栈初始化的 问题,编译器的初始化堆栈的函数,这个函数你看不到的,但默认就这样了,所以就不该担心空间问题了,在一开始就已经给你运行的代码准准好了的,所以不同模式下就有不同模式的堆栈,不然怎么能保证在模式切换的时候数据不被破坏啊.
ads支持的是递减的 ,在其他的一些编译器下只能是递增的栈,所以这个问题应该是这么说把操作系统移植到处理器上用的编译器的堆栈方式怎么求出来的问题。
因为每个操作系统都有任务堆栈需要初始化,如果方向弄反了就出错了。
文件OS CPU. H 中包括了用# define 语句定义的与处理器相关的常数、宏以及类型. 移植时主要修改的内容有:与编译器相关的数据类型的设定;用#define 语句定义2 个宏开关中断;根据堆栈的方向定义OS STK GROWTH等.
在将uC/ OS - II 移植到ARM 处理器上时,首先进行基本配置和数据类型定义. 重新定义数据类型是为了增加代码的可移植性,因为不同的编译器所提供的同一数据类型的数据长度并不相同,例如int型,在有的编译器中是16 位,而在另外一些编译器中则是32 位. 所以,为了便于移植,需要重新定义数据类型,如INT32U 代表无符号32 位整型. typedefunsigned int INT8U ,就是定义一个8 位的无符号整型数据类型.
其次就是对ARM 处理器相关宏进行定义,如ARM处理器中的退出临界区和进入临界区的宏定义,退出临界区宏定义[5 ] : # define OS EXITCRITICAL () ARMDisable Int ( ) / / 关中断,进入临界区宏定义# define OS ENTER CRITICAL ( ) AR2MEnableInt () / / 开中断.
最后就是堆栈增长方向的设定. 当进行函数调用时,入口参数和返回地址一般都会保存在当前任务的堆栈中,编译器的编译选项和由此生成的堆栈指令就会决定堆栈的增长方向[6 ] ,定义为# define OS STK GROWTH 1.
代码
#include "stdafx.h"
#include

void foo(int num) {
 printf("%p\n", (void*)&num);
 if (!num) foo(1);
}

int main(void) {
 foo(0);
 return 0;
}
第二次的值小于第一次的

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

lmnos2013-08-08 08:57:09