【问题标题】:Use the parameters of a function that takes any number of parameters, in C在 C 中使用带有任意数量参数的函数的参数
【发布时间】:2013-09-13 18:05:24
【问题描述】:

我刚刚读到:"C void arguments" 关于 C 中这些函数定义之间的区别:

int f(void)

int f()

理解了第二种形式意味着一个返回整数的函数,它接受任意数量的参数,我想知道我们如何才能真正访问和使用这些未知参数

我很想获得示例代码和解释。

另外,我知道 C 中 Varargs 的机制(带有 va_argva_endva_start 函数),并且很高兴听到 这种机制和 f() 形式之间的区别 上面提到过。

非常感谢!

【问题讨论】:

  • 这些是函数声明,而不是定义;你忘记了分号(即它是int f(); 而不仅仅是int f())。

标签: c parameters variadic-functions


【解决方案1】:

第二个版本接受可变数量的参数,它接受固定(但未指定)的参数序列。实际上,它声明了函数,但没有原型函数。因此,不会检查对函数的调用是否有有效类型。编译器通常会查看对函数的第一次调用,并根据第一次调用中找到的类型检查其他调用。

这就是为什么如果省略#include <stdio.h>,第一次调用printf 是可以接受的,但是任何不同类型的printf 调用都会产生错误。例如。

int main() {
    printf("hello");        //<--- accepted, compiler may assume `int printf(char *);`
    printf("%s", "world");  //<--- error, type mismatch
}

要让函数接受可变数字,它必须至少有一个固定参数,然后是标记...

int f (int first, ...);

您需要包含stdarg.h 头文件。并且该函数可以使用宏访问参数。

void f (int c,...){
    va_list ap;
    va_start(ap, c);
    for(;c--;)
        printf("%d", va_arg(ap,int));
    va_end (ap);
}

将固定参数作为剩余参数的计数很方便。您还需要以某种方式确定每个参数的类型。在本例中,假设它们都是int

【讨论】:

  • 我将其称为“固定(未指定)参数序列而不是集合参数(顺序显然很重要!)。
【解决方案2】:
 int f(void);

f 声明为不带参数并返回int 结果的函数,而

 int f();

f 声明为采用固定但未指定数量和类型的参数的函数(当然,返回int 结果)。

鉴于这样的声明,必须有一个定义在某处实际定义参数。如果不带参数,可能定义为:

int f() {
    return 42;
};

如果它需要2个int参数,它可能被定义为:

int f(int x, int y) {
    return x + y;
}

任一定义与int f();兼容,但只有第一个与int f(void)兼容。

一个指定参数类型的函数声明,或者,作为一种特殊情况,使用void 关键字来指定没有参数,是一个原型执行此操作的函数声明,例如int f(); 是旧式声明。

除非您使用不支持原型的具有数十年历史的 pre-ANSI 编译器,或者您正在维护非常旧的代码并且没有时间或其他资源来更新它,否则 没有理由不使用原型。如果你调用一个没有可见原型的函数,你仍然需要传递正确数量和类型的参数;不同的是,如果调用不正确,编译器将无法告诉你。

(您可以定义一个接受可变数量和类型参数的函数;printf 就是一个例子。这是使用, ... 表示法完成的。函数本身使用&lt;stdarg.h&gt; 中定义的特性来处理论点。但这与您问题中显示的声明无关。)

这是一个使用过时的旧式函数声明和定义的小程序:

#include <stdio.h>
#include <stdlib.h>

int add();

int main(argc, argv)
int argc;
char **argv;
{
    if (argc == 3) {
        /* NOTE: atoi() does no error checking */
        printf("sum = %d\n", add(atoi(argv[1]), atoi(argv[2])));
    }
    else {
        fprintf(stderr, "Usage: %s x y\n", argv[0]);
        exit(EXIT_FAILURE);
    }
}

int add(x, y)
int x, y;
{
    return x + y;
}

