web-dev-qa-db-ja.com

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

この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ですか?

66
user34537

あなたが尋ねている質問は、実際には1つではなく2つの質問です。これまでのほとんどの返信は、全体を包括的な「これはK&Rスタイルです」という回答でカバーしようとしていましたが、実際には、C&Rスタイルと呼ばれるものとはまったく関係がありません(C言語全体を見ない限り)。何らかの方法で「K&Rスタイル」として:)

最初の部分は、関数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の宣言を省略できません)。

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では、最初に使用する前に関数を宣言する必要がありますが、宣言はプロトタイプである必要はありません。混乱は明らかに、人気のある用語の取り違えに起因します。実際、「関数宣言」は「プロトタイプ」と同じではありませんが、多くの人は関数宣言を「プロトタイプ」と呼びます。

142
AnT

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

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

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

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

17
Dirk

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

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

4
unwind

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

現在のC言語で関数プロトタイプを使用する方がはるかに便利です。
あなたも しなければならない それらをC99で使用します(C89はまだ古い構文を受け入れます)。

また、C99では関数を宣言する必要があります(おそらくプロトタイプなし)。新しい関数を最初から作成する場合は、宣言を提供する必要があります...プロトタイプにすることもできます。何も失うことはなく、コンパイラーから余分なチェックを受けることができます。

1
pmg

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

1
Robert Gamble