函数声明与原型的替代(K& R)C语法

时间:2009-10-27 12:59:30

标签: c

这个C语法的用处是什么 - 使用'K& R'样式函数声明?

int func (p, p2)
    void *p;
    int  p2;
{
    return 0;
}

我能够在Visual Studios 2010beta中写这个

//yes the params are flipped
void f()
{
    void *v=0;
    func(5,v);
}

我不明白。这种语法有什么意义?我可以写:

int func (p, p2)
    int  p2;
{
    return 0;
}
//and write
int func (p, p2)
{
    return 0;
}

它似乎唯一指定的是它使用了多少参数和返回类型。我想没有类型的参数有点酷,但为什么允许它和函数声明符之后的int paranName?这很奇怪。

这还是标准C吗?

6 个答案:

答案 0 :(得分:141)

你问的问题实际上是两个问题,而不是一个问题。到目前为止,大多数回复试图用一般的毯子覆盖整个事物“这是K& R风格”的答案,而事实上它只有一小部分与所谓的K& R风格有关(除非你看到整个C语言以某种方式作为“K& R-style”:)

第一部分是函数 definition

中使用的奇怪语法
int func(p, p2)
void *p;
int  p2; /* <- optional in C89/90, but not in C99 */
{
  return 0;
}

这个实际上是K&amp; R风格的功能定义。其他答案已经很好地涵盖了这一点。实际上并没有太多的东西。语法已弃用,但即使在C99中仍然完全支持(C99中的“无隐式整数”规则除外,这意味着在C99中您不能省略p2的声明。)

第二部分与K&amp; R风格没什么关系。我指的是可以用“交换”参数调用该函数,即在这样的调用中不进行参数类型检查。这与K&amp; R风格的定义本身几乎没什么关系,但它与你没有原型的功能有关。当你声明一个像这样的函数时,你会看到在C中

int foo();

它实际上声明了一个函数foo,该函数带有未知类型的未指定数量的参数。您可以将其称为

foo(2, 3);

j = foo(p, -3, "hello world");

如此(你明白了);

只有具有适当参数的调用才会“起作用”(意味着其他人产生未定义的行为),但完全取决于您确保其正确性。即使编译器以某种方式神奇地知道正确的参数类型及其总数,编译器也不需要诊断错误的编译器。

实际上,此行为是C语言的功能。一个危险的,但仍然是一个功能。它允许你做这样的事情

void foo(int i);
void bar(char *a, double b);
void baz(void);

int main()
{
  void (*fn[])() = { foo, bar, baz };
  fn[0](5);
  fn[1]("abc", 1.0);
  fn[2]();
}

即。在不使用任何类型转换的“多态”数组中混合不同的函数类型(虽然这里不能使用可变参数函数类型)。同样,这种技术的固有危险是非常明显的(我不记得曾经使用它,但我可以想象它在哪里有用),但毕竟这是C.

最后,将答案的第二部分链接到第一部分的位。当您制作K&amp; R风格的函数定义时,它不会为函数引入原型。就功能类型而言,您的func定义将func声明为

int func();

即。既未声明类型也未声明参数总数。在您的原始帖子中,您说“......它似乎指明了它使用了多少个参数......”。从形式上讲,它没有!在您的双参数K&amp; R风格func定义后,您仍然可以将func称为

func(1, 2, 3, 4, "Hi!");

并且不会有任何约束违规。 (通常,质量编译器会给你一个警告)。

此外,有时被忽视的事实是

int f()
{
  return 0;
}

也是K&amp; R风格的函数定义,不引入原型。要使其“现代化”,您必须在参数列表

中加上明确的void
int f(void)
{
  return 0;
}

最后,与流行的看法相反,C99完全支持K&amp; R风格的函数定义和非原型函数声明。如果我没记错的话,前者自C89 / 90以来就被弃用了。 C99要求在第一次使用之前声明函数,但声明不需要是原型。混乱显然源于流行的术语混淆:许多人将任何函数声明称为“原型”,而实际上“函数声明”与“原型”不同。

答案 1 :(得分:17)

这是非常古老的K&amp; R C语法(早于ANSI / ISO C)。现在,你不应再使用它了(因为你已经注意到它的主要缺点:编译器不会为你检查参数的类型)。在您的示例中,参数类型实际默认为int

当时使用了这种语法,有时会找到像

这样的函数
foo(p, q) 
{
    return q + p;
}

实际上是一个有效的定义,因为foopq的类型默认为int

答案 2 :(得分:4)

这只是一种旧语法,它可以提供您可能更熟悉的“ANSI C”语法。它通常被称为“K&R C”。

编译器支持它完整,当然也能够处理旧的代码库。

答案 3 :(得分:1)

这是C没有功能原型的遗物。当时的方式,(我认为)函数被假定为返回int,并且其所有参数都被假定为int。没有检查功能参数。

在当前的C语言中使用函数原型会好得多。
必须在C99中使用它们(C89仍然接受旧的语法)。

C99需要声明函数(可能没有原型)。如果你是从头开始编写一个新函数,你需要提供一个声明......使它成为一个原型:你什么都不会丢失并从编译器中获得额外的检查。

答案 4 :(得分:1)

这是1989年C标准化之前的原始K&amp; R语法.C89引入了函数原型,借用了C ++,并弃用了K&amp; R语法。在新代码中没有理由使用它(并且有很多理由)。

答案 5 :(得分:-1)