C 语言中的变参数函数

如果你曾经使用过 C 语言编程,你可能想知道标准函数如 printf() 和 scanf() 如何在函数调用中接受可变数量的参数。这些能够接受可变数量参数的函数被称为变参函数。

你可能会时不时地需要编写一个变参函数,标准库 <stdarg.h> 可以帮助你编写自己的变参函数。

例如下图所示,如果你需要一个能够对 n 个数字进行求和的函数,其中 n 可以是大于1的任意数字,你可能不想编写多个求和函数,而是在这种情况下需要一个变参函数。

int sum_of_ 2_ numbers(int x, int y) {
return x+y;
}

int sum_of_ 3_ numbers(int x, int y, int z) {
return x+y+z;
}

int sum_of_4_ numbers(int x, int y, int z, int w) {
return x+y+z+w;
}

理解 <stdarg.h> 中定义的宏函数

在编写自己的变参函数之前,我们需要理解三个宏函数:

  1. va_start(va_list pargs, last)
    • 这个宏接受两个参数。第一个参数是一个声明为va_list类型的变量,它是一个参数指针变量。第二个参数是变参函数接受的最后一个固定参数。
    这个宏将参数指针变量 pargs 初始化为指向变参函数接受的第一个可选参数。
  2. va_arg(va_list pargs, type)
    • 这个宏接受两个参数。第一个参数与va_start()宏中的第一个参数相同。第二个参数指定了由pargs指向的期望的数据类型。
    此函数返回由pargs指向的参数的值,并同时更新pargs以指向列表中的下一个参数。
  3. va_end(va_list pargs)
    • 这个宏结束了对pargs的使用。根据手册,调用va_end(pargs)后,进一步使用带有pargs的va_arg调用可能不起作用。然而,在GNU C库中,va_end什么也不做,因此除了便于移植的原因,您可能不需要使用它。

声明变参函数

变参函数的原型:

return_type func_name(type arg1, …)

最后一个固定参数 arg1 后的省略号表示在最后一个固定参数之后可能接受可变数量的参数。

例如:
void print(char *str, …);
int sum(unsigned int n, …);
double average(unsigned int n, …);
int formatStr(char *dest, const char *formatted_str, …)

注意:以下示例可能不是变参函数的最佳应用,它们仅用于展示变参函数的概念。

示例 1 — 计算双精度值的平均值的变参函数

#include <stdio.h>
#include <stdarg.h>
/* Function prototype for averaging n double-typed numbers */
double average(unsigned int n, ...);
int main() {
   double avg1, avg2;
   avg1 = average(2, 50.3, 49.7);
   avg2 = average(5, 12.45, 2.87, 0.256, 90.1532, 6320.12);
   printf("avg1: %f\navg2: %f", avg1, avg2);
}
double average(unsigned int n, ...) {
   /* pointer to the variable arguments list */
   va_list pargs;
   
   /* Initialise pargs to point to the first optional argument */ 
   va_start(pargs, n);
   
   double sum = 0;
   unsigned int i;
   for(i=0;i<n;i++) {  /* iterate though the optional arguments */
    double x = va_arg(pargs, double);
    sum += x;
   }
   va_end(pargs);
   return (n > 0)? (sum / n):0.00;
}

Output:

avg1: 50.000000
avg2: 1285.169840

示例 2 — 创建格式化的字符串数据

#include <stdio.h>
#include <stdarg.h>
/* Function prototype for creating formatted string */
int formatStr(char *dest, const char *formatted_str, ...);
int main() {
   char f_str[50];
   char name[] = "Johnson";
   double height = 170.43;
   formatStr(f_str, "Name: %s\nHeight: %f", name, height);
}
int formatStr(char *dest, const char *formatted_str, ...) {
   /* pointer to the variable arguments list */
   va_list pargs;
   
   /* Initialise pargs to point to the first optional argument */
   va_start(pargs, formatted_str);
   /* n_chars is the final length of the formatted string */
   int n_chars = vsprintf(dest, formatted_str, pargs);
   
   /* print the result of formatted string */
   puts(dest);
   va_end(pargs);
   return n_chars;
}

Output:

Name: Johnson
Height: 170.430000

总结:

总之,变参函数是一种可以接受可变数量参数的函数,就像C标准库中的printf()和scanf()一样。我们还可以根据需要定义自己的变参函数,只需包含标准库<stdarg.h>即可。该库提供了类似函数的宏以帮助我们编写变参函数。va_list是用于声明指向可选参数的指针的内置变量类型。va_start宏用于初始化va_list变量,将其指向第一个可选参数。va_arg宏用于访问va_list指针指向的可选参数的值,并将其更新为指向下一个可选参数。通过参考上述两个示例代码,您将更清楚地了解变参函数的概念。

感谢阅读,希望您从本文中有所收获。

Leave a Reply

Your email address will not be published. Required fields are marked *