第九章函数的再探

2017-12-22  本文已影响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 静态变量:函数内部的追踪

声明一个静态变量

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, ... );

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

要实现变元个数可变的函数,必须同时使用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 长度可变的变元列表的基本规则

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

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);
}
上一篇 下一篇

猜你喜欢

热点阅读