请注意,如果我写了add(1, 2, 3, 4)add("foo", "bar"),编译器就不会发现错误;该程序只是行为不端。

这是一个使用现代函数声明和定义的等效程序:

#include <stdio.h>
#include <stdlib.h>

int add(int x, int y);

int main(int argc, char **argv) {
    if (argc == 3) {
        /* NOTE: atoi() does no error checking */
        printf("sum = %d\n", add(atoi(argv[1]), atoi(argv[2])));
    }
    else {
        fprintf(stderr, "Usage: %s x y\n", argv[0]);
        exit(EXIT_FAILURE);
    }
}

int add(int x, int y) {
    return x + y;
}

可见的原型意味着编译器能够诊断不正确的调用。

【讨论】:

  • 我很好奇,如果编译器不知道函数的参数类型,它如何使用正确的调用约定。
  • @newacct:它没有。给定一个没有可见原型的调用,编译器假定提升的参数与定义的参数类型匹配。如果该假设无效,则该调用具有未定义的行为。这就是为什么您应该始终使用原型。
【解决方案3】:

int f(void) 表示返回不带参数的 int 的函数。

你可以看这个例子来看看使用 int f() 的结果

#include <stdio.h>

void f();

int main() {
  f(); /* prints some garbage */
  f(16);  /* prints 16 */ 
  return 0;
}

void f(a1)
  int a1;
{
   printf("%d\n", a1);
}

还有这个:-

#include <stdio.h>
    #include <stdlib.h>
    int f();
    int f(int x) {
        return x;
    }
    int main (int argc, char *argv[]) {
         printf ("%d\n", f(atoi(argv[1])));
         return 0;
    }

pax> gcc -Wall --std=c99 -o qq qq.c
pax> ./qq 42
42

【讨论】:

  • 恰好在您的机器上执行此操作。一般来说,它是undefined behavior 特别是f(17,0) 调用!!
  • 为声明void f(void)的情况添加示例代码,你的答案将是完美的:-)
  • 谢谢!我从未见过这种语法:“void f(a1) int a1;”。它是针对这种特定用途的特殊语法吗?
  • @Reflection 这是一种已弃用的 K&R 风格。常见于 1995 年之前的编程书籍中。
【解决方案4】:

关于f() 形式: click.

一个例子,一个类似 printf 的函数,可以将十进制数字或字符串打印到文件描述符中:

#include <stdarg.h>
/* Formatted printing into a file descriptor */                          
int printfd(int fd, char *fmt, ...)                                      
{                                                                        
    int bytes = 0;                                                   
    int i_val = 0;                                                   
    va_list va;                                                      
    va_start(va, fmt);                                               
    while (*fmt) {                                                   
            char *perc = strchr(fmt, '%');                           
            int len = perc == NULL ? strlen(fmt) : perc - fmt;       
            if (len) {                                               
                    bytes += write(fd, fmt, len);                    
                    fmt += len;                                      
            } else {                                                 
                    fmt = perc + 1;                                  
                    if (*fmt == 0)                                   
                            continue;                                
                    else if (*fmt == '%')                            
                            bytes += write(fd, fmt, 1);              
                    else if (*fmt == 'd')                            
                            bytes += writedec(fd, va_arg(va, int));  
                    else if (*fmt == 's')                            
                            bytes += writestr(fd, va_arg(va, char*));
                    fmt++;                                           
            }                                                        
    }                                                                
    va_end(va);                                                      
    return bytes;                                                    
}                                                                        

【讨论】:

  • 不,这不能回答问题!
  • @BasileStarynkevitch - Au Contraire。这篇文章的标题表明提问者想知道如何在 C 语言中使用带有任意数量参数的函数的参数。也许后续问题会混淆最初陈述的意图,但这篇文章仍然很好地回答了它。 +1 给 SzG。
猜你喜欢
  • 1970-01-01
  • 2015-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-20
  • 1970-01-01
相关资源
最近更新 更多