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

人气:865 发布:2022-10-16 标签: function c function-definition kernighan-and-ritchie function-declaration

问题描述

这种 C 语法有什么用处——使用K&R"风格的函数声明?

What is useful about this C syntax — using 'K&R' style function declarations?

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

我能够在 Visual Studios 2010beta 中编写此代码

I was able to write this in Visual Studios 2010beta

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

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

I don't understand. What's the point of this syntax? I can write:

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

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

The only thing it seems to specify is how many parameters it uses and the return type. I guess parameters without types is kind of cool, but why allow it and the int paranName after the function declarator? It's weird.

这还是标准的 C 吗?

Also is this still standard C?

推荐答案

您提出的问题实际上是两个问题,而不是一个.到目前为止,大多数回复都试图用一个通用的这是 K&R 风格"的答案来覆盖整个事情,而实际上只有一小部分与所谓的 K&R 风格有关(除非你看到以一种或另一种方式将整个 C 语言视为K&R 风格":)

The question you are asking is really two questions, not one. Most replies so far tried to cover the entire thing with a generic blanket "this is K&R style" answer, while in fact only a small part of it has anything to do with what is known as K&R style (unless you see the entire C language as "K&R-style" in one way or another :)

第一部分是函数中使用的奇怪语法定义

The first part is the strange syntax used in function definition

int func(p, p2)
void *p;
int  p2; /* <- optional in C89/90, but not in C99 */
{
  return 0;
}

这实际上是一个 K&R 风格的函数定义.其他答案已经很好地涵盖了这一点.事实上,它并没有多少.该语法已弃用,但即使在 C99 中仍然完全支持(C99 中的无隐式 int"规则除外,这意味着在 C99 中您不能省略 p2 的声明).

This one is actually a K&R-style function definition. Other answer have covered this pretty well. And there's not much to it, actually. The syntax is deprecated, but still fully supported even in C99 (except for "no implicit int" rule in C99, meaning that in C99 you can't omit the declaration of p2).

第二部分与 K&R 风格无关.我指的是可以使用交换"参数调用该函数的事实,即在此类调用中不进行参数类型检查.这与 K&R 风格定义本身几乎没有关系,但它与您没有原型的函数有关.你看,在 C 中,当你声明一个这样的函数时

The second part has little to do with K&R-style. I refer to the fact that the function can be called with "swapped" arguments, i.e. no parameter type checking takes place in such a call. This has very little to do with K&R-style definition per se, but it has everything to do with your function having no prototype. You see, in C when you declare a function like this

int foo();

它实际上声明了一个函数foo,该函数接受未指定数量的未知类型参数.你可以称之为

it actually declares a function foo that takes an unspecified number of parameters of unknown type. You can call it as

foo(2, 3);

和作为

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

等等(你懂的);

只有具有正确参数的调用才会工作"(意味着其他调用会产生未定义的行为),但完全由您来确保其正确性.编译器不需要诊断不正确的参数,即使它以某种方式神奇地知道正确的参数类型及其总数.

Only the call with proper arguments will "work" (meaning that the others produce undefined behavior), but it is entirely up to you to ensure its correctness. The compiler is not required to diagnose the incorrect ones even if it somehow magically knows the correct parameter types and their total number.

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

Actually, this behavior is a feature of C language. A dangerous one, but a feature nevertheless. It allows you to do something like this

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.

i.e. mix different function types in a "polymorphic" array without any typecasts (variadic function types can't be used here though). Again, inherent dangers of this technique are quite obvious (I don't remember ever using it, but I can imagine where it can be useful), but that's C after all.

最后,将答案的第二部分链接到第一部分的位.当您进行 K&R 风格的函数定义时,它不会引入该函数的原型.就函数类型而言,您的 func 定义将 func 声明为

Finally, the bit that links the second part of the answer to the first. When you make a K&R-style function definition, it doesn't introduce a prototype for the function. As far as function type is concerned, your func definition declares func as

int func();

即既没有声明类型也没有声明参数的总数.在您的原始帖子中,您说......它似乎指定了它使用了多少参数......".正式地说,它没有!在你的两个参数 K&R 风格的 func 定义之后,你仍然可以调用 func as

i.e. neither the types nor the total number of parameters are declared. In your original post you say "... it seems to specify is how many params it uses ...". Formally speaking, it doesn't! After your two-parameter K&R-style func definition you still can call func as

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

并且不会有任何违反约束的情况.(通常,高质量的编译器会给你一个警告).

and there won't be any constraint violation in it. (Normally, a quality compiler will give you a warning).

另外,一个有时被忽视的事实是

Also, a sometimes overlooked fact is that

int f()
{
  return 0;
}

也是一个 K&R 风格的函数定义,不引入原型.要使其现代",您必须在参数列表中放置一个显式 void

is also a K&R-style function definition that does not introduce a prototype. To make it "modern" you'd have to put an explicit void in the parameter list

int f(void)
{
  return 0;
}

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

Finally, contrary to a popular belief, both K&R-style function definitions and non-prototyped function declarations are fully supported in C99. The former has been deprecated since C89/90, if I remember correctly. C99 requires the function to be declared before the first use, but the declaration is not required to be a prototype. The confusion apparently stems from the popular terminological mix-up: many people call any function declaration "a prototype", while in fact "function declaration" is not the same thing as "prototype".

348