【问题标题】:What happens to the unspecified arguments in function()? [duplicate]function() 中未指定的参数会发生什么? [复制]
【发布时间】:2020-05-25 20:43:47
【问题描述】:

我一直在阅读difference between function() and function(void) in C,我才知道

函数中的空参数列表声明表示 函数采用未指定个参数

所以I ran this code:

#include <stdio.h>

void fun();

int main(void)
{
    fun(12, 13.22, 1234567890987654321, "wow", 'c');
}

void fun()
{
    printf("What happened to those arguments?");
}

我不明白为什么 C 允许这样做。到目前为止,我读过的所有与此相关的帖子都说使用它是不好的做法,它已经过时等。同样从上面的代码中,我认为这些参数的类型也未指定。所以我只想知道“未指定类型的未指定参数”背后的原因:

  • 可以用这些参数做什么?

  • 是否可以在函数中访问这些参数?

【问题讨论】:

  • C11 标准草案 n1570:6.5.2.2 函数调用 6 如果表示被调用函数的表达式的类型不包含原型,[...] 如果参数的数量不等于参数的数量,行为未定义。
  • 在声明时,参数的数量(和类型)是未指定的。然而,函数的使用必须与其定义的数量(和类型)相对应。 int foo(); /*declaration*/ int main(void) { foo(42) /*UB*/; foo(3.14159); return 0; } /*definition*/ int foo(double x) { return x; } ==> 缩进 ==> ideone.com/4zzlTV

标签: c function


【解决方案1】:

支持该符号的原因是历史性的。在第一个 C 标准 (C89/C90) 之前,您不能在 C 中使用原型;原型是标准 C 的最大和最重要的特性之一。因此,所有函数声明都以“空括号”样式编写(当它们被编写时;大多数返回 int 的函数根本没有声明)。在 C89/C90 中也添加了 void 类型,尽管一些编译器在标准最终确定之前支持它。

因为对于 C89/C90 的成功而言,现有代码大部分应该继续工作至关重要,因此标准必须允许空括号样式。因此,您的代码可能是用准标准 C 编写的:

#include <stdio.h>

int fun();  /* This declaration would probably have been absent */

int main(void)
{
    fun(12, 13.22, 1234567, "wow", 'c');
    return 0;   /* This was required until C99 to give reliable exit status */
}

fun(i, d, l, s, c)      /* No return type - implicitly returns int */
long l;                 /* Defined out of sequence - bad style, but legal */
char c;                 /* Passed as int; converted to char in function */
char *s;                /* Should define all pointer arguments */
double d;               /* No definition of i; it was an int by default */
{
    printf("This is what happened to those arguments:\n");
    printf("i = %d\n", i);
    printf("d = %f\n", d);
    printf("l = %ld\n", l);
    printf("s = [%s]\n", s);
    printf("c = %c\n", c);
    /* No return statement - don't use the value from the function */
}

出于好奇:你可以在函数定义中省略char *s; 行,它仍然编译并产生相同的输出。不过,尝试这样做是个坏主意。您可以将int fun(); 行替换为static fun();,并且在没有请求诊断时代码编译干净。

即使您现在使用 GCC 9.3.0 编译此文件 (old31.c),您也不会收到警告:

$ gcc -std=c90 -o old31 old31.c
$

您所写的示例绕过了向后兼容性规定。使用void 意味着它是新代码(它在许多准标准C 编译器中无效,因为它使用了void)。并且新代码不应无故利用向后兼容条款。在 1991 年和当前的千禧年都是如此(但在 1991 年,有更多充分的理由来利用向后兼容条款)。良好的准标准代码通常按使用顺序列出所有参数。省略定义和乱序定义并不完全令人满意。

你问:

  • 可以用这些参数做什么?

在问题的代码中,不能对参数进行任何操作。调用者将值压入堆栈,并在函数返回时将它们弹出。被调用的函数不知道它们的存在并且不能对它们做任何事情。

  • 是否可以在函数中访问这些参数?

否——不使用任何标准机制。

【讨论】:

    【解决方案2】:

    当参数列表为空时,函数声明和函数定义是有区别的。

    C standard 的第 6.7.6.3p14 节指出:

    标识符列表仅声明参数的标识符 功能。函数声明器中的空列表是 该函数定义的一部分指定 函数没有参数。函数中的空列表 不属于该函数定义的声明符 指定没有关于数量或类型的信息 提供参数。

    这意味着这个声明:

    void fun();
    

    表示fun 采用未知数量的参数。虽然这个定义:

    void fun()
    {
        printf("What happened to those arguments?");
    }
    

    表示fun 不带参数。所以这个函数调用:

    fun(12, 13.22, 1234567890987654321, "wow", 'c');
    

    无效并调用undefined behavior,因为调用中的参数数量与实际参数数量不匹配。这在第 6.5.2.2p6 节中关于函数调用运算符() 进行了详细说明:

    如果表示被调用函数的表达式有 不包含原型的类型,整数促销 对每个参数执行,并且参数具有 类型 float 提升为 double。这些被称为 default 论据促销。如果参数的数量不等于 参数的数量,行为未定义。如果 函数是用包含原型的类型定义的,并且要么 原型以省略号 (, ...) 或 提升后的参数与类型不兼容 参数,行为未定义。如果函数是 使用不包含原型的类型定义,并且 提升后的参数类型不兼容 与提升后的参数相比,行为是 未定义,但以下情况除外:

    • 一个提升类型是有符号整数类型,另一个提升类型是对应的无符号整数类型, 并且值可以在两种类型中表示;

    • 这两种类型都是指向字符类型或void的合格或不合格版本的指针。

    至于为什么这是允许的,这是可以追溯到 C 的预标准化版本的遗留行为,其中变量的类型和函数的返回类型默认为 int 和方法的声明函数与现在不同。

    【讨论】:

      猜你喜欢
      • 2018-07-20
      • 2016-02-19
      • 2015-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多