c 语言中的函数参数

很久以前写的旧文,不改了,直接发出来
---------------
这个帖子分以下几个部分:

  1,声明和定义
  2,参数传递方法
  3,参数入栈顺序
  4,指针作为参数
  5,数组作为参数
  6,结构体作为参数



  1,函数声明和定义:
      首先,分清楚声明和定义。
      在一个函数声明中,你需要给出函数名,函数返回值类型(如果有的话),以及在调用这个函数时
  必须提供的参数个数及类型。比如:
  elem   *   next_elem();
  char   *   strcpy(char   *to   ,const   char   *from);
  void   exit(int);

  在声明中,只有以下三项是必需的:
    a.函数名
    b.返回值类型
    c.参数个数及类型

    定义:
    在程序调用的每个函数都应被定义(在某个地方,且仅仅定义一次),
  函数定义也就是一个给出了函数体的函数声明
      比如:
      void   plus(int   a);   //声明   


    void   plus(int   a)
      {   //给出了函数体的声明,也就是定义   
          ++a;
        }


    注意:同一个函数的定义和声明和定义必须一致:
      即:
    a.函数名一致
    b.返回值类型一致
    c.参数个数及类型一致
    并没有把参数名称作为必须一致的一部分



  2,参数传递方法:
      函数参数传递方法不外下边的三种:
      a,通过寄存器
      b,通过全局变量指针
      c,通过栈
      c中函数参数传递一般通过栈

      如下代码:
  #include   <stdio.h>

    void   plus(int   b);

    #ifdef   MAIN   
    int   main(void)
      {
        int   a   =   0;
        plus(a);
        printf("a   =   %d",   a);
        return   0;
      }
    #endif

      void   plus(int   b)
      {           ++b;
        }

  有朋友已经看出来了
    void   plus(int   b)
      {           ++b;
        }
  plus这个函数根本不能改变函数外的任何变量

  为什么呢
  因为c中参数是通过栈传递的,在main中调用plus(a)时
  main中的a   精确副本被压入栈,然后,进入到plus以后,pop出来
  然后,复制到局部变量b(也在栈中)的空间
  再对b执行++b;
  然后,函数结束时,b所占用的栈的空间被释放
  然后,就等于什么   也没干
  所以,从这个意义上来说,c的函数调用时,参数传递是单向的


  3,参数入栈顺序:

        不一定
        有从左向右入栈的,有从右向左入栈
        比如下边的程序:
        #include   <stdio.h>
        void   fun(int   a,int   b,int   c)
        {
          printf("   a   =   %d/n",a);
          printf("   b   =   %d/n",b);
          printf("   c   =   %d/n",c);
        }

        int   main(void)
        {
        int   c;
        c   =   0;
        fun(c,++c,++c);
        return   0;
        }
    有可能在不同的编译器上得出不同的结果。
    详情可参看:
  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_core___stdcall.asp   


    4,指针作为参数
  指针这个玩意儿有点与别人不一样
      由于指针是一个变量,存放了另外一个变量的地址
      当这个地址被当作参数,传入到函数里以后,我们就可以按图索骥来找
      这个地址里的东西,胡作非为一番了
      #include   <stdio.h>

    void   plus(int   *b);

    #ifdef   MAIN   
    int   main(void)
      {
        int   a   =   0;
        plus(&a);
        printf("a   =   %d",   a);
        return   0;
      }
    #endif

      void   plus(int   *b)
      {           ++   *b;
        }

      void   swap(int   *v1,int   *v2)
        {
          int   vt   =   *v1;
          *v2   =   vt;
            *v1   =   *v1;
        }

  5,数组作为参数:

      数组作为函数的参数,传递的是数组的首元素的地址
      (数组当作参数时,退化为指针)
      如:
      #include   <stdio.h>
      #include   <stdio.h>



    #ifdef   MAIN   
    int   main(void)
      {
        int   len;
        char   str[]   =   "an   array";
        len   =strlen(str);
        printf("len   of   str   is   :%d",len);
        return   0;
      }
    #endif


  所以,在被调用的函数内部,数组参数的大小是不可用的

  6,结构体作为参数:
        跟一般的参数没有两样,但是当结构体很大时,
      传递可能要占用较大的空间,可以考虑只传入结构体的指针


      ps:
      上边有#ifdef   MAIN的代码,可以用gcc   -o   output.exe   -DMAIN   input.c来编译

