Why do functions need to be prior declared in C ?
6 に答える
現代の C 言語の関数は、次の 2 つの理由から事前に宣言する必要があります。1) 特定の名前が関数の名前である (他の名前ではない) ことをコンパイラに伝えるため、2) コンパイラに正確な戻り値の型を伝えるためコンパイラがその戻り値を正しく処理できるように関数を変更します。
C では、関数はプロトタイプの有無にかかわらず宣言できます。プロトタイプ宣言は、コンパイラにより多くの情報を提供します。これには、関数パラメーター (存在する場合) の数と型に関する情報が含まれているため、コンパイラーが関数呼び出しの引数を適切に準備するのに役立ちます。
C 言語の C89/90 バージョンでは、関数を事前に宣言する必要がなかったため、コンパイラは呼び出しの時点で関数に関する仮定を作成していました。言うまでもなく、多くの場合、これは危険であることが判明しました。
関数を呼び出すときに、コンパイラが型エラーを検出できるようにします。もちろん、それを回避する方法はありますが、それが彼らが選んだ方法です。
古いコンパイラは速度とメモリに制限があるため、すべてを1回のパスで実行できるようにする必要があります(ファイルを上から下に読み取るだけで、すべてが理解されます)。
最新の設計のコンパイラは、ある時点でまだ宣言されていない場合でも、別のファイルで関数を上下に検索できます。
基本的に、あなたは....
多くのコンパイラは、まだ宣言していない場合、int Function() シグネチャを呼び出していると想定します。リンカーは...うーん..おそらくそれを食べません。
宣言された関数の署名が呼び出しステートメントと一致しない場合、コンパイラは気にしますが、問題が発生するのは 2 番目のステップです。 - 引数の受け渡しと戻り値のプリコードのコーディング
後者は実際には呼び出しごとに決定する必要があります。この 2 番目のコード生成ステップで、Cコンパイラーは実際に宣言を見落とします (これが関数シグネチャを作成します)。次に、リンカーは、関数のシンボリック呼び出しを実際の... er... 呼び出しに変換する必要もあります。しかし、関数が「どこかに」存在する場合 ( extern修飾子を調べてください)、リンカーはショーストッパーにはなりません。
このすべての例外は、関数ポインターメカニズムです。コンパイラーとリンカーに期待される署名を伝えますが、呼び出し自体はコンパイラーによって決定されず、リンカーによる「ハードコーディングされた」呼び出しもありません...チェックそれを出します。
ワンパス コンパイラが各引数に渡すバイト数を認識できるようにします。
検討:
f(12345);
int f(char input)
{
printf("%c",input);
}
プロトタイプがない場合、コンパイラは がf
int を受け入れると想定しsizeof(int)
、(プラットフォームに応じてスタックまたはレジスタを介して) 関数にバイトを送信します。しかし、関数は 1 バイトしか見ていないため、間違った結果が返されます。
これが唯一の理由ではないと確信していますが、たとえばヘッダーファイルからの関数の宣言しか知らない場合、他の関数への呼び出しを含むファイルをコンパイルできます。これは、関数自体の定義を再コンパイルせずに可能です (別のファイルにある可能性があります)。ただし、関数が正しく呼び出されていることを確認するには、コンパイラはその宣言を認識している必要があります。その後、リンカーが残りを処理します。
ここで少し例を挙げます
main.c:
#include "function.h"
int main(){
function();
return 0;
}
関数.h:
#ifndef FUNCTION_H
#define FUNCTION_H
void function();
#endif
関数.c:
#include "function.h"
void function(){}
私はそのようにコンパイルするためにgccを使用しています:
gcc function.c -c
これにより、オブジェクト ファイル function.o が生成されます。これで、メイン関数をコンパイルするときに、function.c ファイルをコンパイルする必要がなくなりました。ヘッダー ファイルとオブジェクト ファイルからの宣言を知るだけで済みます。
gcc main.c function.o -o test
これで、オブジェクト ファイルは再コンパイルせずにプログラムにリンクされます。