淺談C語言函數(shù)調(diào)用參數(shù)壓棧的相關(guān)問題
參數(shù)入棧的順序
以前在面試中被人問到這樣的問題,函數(shù)調(diào)用的時(shí)候,參數(shù)入棧的順序是從左向右,還是從右向左。參數(shù)的入棧順序主要看調(diào)用方式,一般來說,__cdecl 和__stdcall 都是參數(shù)從右到左入棧。
看下面的代碼:
#include <stdio.h>
int test(int a, int b)
{
  printf("address of a %x.\n", &a);
  printf("address of b %x.\n", &b);
  return 0;
}
int main()
{
  test(1, 2);
  return 0;
}
在64位Ubuntu的系統(tǒng)下的運(yùn)行結(jié)果是:
address of a 1ec62c. address of b 1ec628.
32位Ubuntu的結(jié)果是:
address of a bfd03290. address of b bfd03294.
可以看出,首先,不同的體系結(jié)構(gòu),棧增長的方向也不同,有的是從低地址向高地址方向增長,有的是從高地址向低地址方向增長。
可以用以下的代碼來判斷棧的增長方向:
typedef enum {
  LOW_TO_HIGH,
  HIGH_TO_LOW,
  LEFT_TO_RIGHT,
  RIGHT_TO_LEFT,
}stack_direc_t;
int stack_grow_direc()
{
  static char *p = NULL;
  char c;
  if (p == NULL) {
    p = &c;
    stack_grow_direc();
  }
  else {
    printf("First in stack address is %x.\n", p);
    printf("Second in stack address is %x.\n", &c);
    if (&c > p) {
      printf("Stack grows from low address to high address!\n");
      return LOW_TO_HIGH;
    }
    else {
      printf("Stack grows from high address to low address!\n");
      return HIGH_TO_LOW;
    }
  }
}
函數(shù)調(diào)用時(shí)棧里都有什么
以參數(shù)從左到右入棧為例:
push arg0 -- High Address push arg1 ... push argn push eip push ebp -- Low address
32位系統(tǒng)和64位系統(tǒng)函數(shù)調(diào)用時(shí),參數(shù)入棧方式有不同么?
這個(gè)問題在不久之前被人問題,當(dāng)時(shí)傻了,我一直以來只關(guān)注過32位系統(tǒng)的參數(shù)入棧方式,一直以為64位系統(tǒng)也是一樣,沒有什么不同,現(xiàn)在歸納起來有兩點(diǎn):
64位系統(tǒng)先把傳入?yún)?shù)放在寄存器里面,在被調(diào)函數(shù)的具體實(shí)現(xiàn)中把寄存器的值入棧,然后再去棧中取參數(shù)
64位系統(tǒng)棧中參數(shù)存放的順序是從左至右的(因?yàn)橄冉?jīng)歷了寄存器傳值)
看下面的反匯編:
C代碼同上面一樣
Ubuntu 32位反匯編:
int main()
{
 804846d:  55           push  %ebp
 804846e:  89 e5          mov  %esp,%ebp
 8048470:  83 e4 f0        and  $0xfffffff0,%esp
 8048473:  83 ec 10        sub  $0x10,%esp
  test(1, 2);
 8048476:  c7 44 24 04 02 00 00  movl  $0x2,0x4(%esp)
 804847d:  00 
 804847e:  c7 04 24 01 00 00 00  movl  $0x1,(%esp)
 8048485:  e8 8a ff ff ff     call  8048414 <test>
  return 0;
 804848a:  b8 00 00 00 00     mov  $0x0,%eax
}
int test(int a, int b)
{
 8048414:  55           push  %ebp
 8048415:  89 e5          mov  %esp,%ebp
 8048417:  83 ec 18        sub  $0x18,%esp
  printf("address of a %x.\n", &a);
 804841a:  b8 60 85 04 08     mov  $0x8048560,%eax
 804841f:  8d 55 08        lea  0x8(%ebp),%edx
 8048422:  89 54 24 04       mov  %edx,0x4(%esp)
 8048426:  89 04 24        mov  %eax,(%esp)
 8048429:  e8 12 ff ff ff     call  8048340 <printf@plt>
  return 0;
 8048466:  b8 00 00 00 00     mov  $0x0,%eax
}
Ubuntu 64位反匯編:
int main()
{
 40056e:  55           push  %rbp
 40056f:  48 89 e5        mov  %rsp,%rbp
  test(1, 2);
 400572:  be 02 00 00 00     mov  $0x2,%esi
 400577:  bf 01 00 00 00     mov  $0x1,%edi
 40057c:  e8 ac ff ff ff     callq 40052d <test>
  return 0;
 400581:  b8 00 00 00 00     mov  $0x0,%eax
}
int test(int a, int b)
{
 40052d:  55           push  %rbp
 40052e:  48 89 e5        mov  %rsp,%rbp
 400531:  48 83 ec 10       sub  $0x10,%rsp
 400535:  89 7d fc        mov  %edi,-0x4(%rbp)
 400538:  89 75 f8        mov  %esi,-0x8(%rbp)
  printf("address of a %x.\n", &a);
 40053b:  48 8d 45 fc       lea  -0x4(%rbp),%rax
 40053f:  48 89 c6        mov  %rax,%rsi
 400542:  bf 14 06 40 00     mov  $0x400614,%edi
 400547:  b8 00 00 00 00     mov  $0x0,%eax
 40054c:  e8 bf fe ff ff     callq 400410 <printf@plt>
  return 0;
 400567:  b8 00 00 00 00     mov  $0x0,%eax
}
看32位的ubuntu操作系統(tǒng), 8048476: 的確是把參數(shù)直接入棧,2先入棧,1后入棧。
8048476: c7 44 24 04 02 00 00 movl $0x2,0x4(%esp) 804847d: 00 804847e: c7 04 24 01 00 00 00 movl $0x1,(%esp) 8048485: e8 8a ff ff ff call 8048414 <test>
再來看64位的ubuntu操作系統(tǒng),2 和1根本就沒有放入到棧中,而是放到了寄存器esi和edi中。
40056f: 48 89 e5 mov %rsp,%rbp test(1, 2); 400572: be 02 00 00 00 mov $0x2,%esi 400577: bf 01 00 00 00 mov $0x1,%edi 40057c: e8 ac ff ff ff callq 40052d <test>
再來看64位系統(tǒng)test的實(shí)現(xiàn),先把edi入棧,再把esi入棧,這就是為什么函數(shù)看起來像是從左到右入棧的原因了。
40052d: 55 push %rbp 40052e: 48 89 e5 mov %rsp,%rbp 400531: 48 83 ec 10 sub $0x10,%rsp 400535: 89 7d fc mov %edi,-0x4(%rbp) 400538: 89 75 f8 mov %esi,-0x8(%rbp)
以上就是小編為大家?guī)淼臏\談C語言函數(shù)調(diào)用參數(shù)壓棧的相關(guān)問題的全部內(nèi)容了,希望對(duì)大家有所幫助,多多支持我們~
欄 目:C語言
下一篇:c語言實(shí)現(xiàn)詞頻統(tǒng)計(jì)的簡單實(shí)例
本文標(biāo)題:淺談C語言函數(shù)調(diào)用參數(shù)壓棧的相關(guān)問題
本文地址:http://www.jygsgssxh.com/a1/Cyuyan/2045.html
您可能感興趣的文章
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用函數(shù)刪除字符
 - 04-02c語言的正則匹配函數(shù) c語言正則表達(dá)式函數(shù)庫
 - 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
 - 04-02c語言中對(duì)數(shù)函數(shù)的表達(dá)式 c語言中對(duì)數(shù)怎么表達(dá)
 - 04-02c語言用函數(shù)寫分段 用c語言表示分段函數(shù)
 - 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排序法函數(shù)
 - 04-02c語言沒有round函數(shù) round c語言
 - 04-02c語言分段函數(shù)怎么求 用c語言求分段函數(shù)
 - 04-02C語言中怎么打出三角函數(shù) c語言中怎么打出三角函數(shù)的值
 - 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求階乘
 


閱讀排行
本欄相關(guān)
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
 - 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
 - 04-02c語言的正則匹配函數(shù) c語言正則表達(dá)
 - 04-02c語言用函數(shù)寫分段 用c語言表示分段
 - 04-02c語言中對(duì)數(shù)函數(shù)的表達(dá)式 c語言中對(duì)
 - 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
 - 04-02c語言沒有round函數(shù) round c語言
 - 04-02c語言分段函數(shù)怎么求 用c語言求分段
 - 04-02C語言中怎么打出三角函數(shù) c語言中怎
 - 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
 
隨機(jī)閱讀
- 04-02jquery與jsp,用jquery
 - 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
 - 01-10C#中split用法實(shí)例總結(jié)
 - 01-10delphi制作wav文件的方法
 - 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
 - 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
 - 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
 - 01-10使用C語言求解撲克牌的順子及n個(gè)骰子
 - 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
 - 01-11ajax實(shí)現(xiàn)頁面的局部加載
 