-----------------------

  为了不使bcc的优化起作用,改一下代码:
  #include   <stdio.h>

    void   plus(int   b);

    #ifdef   MAIN   
    int   main(void)
      {
        int   a   =   0;
        plus(a);
        printf("a   =   %d/n",   a);
        return   0;
      }
    #endif

      void   plus(int   b)
      {
            ++b;
            printf("b   =   %d/n",b);
        }
  生成汇编代码:

  E:/cl>bcc32   -S   -DMAIN   tp2.c
  Borland   C++   5.5.1   for   Win32   Copyright   (c)   1993,   2000   Borland
  tp2.c:
  打开tp2.asm:
        ;   int   main(void)
        ;
  push             ebp
  mov               ebp,esp
  push             ebx
        ;
        ;     {
        ;       int   a   =   0;
        ;
  @1:
  xor               ebx,ebx   ;a   =0
        ;
        ;       plus(a);
        ;
  ?live1@32:   ;   EBX   =   a
  push             ebx           ;将a的值入栈//注意,不是a的地址     
  call             _plus       ;

  ;   ......

  _plus proc near
  ?live1@96:
        ;
        ;     void   plus(int   b)
        ;
  push             ebp
  mov               ebp,esp                               ;
  mov               eax,dword   ptr   [ebp+8]   ;将a的值给b,即形参与实参结合
        ;                                                         ;dword   ptr   [ebp+8]就是刚刚push入栈的a
        ;     {
        ;           ++b;
        ;
  ?live1@112:   ;   EAX   =   b                               ;EAX就是b
  @4:
  inc               eax                                     ;++b,只改变了局部变b的内容,没有改变a的内容
        ;
        ;           printf("b   =   %d/n",b);
        ;
  push             eax
  push             offset   s@+8
  call             _printf
  add               esp,8
        ;
        ;       }
        ;
  ?live1@144:   ;
  @5:
  pop               ebp
  ret
  _plus endp
  再看下边的程序:
      #include   <stdio.h>

    void   plus(int   *b);

    #ifdef   MAIN   
    int   main(void)
      {
        int   a   =   0;
        plus(&a);
        printf("a   =   %d",   a);
        return   0;
      }
    #endif

      void   plus(int   *b)
      {           ++   *b;
        }


          ;   int   main(void)
        ;
  push             ebp
  mov               ebp,esp
  push             ecx
        ;
        ;     {
        ;       int   a   =   0;
        ;
  @1:
  xor               eax,eax                               ;eax   清0
  mov               dword   ptr   [ebp-4],eax   ;a   =   0,dword   ptr   [ebp-4]就是局部变量a
        ;
        ;       plus(&a);
        ;
  lea               edx,dword   ptr   [ebp-4]   ;(Load   Effective   Address)得到a的有效地址,即:&a
  push             edx                                       ;将a的地址入栈
  call             _plus

  ;..............

  _plus proc near
  ?live1@96:
        ;
        ;     void   plus(int   *b)
        ;
  push             ebp
  mov               ebp,esp
        ;
        ;     {           ++   *b;
        ;
  @4:
  mov               eax,dword   ptr   [ebp+8]     ;b   =   &a;dword   ptr   [ebp+8]是参数在栈中的地址
  inc               dword   ptr   [eax]                 ;将eax地址处的内容++   (即:   ++*b   ;++*(&a)   )
        ;
        ;       }
        ;
  @5:
  pop               ebp
  ret



  通过这些汇编代码可以看出c参数传递的特性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值