C语言-第34课 - 可变参数分析与宏分析

第34课 - 可变参数分析与宏分析

 

问题引入;如何编写一个可以计算n个数平均值的函数?

下面是一个将所求的元素组成数组,遍历求和的方法:

#include <stdio.h>

float func(int array[], int size)

{

    int i = 0;

    float avr = 0;

    for(i=0; i<size; i++)

    {

        avr += array[i];

    }    

    return avr / size;

}

int main()

{

    int array[] = {1, 2, 3, 4, 5};    

    printf("%f\n", func(array, 5));    

    return 0;

}

这里的不方便就是,我们必须把我们要求平均值的数组成一个数组。我们下面看看可变参数的方法:

 

#include <stdio.h>

#include <stdarg.h>

float average(int n, ...) //...表示可变参数

{

    va_list args;   //args就是可变参数的列表

int i = 0;

    float sum = 0;  

    va_start(args, n);   //初始化列表

    for(i=0; i<n; i++)

    {

        sum += va_arg(args, int); //int表示指明类型

    }   

    va_end(args);    

    return sum / n;

}

int main()

{

    printf("%f\n", average(5, 1, 2, 3, 4, 5));

    printf("%f\n", average(4, 1, 2, 3, 4));    

    return 0;

}

 

 

  1. 可变参数

(1) C语言中可以定义参数可变的函数(如printf函数)。这里就像我们的open函数有两种方式。

(2) 参数可变的函数的实现依赖于stdarg.h头文件。

(3) va_list变量与va_startva_endva_arg配合使用能够访问参数值。

 

  1. 可变参数的限制

(1) 可变参数必须从头到尾按照顺序逐个访问,不可以取中间的参数。

(2) 参数列表中至少存在一个确定的命名参数。

(3) 可变参数宏无法判断实际存在的参数的数量。

(4) 可变参数宏无法判断参数的实际类型。

注意:va_arg中如果指定了错误的类型,那么结果是不可预测的。

 

l 小结:
可变参数是C语言提供的一种函数的设计技巧。

l 可变参数的函数提供了一种更方便的函数调用方式。

l 可变参数必须顺序的访问。

l 无法直接访问可变参数裂变中间的参数值。

 

 

  1. 函数VS

(1) 宏是由预处理直接替换展开的,编译器不知道宏的存在。

(2) 函数是由编译器直接编译的实体,调用行为由编译器决定。

(3) 多次使用宏会导致程序代码量增加。

(4) 函数是跳转执行的,因此代码量不会增加。

(5) 宏的效率比函数的高,因为是直接展开,无调用开销。

(6) 函数调用时会创建活动记录,效率不如宏。

例:

#include <stdio.h>

#define RESET(p, len) while( len > 0) ((char*)p)[--len] = 0  //

void reset(void* p, int len)   //函数

{

    while( len > 0 )

    {

        ((char*)p)[--len] = 0;

    }

}

int main()

{

    int array[] = {1, 2, 3, 4, 5};

    int len = sizeof(array);

int i;

for(i=0;i<5;i++)

{

printf("%d ",array[i]);

}

    printf("\n");

    reset(array, len);

for(i=0;i<5;i++)

{

printf("%d ",array[i]);

}

printf("\n");

    //RESET(array, len);

    return 0;

}

运行结果:1 2 3 4 5

          0 0 0 0 0

reset(array, len);改为RESET(array, len);运行结果一样。

我们使用gcc -E test.c -o test.i可以得到编译的中间程序test.i 这里面我们能看到宏变量被转换成了函数中的代码。减少了进退栈的开销。

 

  1. 宏的有点和缺点

宏的效率比函数的高,但是它的副作用巨大,容易出错。

#include <stdio.h>

#define ADD(a, b) a + b

#define MUL(a, b) a * b

#define _MIN_(a, b) ((a) < (b) ? (a) : (b))

int main()

{

    int i = 1;

    int j = 10;    

    printf("%d\n", MUL(ADD(1, 2), ADD(3, 4)));

    printf("%d\n", _MIN_(i++, j));    

    return 0;

}

运行结果:11

  2

分析:这和我们分析的21,1结果不同,我们将宏展开发现了这两条语句:printf(“%d\n”,1 + 2 * 3 +4);printf(“%d\n”,((i++) < (j) ? (i++) : (j)));这就是宏的缺点,若是我们在第一条语句中加括号,会有效果。对于第二条语句就无法解决。

 

  1. 函数的有点和缺点

函数存在实参到形参的传递,因此没有副作用,但是函数需要创建活动对象,效率受影响。

#include <stdio.h>

int add(int a, int b)

{

    return a + b;

}

int mul(int a, int b)

{

    return a * b;

}

int _min_(int a, int b)

{

    return a < b ? a : b;

}

int main()

{

    int i = 1;

    int j = 10;    

    printf("%d\n", mul(add(1, 2), add(3, 4)));

    printf("%d\n", _min_(i++, j));

    return 0;

}

运行结果:21

  1

 

  1. 宏无可代替的优势

宏参数可以是任何C语言的实体。

宏编写的_MIN_参数类型可以是intfloat等等。

宏的参数可以是类型名。

#include <stdio.h>

#include <malloc.h>

#define MALLOC(type, n) (type*)malloc(n * sizeof(type))

int main()

{

    int* p = MALLOC(int, 5);   

    int i = 0;   

    for(i=0; i<5; i++)

    {

        p[i] = i + 1;   

        printf("%d ", p[i]);

    }

printf("\n");

    free(p);

    return 0;

}

运行结果:1 2 3 4 5

 

   

小结:

l 宏和函数并不是竞争对手。

l 宏能够接受任何类型的参数,效率高,易出错。

l 函数的参数必须是固定类型,效率稍低,不易出错。

l 宏可以实现函数不能实现的功能。

posted @ 2018-10-21 20:27  free-锻炼身体  阅读(190)  评论(0编辑  收藏  举报