美文网首页
第九章函数的再探

第九章函数的再探

作者: 全無 | 来源:发表于2017-12-22 22:25 被阅读14次

9.1 函数指针

函数的内存地址存储了函数的开始执行的位置,存储在函数指针中的内容就是这个地址。

9.1.1 声明函数指针

函数指针变量的声明

int (*pfunction) (int);   //函数指针变量的声明

指针只能赋予有这个返回类型的函数
函数指针名
指针只能赋予有这些参数类型的函数

9.1.2通过函数指针调用函数

函数原型

int sum(int a , int b);

函数指针

int (*pfun) (int,int) = sum;

这条语句声明了一个函数指针pfun,它存储函数的地址。该语句还用sum()函数的地址初始化pfun。要提供初始值,只需要使用有所需原型的函数名。
注意:像使用函数名那样使用函数指针名调用该指针指向的函数,不需要取消引用运算符。

9.1.3函数指针的数组

如果需要的是数组的地址,只要使用数组名即可,同样,如果需要的是函数的地址,只要使用函数名即可。

  • 如何通过指针调用函数

指针变量的声明
指针数组变量的声明

数组的所有元素的类型都是相同的
函数指针数组的的所有元素都是相同的类型

数据数组
可以在声明中初始化指针数组的所有的元素
大括号中的初始值的个数确定了数组中的元素的数目
函数指针数组的初始化列表
数组的初始化列表的
函数调用的方式一样

9.1.4作为变元的函数指针

要声明函数指针数组,只需要将数组的大小放在函数指针数组名之后:

int (*pfunctions[10]) (int);

9.2 函数中的变量

  • 简单 、简化开发程序的过程,

9.2.1 静态变量:函数内部的追踪

  • 自动变量:
  • 静态变量:只要程序开始执行,静态变量就一直存在,但是他只能在声明它的范围内可见。不能在该作用域的外部引用
  • 所有的静态变量都会初始化为0,除非将它们初始化为其他值。
    静态变量的用法。

声明一个静态变量

static int count = 0;
  • 注意可以在函数内创建任意类型的静态变量。

静态变量和自动变量的不同
(一)静态变量在函数的作用域内定义,但当执行退出静态变量后,这个静态变量不会销毁。
(二)自动变量每次进入作用域时,都会初始化一次,但是声明为static的变量只能在程序开始时初始化一次。
静态变量只能在包含其声明的函数中可见

9.2.2在函数之间共享变量

  • 全局变量是由位置决定的。只要可以在任意位置访问,就是全局变量

注意:
在c语言中,最好不要给本地变量和全局变量使用相同的名称。虽然合法,但是缺容易出错。

9.3 调用自己的函数:递归(recursion)

(factorial)

函数调用自己称为递归
递归在程序设计中不常见,但是他是一个效率很高的技巧
问题:如何停止递归过程

void Looper(void)
{
   printf("Looper function called.\n");
   Looper();
}

代码中没有停止该过程的机制。
一个调用自己的函数必须包含停止处理的方式

9.4 变元个数可变的函数

  • 编写参数个数可变的函数时,第一个明显的问题是如何指定它的原型。
double average(double v1, double v2, ... );

在前两个固定的变元后面,可以有数量可变的变元

  • 在编写函数时如何引用变元?
    唯一的方法是通过指针间接地指定变元
    <stdarg.h>头文件为此提供了通常实现为宏的例程,

要实现变元个数可变的函数,必须同时使用3个宏:
va_start() 、 va_arg() 、 va_end()

第一个宏的形式如下:

void  va_start(va_list  parg , list_fixed_arg);

这个宏的名称variable argument start
这个函数接受两个变元:va_list类型的指针和为函数指定的最后一个固定参数的名称,
va_list类型:用于存储支持可变参数列表的例程所需信息

double average(double v1, double v2, ...)
{
    va_list parg;
    //More code to go here....
    va_start (parg,  v2);
     //More code to go here...
}

调用va_start()的结果是将变量parg设定为指向传送函数的第一个可变变元。

如何访问每个可变变元值

//function  to calculate the average of two or more arguments
double average (double v1,double v2,...)
{
   va_list  parg;
   double sum = v1+v2;
   double value = 0.0;
   int count = 2;

    va_start  (parg ,v2)
    while((value = va_arg(parg,double)) != 0.0)
    {
         sum += value;
         ++count;
    }
    va_list (parg);
     return sum/count;
}
while((value = va_arg(parg,  double)) != 0.0)

循环条件调用了<stdarg.h>头文件中的另一个函数va_arg(),
va_arg()函数的第一个变元是通过调用va_start()初始化的变量parg,
第二个变元是期望确定的变元类型的说明。
va_arg()函数会返回parg指定的当前变元值,并将它存储到value中,同时会更新parg指针,使之根据调用中指定的类型,指向列表中的下一个变元。
必须有某种方式来确定可变变元的类型。

9.4.1 复制va_list

