真に標準に準拠するには、C のすべての関数 (main を除く) にプロトタイプが必要です。それらが同じ翻訳単位で定義された後にのみ使用される場合でも?
6 に答える
それは、「本当に標準に準拠している」という意味に依存します。ただし、簡単な答えは、「すべての関数が使用される前にスコープ内にプロトタイプがあることを確認することをお勧めします」です。
より適切な回答では、関数が変数の引数 (特にprintf()
関数のファミリ) を受け入れる場合、厳密に標準に準拠するためにプロトタイプがスコープ内にある必要があることに注意してください。これは、C89 (ANSI から) および C90 (ISO から。セクション番号を除いて C89 と同じ) に当てはまります。ただし、「varargs」関数以外では、をint
返す関数を宣言する必要はありません。また、以外を返す関数にint
は、戻り値の型を示す宣言が必要ですが、引数リストのプロトタイプは必要ありません。
ただし、プロトタイプがない場合に「通常の昇格」の対象となる引数を関数が受け取る場合 (たとえば、char
orを取る関数short
- どちらも に変換されint
ます。もっと深刻なのは、おそらく、 a のfloat
代わりに a double
) の場合、プロトタイプが必要です。標準は、古い C コードを標準準拠のコンパイラでコンパイルできるようにするため、これに関して緩いものでした。古いコードは、使用前に関数が宣言されていることを保証することを心配するように書かれていませんでした.また、定義上、古いコードはプロトタイプを使用していませんでした。
static a;
C99 では、'implicit int' が許可されていません。これは、' ' (int
デフォルトでは )のような変わったケースと暗黙の関数宣言の両方を意味します。これらは、ISO/IEC 9899:1999 の序文で (他の約 50 の主要な変更と共に) 言及されており、その標準を以前のバージョンと比較しています。
- 暗黙の削除
int
…- 暗黙の関数宣言を削除
ISO/IEC 9899:1990 では、§6.3.2.2関数呼び出しが次のように述べられています。
関数呼び出しで括弧で囲まれた引数リストの前にある式が識別子のみで構成され、この識別子の宣言が表示されない場合、識別子は、関数呼び出しを含む最も内側のブロックでの宣言とまったく同じように暗黙的に宣言されます。
extern int identifier();
現れた。38
38つまり、ブロック スコープを持つ識別子で、パラメーター情報がなく、
int
. 実際に「関数を返す」型として定義されていない場合int
、動作は未定義です。
このパラグラフは、1999 年標準にはありません。私は(まだ)static a;
C90で許可しstatic int a;
、C99でそれを許可しない(要求する)言葉遣いの変更を追跡していません。
関数が静的である場合、それを使用する前に定義することができ、宣言を前に置く必要がないことに注意してください。非静的関数がその前に宣言なしで定義されている場合、GCC は気を悪くする可能性があります ( -Wmissing-prototypes
)。
プロトタイプは、関数のパラメーターの型を指定する関数宣言です。
ANSI C (Kernighan & Ritchie の「The C Programming Language」の 1978 年初版で記述された言語) にはプロトタイプがありませんでした。関数宣言でパラメーターの数または型を記述することができませんでした。正しい数と型の引数を渡すのは、呼び出し元次第でした。
ANSI C では、パラメーターの型を指定する宣言である「プロトタイプ」が導入されました (初期の C++ から借用された機能)。
C89/C90 (ANSI と ISO 規格は同じ言語を記述しています) の時点で、可視宣言なしで関数を呼び出すことは合法です。暗黙の宣言が提供されます。暗黙の宣言が実際の定義と互換性がない場合 (たとえば、 を呼び出しsqrt("foo")
た場合、動作は未定義です。この暗黙の宣言も非プロトタイプ宣言も可変引数関数と互換性がないため、可変引数関数の呼び出し (printf
または などscanf
)目に見えるプロトタイプが必要です。
C99 は暗黙の宣言を削除しました。宣言が表示されていない関数の呼び出しは制約違反であり、コンパイラの診断が必要です。しかし、その宣言はまだプロトタイプである必要はありません。パラメーターの型を指定しない古いスタイルの宣言である可能性があります。
C11 では、この領域に大きな変更はありませんでした。
そのため、2011 年の ISO C 標準の時点でも、古いスタイルの関数宣言と定義 (1989 年以来「時代遅れ」になっている) は、適合コードでまだ許可されています。
1989 年以降のすべてのバージョンの C では、スタイルの問題として、すべての関数にプロトタイプを使用しない理由はほとんどありません。古いスタイルの宣言と定義は、古いコードを壊さないようにするためだけに残されています。
いいえ、関数は常にプロトタイプを必要とするわけではありません。唯一の要件は、関数を使用する前に「宣言」することです。関数を宣言する方法は 2 つあります。プロトタイプを作成する方法と、関数自体を作成する方法 (「定義」と呼ばれます) です。定義は常に宣言ですが、すべての宣言が定義であるとは限りません。
新しい関数を書くときの良いヒントは、main を一番下にして逆さまに書くことです。そうすれば、関数の引数や戻り値の型について気が変わったときに、プロトタイプを修正する必要もありません。プロトタイプを絶えず修正し、時代遅れになったときにコンパイラのすべての警告に対処することは、非常に面倒です。
関数がスムーズに連携するようになったら、コードを適切な名前のモジュールに移動し、プロトタイプを同じ名前の .h ファイルに配置します。それは深刻な時間を節約します。私が 5 年間で見つけた最大の生産性向上ツールです。
はい、すべての関数にはプロトタイプが必要ですが、そのプロトタイプは別の宣言または関数の定義の一部として表示されます。C89 以降で記述された関数定義には当然プロトタイプがありますが、従来の K&R スタイルで記述すると、次のようになります。
main (argc, argv)
int argc;
char **argv;
{
...
}
その場合、関数定義にはプロトタイプがありません。ANSI C (C89) スタイルで書くと、次のようになります。
main (int argc, char **argv) { ... }
関数定義にはプロトタイプがあります。
私の知る限り (ANSI C89/ISO C90)、いいえ。C99 についてはよくわかりません。しかし、私は同じことを期待します。
個人的なメモ:関数プロトタイプを作成するのは...
- (A() が B()を呼び出し、 B() が A() を呼び出す場合)、または
- 関数をエクスポートしています。そうでなければ、それは余計に感じます。