1. ホーム
  2. c

関数宣言とプロトタイプの代替(K&R)C構文

2023-08-30 18:37:05

質問

これのどこが便利なのか C 構文 - 'K&R'スタイルの関数宣言を使用すること?

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

Visual Studios 2010betaで書けました。

// yes, the arguments 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なのでしょうか?

どのように解決するのですか?

あなたが質問していることは、実際には 1 つではなく 2 つの質問です。これまでのところ、ほとんどの回答は、一般的な包括的な "これは K&R スタイルです" という回答で全体をカバーしようとしていますが、実際には K&R スタイルとして知られているものと関係があるのはごく一部です (C 言語全体をある意味で "K&R-style" として捉えていないのでしたら......ですが)。

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

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

これは実際にK&Rスタイルの関数定義です。他の回答がこれをかなりカバーしています。そして、実際にはそれほど多くのことはありません。この構文は非推奨ですが、C99でもまだ完全にサポートされています(C99の"no implicit int"ルールを除いて、つまり、C99では p2 ).

2番目の部分はK&Rスタイルとはあまり関係がありません。私は、関数が引数を入れ替えた状態で呼び出される可能性があるという事実を指しています。これは、K&Rスタイルの定義とはあまり関係がありませんが、プロトタイプを持たない関数には関係があります。C言語では、次のような関数を宣言します。

int foo();

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

foo(2, 3);

というように

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

といった具合です(おわかりになりますよね)。

適切な引数を持つ呼び出しだけが、quot;work" (つまり、他のものは未定義の動作を生成します) ですが、その正しさを保証するのは完全にあなた次第です。コンパイラーは、たとえ魔法のように正しい引数の型とその合計数を知っていたとしても、不正確なものを診断することは要求されません。

実際、この動作は 機能 であり、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]();
}

つまり、型キャストなしでquot;polymorphic"配列に異なる関数型を混ぜることができます(ただし、可変長関数型はここでは使えません)。繰り返しますが、この手法の本質的な危険性は非常に明白です(私はこれを使った記憶がありませんが、これが有用である場所を想像することはできます)。

最後に、答えの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では、最初に使用する前に関数を宣言する必要がありますが、その宣言はプロトタイプである必要はありません。多くの人が関数宣言をプロトタイプと呼びますが、実際には関数宣言とプロトタイプは同じものではありません。