79

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

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

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;
}

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.

Also is this still standard C?

4

5 に答える 5

157

あなたが尋ねている質問は、実際には 1 つではなく 2 つの質問です。これまでのほとんどの回答は、「これは K&R スタイルです」という一般的なブランケットの回答で全体をカバーしようとしましたが、実際には、K&R スタイルとして知られているものと関係があるのはごく一部にすぎません (C 言語全体を見ない限り)。ある意味で「K&Rスタイル」として:)

最初の部分は、関数定義で使用される奇妙な構文です

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)。

2 番目の部分は、K&R スタイルとはほとんど関係ありません。関数は「交換された」引数で呼び出すことができるという事実に言及します。つまり、そのような呼び出しではパラメーターの型チェックは行われません。これは、K&R スタイルの定義自体とはほとんど関係ありませんが、プロトタイプを持たない関数とはすべて関係があります。ほら、Cでこのような関数を宣言すると

int foo();

実際には、不明な型の不特定数のパラメータをfoo取る関数を宣言しています。次のように呼び出すことができます

foo(2, 3);

そして

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

ansなど(アイデアはわかります)。

適切な引数を指定した呼び出しのみが「機能」しますが (つまり、他の引数は未定義の動作を生成します)、その正確性を保証するのは完全にあなた次第です。コンパイラは、何らかの方法で正しいパラメーターの型とその総数を魔法のように知っていても、間違ったものを診断する必要はありません。

実は、この振る舞いは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 です。

最後に、回答の 2 番目の部分を最初の部分にリンクするビットです。K&R スタイルの関数定義を作成すると、関数のプロトタイプが導入されません。関数型に関する限り、func定義は次のように宣言funcします

int func();

つまり、型もパラメータの総数も宣言されていません。元の投稿では、「...使用するパラメーターの数を指定しているようです...」と言っています。正式にはそうではありません!2 パラメーターの K&R スタイルの定義の後でも、次のようにfunc呼び出すことができます。func

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

制約違反はありません。(通常、高品質のコンパイラは警告を表示します)。

また、見過ごされがちな事実として、

int f()
{
  return 0;
}

も、プロトタイプを導入しない K&R スタイルの関数定義です。「モダン」にするにvoidは、パラメーターリストに明示的に配置する必要があります

int f(void)
{
  return 0;
}

最後に、一般的な考えに反して、K&R スタイルの関数定義とプロトタイプ化されていない関数宣言の両方が C99 で完全にサポートされています。私の記憶が正しければ、前者は C89/90 以降非推奨になっています。C99 では、最初に使用する前に関数を宣言する必要がありますが、宣言はプロトタイプである必要はありません。この混乱は、一般的な用語の混同に起因しているようです。多くの人は関数宣言を「プロトタイプ」と呼んでいますが、実際には「関数宣言」は「プロトタイプ」と同じものではありません。

于 2009-10-27T15:51:21.477 に答える
18

これはかなり古い K&R C 構文です (ANSI/ISO C より前の日付)。最近では、もう使用しないでください (主な欠点にすでに気づいているためです: コンパイラは引数の型をチェックしません)。あなたの例では、引数の型は実際にはデフォルトintで設定されています。

当時、この構文が使用されていました。

foo(p, q) 
{
    return q + p;
}

foop、およびqデフォルトの の型として、これは実際には有効な定義でしたint

于 2009-10-27T13:02:03.560 に答える
5

これは単なる古い構文であり、使い慣れた "ANSI C" 構文よりも古いものです。通常は「K&R C」と呼ばれます。

もちろん、コンパイラはそれを完全にサポートし、古いコードベースを処理できるようにします。

于 2009-10-27T13:01:44.627 に答える
2

これは、C に関数のプロトタイプがなかった時代の遺物です。当時、(私が思うに) 関数は returnintであると想定され、そのすべての引数は であると想定されていintました。関数パラメーターのチェックは行われませんでした。

現在の C 言語で関数プロトタイプを使用する方がはるかに優れています。
そして、それらを C99 で使用する必要があります (C89 はまだ古い構文を受け入れます)。

また、C99 では関数を宣言する必要があります (おそらくプロトタイプなしで)。新しい関数をゼロから作成している場合は、宣言を提供する必要があります...これもプロトタイプにします。失うものは何もなく、コンパイラから追加のチェックが得られます。

于 2009-10-27T13:06:22.353 に答える
1

これは、1989 年に C が標準化される前のオリジナルの K&R 構文です。C89 では、C++ から借用した関数プロトタイプが導入され、K&R 構文が非推奨になりました。新しいコードでそれを使用する理由はありません (そして使用しない理由はたくさんあります)。

于 2009-10-27T13:08:26.763 に答える