web-dev-qa-db-ja.com

Cで関数プロトタイプを宣言する必要がありますか?

私はCに少し慣れています(以前のJava、C#、およびC++の経験があります)。 Cでは、関数プロトタイプを宣言する必要がありますか、それなしでコードをコンパイルできますか?そうするのは良いプログラミング習慣ですか?それとも、単にコンパイラに依存していますか? (私はUbuntu 9.10を実行しており、GNU Cコンパイラー、またはgccをCode :: Blocks IDEで使用しています)

53
Mohit Deshpande

ANSI C(C89またはC90を意味する)では、関数プロトタイプを宣言する必要はありません。ただし、それらを使用することをお勧めします。標準でこれらを使用できない唯一の理由は、非常に古いコードとの下位互換性のためです。

プロトタイプがなく、関数を呼び出すと、コンパイラーは関数に渡すパラメーターからプロトタイプを推測します。後で同じコンパイル単位で関数を宣言する場合、関数のシグネチャがコンパイラが推測したものと異なると、コンパイルエラーが発生します。

さらに悪いことに、関数が別のコンパイルユニットにある場合、コンパイルエラーを取得する方法はありません。プロトタイプがないとチェックする方法がないからです。その場合、コンパイラがそれを間違えた場合、関数の呼び出しが関数が予期するものとは異なる型をスタックにプッシュすると、未定義の動作が発生する可能性があります。

慣例では、関数を含むソースファイルと同じ名前のヘッダーファイルでプロトタイプを常に宣言します。

C99またはC11では、標準Cでは、関数を呼び出す前にスコープ内の関数宣言が必要です。多くのコンパイラは、強制しない限り、実際にこの制限を強制しません。

60
user308405

Cの関数のprototypeを宣言する必要はありません。「古い」C(C89/90を含む)または新しいC(C99 )。ただし、関数宣言に関しては、C89/90とC99には大きな違いがあります。

C89/90では、関数を宣言する必要はまったくありませんでした。関数が呼び出しの時点で宣言されていない場合、コンパイラーは呼び出しで渡された引数の型から暗黙的に宣言を「推測」(差し戻し)、戻り型がintであると想定します。

例えば

int main() {
  int i = foo(5); 
  /* No declaration for `foo`, no prototype for `foo`.
     Will work in C89/90. Assumes `int foo(int)` */

  return 0;
}

int foo(int i) {
  return i;
}

C99では、呼び出す関数はすべて、呼び出しのポイントの前に宣言する必要があります。ただし、特にprototypeで宣言する必要はありません。非プロトタイプ宣言も機能します。これは、C99では「暗黙のint」ルールが機能しなくなることを意味します(この場合、推論された関数の戻り値の型に対して)。

fooは呼び出しの時点で宣言されていないため、前の例はC99でコンパイルされません。それでも、非プロトタイプ宣言を追加できます

int foo(); /* Declares `foo`, but still no prototype */

int main() {
  int i = foo(5); 
  /* No prototype for `foo`, although return type is known. 
     Will work in C99. Assumes `int foo(int)` */

  return 0;
}
...

そして、有効なC99コードで終わります。

それにもかかわらず、呼び出す前に関数のプロトタイプを宣言することは常に良い習慣です。

追加のメモ:上記で、関数プロトタイプを宣言する必要は決してないと述べました。実際、一部の機能については必須です。 Cでvariadic関数を適切に呼び出すために(たとえば[printf))関数を宣言する必要があります呼び出しポイントの前にプロトタイプを付けます。それ以外の場合、動作は未定義です。これは、C89/90とC99の両方に適用されます。

65
AnT

関数が使用前に定義されている場合、必須ではありません。

6
Drakosha

必須ではありませんが、プロトタイプを使用しないのは悪い習慣です。

プロトタイプを使用すると、コンパイラーは関数を正しく呼び出していることを確認できます(パラメーターの正しい数とタイプを使用)。

プロトタイプがなければ、これが可能です:

// file1.c
void doit(double d)
{
    ....
}

int sum(int a, int b, int c)
{
    return a + b + c;
}

この:

// file2.c

// In C, this is just a declaration and not a prototype
void doit();
int sum();

int main(int argc, char *argv[])
{
    char idea[] = "use prototypes!";

    // without the prototype, the compiler will pass a char *
    // to a function that expects a double
    doit(idea);

    // and here without a prototype the compiler allows you to
    // call a function that is expecting three argument with just
    // one argument (in the calling function, args b and c will be
    // random junk)
    return sum(argc);
}
3

Cでは、関数プロトタイプを宣言せずに関数定義を使用する場合、問題はなく、関数の戻り値の型が「整数」の場合、プログラムはコンパイルして出力を生成します。他のすべての条件では、コンパイラエラーが表示されます。その理由は、関数を呼び出して関数プロトタイプを宣言しない場合、コンパイラーは整数を返すプロトタイプを生成し、同様の関数定義を検索するためです。関数プロトタイプが一致した場合、正常にコンパイルされます。戻り値の型が整数でない場合、関数プロトタイプは一致せず、エラーが生成されます。そのため、ヘッダーファイルで関数プロトタイプを宣言することをお勧めします。

2
Raviteja

Cでは、まだ宣言されていなくても関数を呼び出すことができますが、間違った引数を使用した場合にコンパイラーがユーザーを救うことができるように、使用する前にすべての関数のプロトタイプを宣言することを強くお勧めします。

1
Anders Abel

関数宣言はヘッダーファイル(X.h)に、定義はソースファイル(X.c)に配置する必要があります。その後、他のファイルが#include "X.h"および関数を呼び出します。

1
cpalmer

関数プロトタイプは、C99標準に従って必須ではありません。

1
zoli2k

呼び出し元のコードをコンパイルするための関数を宣言する必要はありません。ただし、注意点があります。宣言されていない関数はintを返すと想定されており、コンパイラーはまず宣言されていない関数に関する警告を発行し、次に戻り型とパラメーター型の不一致について警告を発行します。

プロトタイプを使用して関数を適切に宣言することは、はるかに優れたプラクティスであることは明らかです。

0