zoukankan      html  css  js  c++  java
  • <C和指针---读书笔记7>

     函数定义:

          类型  函数名 (参数列表) 

            {

                     代码块

               }

     

    类型: 是指返回值类型

    Return语句:  注意return语句将不再往下执行,将退出整个函数。

    如果里面没有返回语句也是可以的。

     

    函数声明:

    为什么要声明? 我们直到变量声明,是为了分配好地址然后本质上都是对地址的操作。

    函数声明其实也是,当程序执行某个分支函数时,需要跳转到函数入口地址处。

    函数声明还有一个作用: 函数一般带有参数列表,列表里面规定了函数的使用条件,为了避免你胡乱写一通造成跑飞,编译器要求你提前声明,

     声明返回值类型、声明参数列表,这样编译器就能彻底的检查你调用这个函数是否规范。避免后续debug问题。

     函数声明方法:

    (1)    函数定义出现在 调用前。 这时直接使用即可。适用于简单的代码。

    (2)    使用函数原型。   ------     类型  函数名 (参数列表);

    在大型工程中,子函数十分繁杂,一般都将子函数写入”xxxx.h”文件中,然后调用即可。避免纠缠与函数声明与否的问题上。

     

    参数列表:

         对参数的调用,是按照”传值调用”,意思是将参数列表的内容copy一个副本,函数调用所操作的对象均是这个副本。

         但是按照这个方法:如果参数列表是一个指针变量,副本依旧是copy值,但间接访问副本,最终访问的却是真正的内容。仅仅是指针值不变。

         故参数列表里面有 指针、数组名 这类代表指针类型的时候,需要特别注意。 

     

    当函数声明、函数定义均好了。下面就是函数调用了。

    函数调用支持普通的调用这没得说,支持递归调用吗?

    递归调用: 支持,有时候直接看注释,看递归完成什么功能,不必纠缠于实现。因为它是螺旋上升式的,比较饶人。 所以不纠缠于实现、只关心功能。

     

    可变参数列表

      比如想求 average(length,va1,va2…..) 想做成任意整数的求平均,函数定义该怎么写?

    考虑到这种需求,C标准提供了一个stdarg.h的库。用于专门处理这种 可变参数列表。

    这个库函数提供了一下:  va_list 、va_start() 、va_arg、va_end.

     

    typedef char *va_list;

    #define va_start(ap,v)     ap = (va_list)&v + sizeof(v)

    #define va_arg(ap,t) (((t *)ap)++[0])

    #define va_end(ap)

    详细描述: va_list:  声明一个char指针

    va_start(ap,v)   其中v是参数列表 “…”前的最后一个参数名  。 等价于:  ap = (va_list)&v + sizeof(v)

          先求出sizeof(v), 同时取v的地址,加起来就代表指针指向v下一个。做为ap的值。

    va_arg(ap,t) : 其中t是 数据类型;  即把ap当成一个 t类型的指针, 取出该值,并  t类型指针+1 =  ap+ 1*  sizeof(t) 、

                               能够正好的卡好位置。

    va_end(ap) :释放。

     

    //////////////////////////////////////////////////////////////////////////////////

    有一下事例可供参考:

    Int  average (int length ,…) {

      Int sum =0;

     Int I;

    Va_list va;

    Va_start(ap, length);

      For( I =0;i<length;i++) {

         Sum = sum +  va_arg(ap,int);

    Va_end(ap);

    Return (sum/length);

    }

    }

     

    调用的时候:

      A = average(3 ,10,11,12);

      B= average(4 ,10,11,12,15);

     

     

     

     

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

    这个 标准库函数,用于可变参数列表。

    比如printf(“the print value is  %c, %d, %d”,a,b,c);       printf(“the print value is  %d, %c,”,e,f);

    显然这些参数列表里面的内容 是变动的,参数个数是变动的。 在函数原型中该怎么写?

    C语言通过指针的形式,可以完成 可变参数列表的读取。 只需要读取可变参数列表首地址,每读一个,指针相应变动,这样就能保证读出正确。

    当可变参数列表是固定的一种数据类型时是容易的,比如int型。 但可变参数列表的 数据类型也是变动的话,我们就不好办了。

    因此程序员必须知晓,可变参数列表的 数据类型顺序。

     

    Printf( const  char * fmt , … )  这是可变参数函数声明的方法。 其中 const char *fmt是一个指向常数字符串的指针。 … 就是可变参数列表的缩写。

     

     

    //////////////////////////////////////////////////////////////////////

    #include "stdarg.h"

    char DbgBuff[128];

    int Printf (const char *fmt,...)

    {

        va_list ap;

        int n;

        va_start(ap, fmt);

        n=vsprintf(DbgBuff, fmt, ap);

        va_end(ap);

        DbgPutChar(DbgBuff);

        return n;

    }

    //////////////////////////////////////////////////////////////////////

    Stdarg.h里面内容是这样写的:

    typedef char *va_list;

    #define va_start(ap,v)     ap = (va_list)&v + sizeof(v)

    #define va_arg(ap,t) (((t *)ap)++[0])

    #define va_end(ap)

     

    Va_list   ap ;  就是声明ap是一个 char * 变量,即指向字符串的指针。

    Va_start(ap, fmt)   等价于         ap = (char * ) &fmt +sizeof(fmt)

                                                                      &fmt获得字符串指针的地址,因为在堆栈中  :     …5  …4 …3 …2 …1 …0  + 指针地址。

                                                                        获得指针地址,并转为char *型是为了 赋值给ap.  加上fmt所占大小,就是…0的地址。 即获得了 可变参数0的地址。

    Va_arg(ap,t)    比如va_arg(ap ,int)  等价于 (     ((int *) ap) ++[0]   )  =    *ap++[0]    =  *ap +[0] 并ap+1 (此处因为ap被强制变为int,故指针ap+1,实质上 +4处理 )。 即ap指向了 可变变量1的地址。      即获得可变变量0的值并 将指针转移到 …1 

                       完成了整个变量列表的可持续性的访问。

     

    Va_end(ap),其实啥也不干。

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    linux 6.9 补丁修补漏洞
    更改交换分区
    MariaDB Windows 安装
    关于Oracle内存分配-解决实际运行时最大Session数不一致远小于系统配置最大的Session数目
    Angular 相关概念
    实用工具推荐
    DDD目录结构
    全局异常处理区分返回响应类型是页面还是JSON
    Lambda学习总结(三)--方法引用
    Lambda学习总结(二)--Stream流
  • 原文地址:https://www.cnblogs.com/mokang0421/p/7476046.html
Copyright © 2011-2022 走看看