va_list parg_copy;
va_copy(parg_copy, parg);

9.4.2 长度可变的变元列表的基本规则

变元数目可变的函数的基本规则

  • 在变元数目可变的函数中,至少要有一个固定变元
  • 必须调用va_start()初始化函数中可变变元列表指针的值。变元指针的类型必须声明为va_list类型
  • 必须有确定每个变元的类型的机制
  • 必须有确定何时终止变元列表的方法。
  • va_arg() 的第二个变元指定了变元值得类型。这个指针类型可以在类型名的后面加上*来指定
  • 在退出变元数目可变的函数前,必须调用va_end();

9.5 main()函数

main()函数可以有两个参数,也可以没有参数

//Program 9.8 A program to list the command line arguments
#include <stdio.h>
int main(int argc, char *argv[0])
{
    printf("Program name: %s\n",argv[0]);
    for(int i =1 ; i < argc ; ++i)
         printf("Argument %d: %s\n", i , argv[i]);
    return 0;
}

argc的值至少是1,因为执行程序时,必须输入程序的名称。argv[0]是程序名称。

int arg_value = 0;
if(argc > 1)
    arg_value = atoi(argv[1]);
else
{
   printf("Command line argument missing.");
   return 0;
}

9.6 结束程序

stdlib.h头文件提供的几个函数可以用于终止程序的执行。标识出在程序正常结束时要调用的一个或多个自定义函数。

9.6.1 abort()函数

abort();

9.6.2 exit()和atexit()函数

exit(EXIT_SUCCESS);

如果变元是EXIT_FAILURE,就把表示终止不成功的消息返回给主机环境。无论如何,exit()都会清空所有的输出缓冲区,把它们包含的数据写入目的地,再关闭所有打开的流,之后把控制权返回给主机环境。

调用atexit()会标识应用程序终止时要执行的函数。

void CleanUp(void)
···
if(atexit(CleanUp))
   printf("Registration of  function failed!\n");

把要调用的函数名作为变元传递给atexit(),如果注册成功,就返回0,否则返回非0 值。调用几次atexit(),就可以注册几个函数,且注册函数最多为32个。把几个函数注册为调用exit()时执行,它们就在程序终止时,以注册顺序的倒序调用。即调用atexit()注册的最后一个函数最先执行。

9.6.3 _Exit()函数

_Exit(1);

会正常的终止程序,并把变元值返回给主机环境,区别是它无法影响程序终止时调用_Exit()函数的结果,因为它不调用任何已经注册的函数

9.6.4 quick_exit()和at_quick_exit()函数

9.7 提高性能

有3个工具可以使编译器生成性能更加的代码。

9.7.1内联声明函数

短函数的每次调用可以用实现该函数的内联代码替代,以提高执行性能。

inline double bmi(double kg_wt, double m_height)
{
   return kg_wt/(m_height*m_height);
}

9.7.2使用resteict关键字

errno_t strcpy_s(char * restrict s1, rsize_t  slmax, const char * restrict s2)
{
      //     
}

9.7.3_Noretrun函数限定符

永远不返回函数

_Noreturn void EndAll(void)
{
    //Tidy up open files ...
    exit(EXIT_SUCCESS);
}

相关文章

  • 第九章函数的再探

    9.1 函数指针 函数的内存地址存储了函数的开始执行的位置,存储在函数指针中的内容就是这个地址。 9.1.1 声...

  • 再探JS函数

    函数是JS的核心概念,主要用来封装语句,需要注意一下 细碎的小点 return语句,一旦return,后续的语句不...

  • 再探Swift函数的派发方式

    Swift 的函数是怎么派发的呢? 我没能找到一个很简明扼要的答案, 但这里有四个选择具体派发方式的因素存在: 声...

  • for循环再探

    for循环的表达式一般如下: for(表达式1;表达式2;表达式3){ 表达式4; } 例如:for( i=0...

  • ByteBuf再探

    什么是ByteBuf ByteBuf是Netty中非常重要的一个组件,他就像物流公司的运输工具:卡车,火车,甚至是...

  • 再探React

    早在一年前,我就很蠢发表过一篇关于React的文章,这篇文章主要根据React官方的介绍初步介绍一下React,并...

  • 再探JavaScript

    一、数据类型二、表达式和运算符三、语句四、对象五、数组六、函数七、This八、闭包和作用域九、OOP十、正则和模式...

  • 再探雪景

    挥洒得随性 飘逸得纯净 就像一个 性情中人 活到最后才明白 那些装腔作势 那些别人的眼神 最终都会归零 自己让自己...

  • 再探老人

    也许会有那么一天 冰冷的金属,趴在我的身上 撩拨着我的心脏

  • 再探仙湖

    再次重逢, 你身边的百花已落尽, 风还是那阵风, 水还是那湖水, 只是细细去闻, 却没了那股春香, 多了些岁月的印...

网友评论

      本文标题:第九章函数的再探

      本文链接:https://www.haomeiwen.com/subject/lnuojttx.html