定義がソースコードの最後に記述されているときに、C言語でデータと関数の*宣言*が必要なのはなぜですか?


15

次の「C」コードを検討してください。

#include<stdio.h>
main()
{   
  printf("func:%d",Func_i());   
}

Func_i()
{
  int i=3;
  return i;
}

Func_i()はソースコードの最後に定義され、で使用する前に宣言は提供されませんmain()。コンパイラがFunc_i()入っmain()てきたまさにその時に、それは出てきてmain()見つけ出しFunc_i()ます。コンパイラは、どういうわけかによって返された値を見つけ、Func_i()それをに渡しprintf()ます。また、コンパイラはの戻り値の型を見つけることができないことも知っていますFunc_i()。それは、デフォルトでは、(guesses?)の戻り値の型を取りFunc_i()ますint。つまり、コードにfloat Func_i()エラーがあった場合、コンパイラは次のエラーを出しFunc_i()ます。

上記の議論から、次のことがわかります。

  1. コンパイラは、によって返された値を見つけることができますFunc_i()

    • コンパイラがFunc_i()出てmain()きてソースコードを検索することによって返された値を見つけることができる場合、明示的に言及されているFunc_i()のタイプを見つけることができないのはなぜですか。
  2. コンパイラFunc_i()は、それがfloat型であることを認識している必要があります。そのため、競合する型のエラーが発生します。

  • コンパイラがそれFunc_iがfloat型であることを知っている場合、なぜFunc_i()int型であると仮定して、競合する型のエラーを与えるのですか?強制的にFunc_i()float型にしないでください。

変数宣言にも同じ疑問があります。次の「C」コードを検討してください。

#include<stdio.h>
main()
{
  /* [extern int Data_i;]--omitted the declaration */
  printf("func:%d and Var:%d",Func_i(),Data_i);
}

 Func_i()
{
  int i=3;
  return i;
}
int Data_i=4;

コンパイラーはエラーを示します:'Data_i' undeclared(この関数で最初に使用)。

  • コンパイラがを見るとFunc_i()、Func_()によって返された値を見つけるためにソースコードに行きます。コンパイラが変数Data_iに対して同じことをできないのはなぜですか?

編集:

コンパイラ、アセンブラ、プロセッサなどの内部動作の詳細はわかりません。私の質問の基本的な考え方は、使用後にソースコード内の関数の戻り値を伝える(書き込む)とその関数の「C」言語により、コンピューターはエラーなしでその値を見つけることができます。さて、なぜコンピューターは同様にタイプを見つけることができません。Func_i()の戻り値が見つかったため、Data_iのタイプが見つからないのはなぜですか。extern data-type identifier;ステートメントを使用しても、その識別子(関数/変数)によって返される値を伝えていません。コンピューターがその値を見つけることができるなら、なぜタイプを見つけることができないのでしょうか。なぜ前方宣言が必要なのですか?

ありがとうございました。


7
コンパイラは、Func_iによって返された値を「見つけません」。これは実行時に行われます。
ジェームズマクロード14

26
断りませんでしたが、質問はコンパイラの動作に関する深刻な誤解に基づいており、コメントでの回答は、まだ克服すべき概念的なハードルがあることを示唆しています。
ジェームズマクロード14

4
最初のサンプルコードは、過去15年間有効で標準に準拠したコードではなかったことに注意してください。C99は、関数定義に戻り値の型がないこと、およびFunc_i無効の暗黙的な宣言をしました。未定義の変数を暗黙的に宣言する規則はなかったため、2番目のフラグメントは常に不正な形式でした。(はい、コンパイラは最初のサンプルを受け入れます。それは、C89 / C90の下で、もしも有効であったためです。)
ジョナサンレフラー

19
@ user31782:質問の一番下の行:言語XはなぜYを必要とするのですか? それはデザイナーが行った選択だからです。あなたは、最も成功した言語のデザイナーは、彼らが行った文脈でそれらの選択を理解しようとするのではなく、数十年前に異なる選択をするべきだったと主張しているようです。あなたの質問への答え:なぜ前方宣言が必要なのですか?与えられた:Cはワンパスコンパイラを使用しているため。あなたのフォローアップの質問のほとんどに対する最も簡単な答えは、それがワンパスコンパイラではないからです。
Mr.Mindor

4
@ user31782本当に、本当にドラゴンの本を読んで、コンパイラとプロセッサが実際にどのように機能するかを理解したいと思っています-すべての必要な知識を単一のSO回答(または100でさえ)に蒸留することは不可能です。コンパイラーに興味がある人にとっては素晴らしい本です。
Voo 14

回答:


26

Cであるため、シングルパス静的に型付けされた弱い型付けコンパイルされた言語。

  1. シングルパスとは、コンパイラーが関数または変数の定義を先読みしないことを意味します。コンパイラーは先読みしないため、関数の宣言は関数の使用前に行わなければなりません。そうでないと、コンパイラーはその型シグネチャが何であるかを知りません。ただし、関数の定義は、後で同じファイルで行うことも、まったく別のファイルで行うこともできます。ポイント#4を参照してください。

    唯一の例外は、未宣言の関数と変数が「int」型であると推定される歴史的な成果物です。現代の実践では、関数と変数を常に明示的に宣言することにより、暗黙的な型付けを回避しています。

  2. 静的型付けとは、すべての型情報がコンパイル時に計算されることを意味します。その情報は、実行時に実行されるマシンコードを生成するために使用されます。Cには、実行時の型付けの概念はありません。一度int、常にint、一度float、常にfloatです。ただし、その事実は次の点で多少不明瞭になっています。

  3. 弱い型指定とは、プログラマが変換操作を明示的に指定する必要なく、Cコンパイラが数値型間で変換するコードを自動的に生成することを意味します。静的型付けのため、プログラムを実行するたびに同じ変換が常に同じ方法で実行されます。コードの特定の場所でfloat値がint値に変換される場合、float値は常にコードのその場所でint値に変換されます。これは実行時に変更できません。もちろん、値自体はプログラムの実行ごとに変わる可能性があり、条件付きステートメントはコードのどのセクションがどの順序で実行されるかを変える可能性がありますが、関数呼び出しや条件のないコードの特定のセクションは常に正確に実行されます実行されるたびに同じ操作。

  4. コンパイルとは、人間が読み取れるソースコードを分析し、機械で読み取り可能な命令に変換するプロセスが、プログラムの実行前に完全に実行されることを意味します。コンパイラが関数をコンパイルしているとき、指定されたソースファイルでさらに何が発生するかは認識しません。ただし、コンパイル(およびアセンブリ、リンクなど)が完了すると、完成した実行可能ファイルの各関数には、実行時に呼び出される関数への数値ポインターが含まれます。これが、main()がソースファイルのさらに下の関数を呼び出すことができる理由です。main()が実際に実行されるまでには、Func_i()のアドレスへのポインターが含まれています。

    マシンコードは非常に具体的です。2つの整数を追加するコード(3 + 2)は、2つの浮動小数点を追加するコード(3.0 + 2.0)とは異なります。これらは両方とも、intをfloatに追加する(3 + 2.0)などとは異なります。コンパイラーは、関数内のすべてのポイントについて、そのポイントで実行する必要のある正確な操作を決定し、その正確な操作を実行するコードを生成します。一度それが行われると、関数を再コンパイルせずに変更することはできません。

これらすべての概念をまとめると、main()がFunc_i()のタイプを判別するためにさらに「見る」ことができない理由は、コンパイルプロセスの最初にタイプ分析が発生するためです。その時点では、ソースファイルのmain()の定義までの部分のみが読み取られて分析されており、Func_i()の定義はまだコンパイラに認識されていません。

main()がFunc_i()が呼び出す場所を「見る」ことができる理由は、呼び出しが実行時に発生し、コンパイルがすべての識別子のすべての名前と型を既に解決した後、アセンブリがすでにすべての関数をマシンコードにリンクし、リンクすると、呼び出される各場所に各関数の正しいアドレスがすでに挿入されています。

もちろん、ほとんどの詳細は省略しました。実際のプロセスははるかに複雑です。あなたの質問に答えるのに十分な高レベルの概要を提供したことを願っています。

さらに、上で書いたことがCに特に当てはまることを覚えておいてください。

他の言語では、コンパイラはソースコードを複数回通過することがあるため、コンパイラは事前宣言せずにFunc_i()の定義を取得できます。

他の言語では、関数および/または変数は動的に型指定される場合があるため、単一の変数が保持されたり、整数、浮動小数点数、文字列、配列、またはオブジェクトが異なる時点で単一の関数が渡されたり返されたりする可能性があります。

他の言語では、タイピングがより強力な場合があり、明示的に指定するには浮動小数点から整数への変換が必要です。さらに他の言語では、タイピングが弱く、文字列「3.0」から浮動小数点3.0から整数3への変換が自動的に実行される場合があります。

また、他の言語では、コードは一度に1行ずつ解釈されるか、バイトコードにコンパイルされてから解釈されるか、ジャストインタイムコンパイルされるか、さまざまな他の実行スキームが使用されます。


1
オールインワンの答えをありがとう。あなたとニキーの答えは私が知りたかったことです。たとえば、Func_()+1ここでコンパイル時に、コンパイラFunc_i()適切なマシンコードを生成するために型を知る必要があります。おそらくどちらかそうではない可能ハンドルへの組み立てのためのFunc_()+1実行時にタイプを呼び出すことによって、またはそれは可能ですが、そうすることは、実行時に遅いプログラムを行います。今のところ、これで十分だと思います。
user106313 14

1
Cの暗黙的に宣言された関数の重要な詳細:それらはint func(...)... 型であると想定されます。つまり、可変引数リストを取ります。これは、関数をとして定義し、int putc(char)宣言するのを忘れると、代わりにとして呼び出されることを意味しますint putc(int)(可変引数リストを介して渡されるcharはに昇格するためint)。そのため、OPの例は暗黙の宣言と一致するために偶然動作しましたが、この動作が推奨されない理由は理解できます(そして適切な警告が追加されました)。
ウリウィットネス

37

C言語の設計上の制約は、シングルパスコンパイラによってコンパイルされることになっていたため、非常にメモリが制約されたシステムに適しています。したがって、コンパイラーは、前に言及したものについてのみ、いつでも知っています。コンパイラーは、ソースを前方にスキップして関数宣言を見つけてから、その関数の呼び出しをコンパイルするために戻ることはできません。したがって、すべてのシンボルを使用する前に宣言する必要があります。次のような関数を事前に宣言できます

int Func_i();

上部またはヘッダーファイルで、コンパイラを支援します。

例では、避けるべきC言語の2つの疑わしい機能を使用しています。

  1. 適切に宣言される前に関数が使用される場合、これは「暗黙の宣言」として使用されます。コンパイラは、即時コンテキストを使用して、関数のシグネチャを把握します。コンパイラーは、実際の宣言が何であるかを把握するために、コードの残りをスキャンしません。

  2. 型なしで宣言されたものがある場合、その型はと見なされますint。これは、たとえば、静的変数または関数の戻り値型の場合です。

ですからprintf("func:%d",Func_i())、暗黙の宣言がありint Func_i()ます。コンパイラが関数定義Func_i() { ... }に到達すると、これは型と互換性があります。ただしfloat Func_i() { ... }、この時点で記述した場合、暗黙的に宣言さint Func_i()れ、明示的に宣言されfloat Func_i()ます。2つの宣言が一致しないため、コンパイラーはエラーを出します。

誤解を解消する

  • コンパイラは、によって返される値を見つけられませんFunc_i。明示的な型が存在しないということは、戻り値の型がintデフォルトであることを意味します。これを行っても:

    Func_i() {
        float f = 42.3;
        return f;
    }

    型はint Func_i()になり、戻り値は暗黙的に切り捨てられます!

  • コンパイラーは最終的にの実際の型を認識しますFunc_iが、暗黙の宣言中に実際の型を認識しません。後で実際の宣言に到達した場合にのみ、暗黙的に宣言された型が正しいかどうかを確認できます。ただし、その時点では、関数呼び出しのアセンブリは既に作成されている可能性があり、Cコンパイルモデルでは変更できません。


3
@ user31782:コードの順序はコンパイル時に重要ですが、実行時には重要ではありません。コンパイラーは、プログラムの実行時に見えなくなります。実行時までに、関数はアセンブルおよびリンクされ、そのアドレスは解決され、呼び出しのアドレスプレースホルダーに固定されます。(それよりも少し複雑ですが、それが基本的な考え方です。)プロセッサは前方または後方に分岐できます。
Blrfl 14

20
@ user31782:コンパイラー値を出力しません。コンパイラはプログラムを実行しません!!
モニカとの明度レース

1
@LightnessRacesinOrbit私はそれを知っています。ネームプロセッサを忘れてしまったため、誤って上記のコメントにコンパイラを記述しました。
user106313

3
@Carcigenicate Cは、B言語の影響を強く受けています。B言語には、ポインターにも使用できるワード幅の整数数値型のみがあります。Cは元々この振る舞いをコピーしましたが、C99標準以来完全に違法になりました。Unit型理論の観点からすてきなデフォルト型を作成しますが、BおよびCが設計された金属システムに近いプログラミングの実用性に失敗します。
アモン14

2
@ user31782:コンパイラは、プロセッサの正しいアセンブリを生成するために変数の型を知っている必要があります。コンパイラは暗黙を検出するとFunc_i()、それは直ちに生成し、いくつかの整数を受信するために、その後、別の場所にジャンプし、その後継続するプロセッサ用のコードを保存します。コンパイラは、後でFunc_i定義を見つけると、署名が一致することを確認し、一致する場合は、Func_i()そのアドレスにアセンブリを配置し、整数を返すように指示します。プログラムを実行すると、プロセッサはこれらの指示に従って値を取得します3
Mooingダック14

10

まず、プログラムはC90標準に対して有効ですが、以下のプログラムに対しては有効ではありません。暗黙的なint(戻り値の型を指定せずに関数を宣言できるようにする)、および関数の暗黙的な宣言(宣言せずに関数を使用できるようにする)はもはや有効ではありません。

第二に、それはあなたが思うように機能しません。

  1. 結果タイプはC90ではオプションです。 int結果を。変数宣言にも当てはまること(ただし、ストレージクラスを指定するstaticか、extern)。

  2. コンパイラがFunc_i前の宣言なしで呼び出されるときにコンパイラが行うことは、宣言があると仮定しています

    extern int Func_i();

    どの程度効果的であるかを確認するためにコードをさらに調べることはありません Func_iに宣言されている。Func_i宣言または定義されていない場合、コンパイラはコンパイル時にその動作を変更しませんmain。暗黙の宣言は関数に対してのみであり、変数に対してはありません。

    宣言内の空のパラメーターリストは、関数がパラメーターを受け取らないことを意味しないことに注意してください((void)そのために指定する必要があります)。可変個の関数に渡される引数に適用される暗黙的な変換。


コンパイラがmain()から出てソースコードを検索することでFunc_i()によって返される値を見つけることができる場合、明示的に言及されているFunc_i()のタイプを見つけることができないのはなぜですか。
user106313 14

1
@ user31782 Func_iの以前の宣言がなかった場合、Func_iが呼び出し式で使用されていることを確認すると、1つ存在するかのように動作しextern int Func_i()ます。どこにも見えません。
AProgrammer 14

1
@ user31782、コンパイラはどこにもジャンプしません。その関数を呼び出すコードを発行します。返される値は実行時に決定されます。同じコンパイルユニットに存在するこのような単純な関数の場合、最適化フェーズは関数をインライン化する場合がありますが、言語のルールを検討する際に考慮すべきことではありません。最適化です。
AProgrammer 14

10
@ user31782、あなたはプログラムがどのように機能するかについて深刻な誤解を持っています。非常に深刻なので、p.seはそれらを修正するのに適した場所ではないと思います(おそらくチャットですが、私はそれをしようとしません)。
AProgrammer 14

1
@ user31782:小さなスニペットを作成し、-S(使用している場合gcc)でコンパイルすると、コンパイラによって生成されたアセンブリコードを確認できます。次に、実行時に戻り値がどのように処理されるかを理解できます(通常はプロセッサレジスタ、またはプログラムスタック上のスペースを使用)。
ジョルジオ

7

あなたはコメントで書いた:

実行は行ごとに行われます。Func_i()によって返される値を見つける唯一の方法は、メインから飛び出すことです

それは誤解です:実行は行ごとではありません。コンパイルは行ごとに行われ、名前解決はコンパイル中に行われ、名前のみを解決し、値を返しません。

便利な概念モデルは次のとおりです。コンパイラーが次の行を読み取るとき:

  printf("func:%d",Func_i());

以下と同等のコードを出力します。

  1. call "function #2" and put the return value on the stack
  2. put the constant string "func:%d" on the stack
  3. call "function #1"

コンパイラーfunction #2は、まだ宣言されていない名前の関数である内部テーブルにメモを作成します。Func_iは、不特定の数の引数を取り、int(デフォルト)を返します。

後で、これを解析するとき:

 int Func_i() { ...

コンパイラが検索します Func_i上記の表を、パラメーターと戻り値の型が一致するかどうかを確認します。そうでない場合は、エラーメッセージで停止します。存在する場合、現在のアドレスを内部関数テーブルに追加し、次の行に進みます。

そのため、コンパイラはFunc_i最初の参照を解析したときに「検索」しませんでした。それは単にいくつかのテーブルにメモを作成し、次の行を解析し続けました。ファイルの最後には、オブジェクトファイルとジャンプアドレスのリストがあります。

後で、リンカはこれをすべて取得し、「関数#2」へのすべてのポインタを実際のジャンプアドレスに置き換えます。したがって、次のようなものが出力されます。

  call 0x0001215 and put the result on the stack
  put constant ... on the stack
  call ...
...
[at offset 0x0001215 in the file, compiled result of Func_i]:
  put 3 on the stack
  return top of the stack

その後、実行可能ファイルが実行されると、ジャンプアドレスは既に解決されており、コンピューターはアドレス0x1215にジャンプできます。名前検索は不要です。

免責事項:私が言ったように、それは概念モデルであり、現実の世界はより複雑です。コンパイラとリンカは今日、あらゆる種類のクレイジーな最適化を行います。私はそれを疑いますが、彼らは探すために「ダウンジャンプ」するかもしれませんFunc_i。しかし、C言語は、そのような超単純なコンパイラを作成できる方法で定義されています。そのため、ほとんどの場合、非常に便利なモデルです。


ご回答ありがとうございます。:コンパイラは、コード発することができない1. call "function #2", put the return-type onto the stack and put the return value on the stack?
user106313

1
(続き)また、あなたが書いた場合printf(..., Func_i()+1);-コンパイラのタイプを知ってFunc_iいる必要があるのでadd integeradd float命令を発行するか命令を発行するかを決定できます。あなたは見つけるかもしれないいくつかのコンパイラは型情報なしで行くことができる特別な場合を、コンパイラはのために働くために持っているすべてのケース。
ニキー14

4
@ user31782:原則として、マシン命令は非常に単純です。2つの32ビット整数レジスタを追加します。メモリアドレスを16ビット整数レジスタにロードします。アドレスにジャンプします。また、はありません。32ビット浮動小数点数を表すメモリ位置を32ビット整数レジスタに喜んでロードし、それを使って何らかの演算を行うことができます。(それはめったに意味をなさない。)そのため、そのようなマシンコードを直接出力することはできません。ランタイムチェックとスタック上の追加の型データを使用して、これらすべてを実行するコンパイラを作成できます。しかし、Cコンパイラではありません。
ニキー14

1
@ user31782:依存、IIRC。float値はFPUレジスタに格納できます-命令はまったくありません。コンパイラは、コンパイル中にどの値がどのレジスタに格納されているかを追跡し、「FPレジスタXに定数1を追加」などを出力します。または、空きレジスタがない場合、スタック上に存在する可能性があります。次に、「スタックポインタを4増やす」命令があり、値は「スタックポインタ-4」のようなものとして「参照」されます。しかし、これらすべては、スタック上のすべての変数(前と後)のサイズがコンパイル時にわかっている場合にのみ機能します。
ニキー14

1
私はこの理解に到達したすべての議論から:コンパイラがFunc_i()orやand を含むステートメントのもっともらしいアセンブリコードを作成するData_iには、それらのタイプを決定する必要があります。アセンブリ言語では、データ型を呼び出すことはできません。安心するには、物事を自分で詳細に勉強する必要があります。
user106313 14

5

Cおよび宣言を必要とする他の多くの言語は、プロセッサ時間とメモリが高価な時代に設計されました。CとUnixの開発はかなりの時間のために手をつないで行き、3BSDは仕事に余分なスペースがなければ1979年に登場するまで、後者は仮想メモリを持っていなかった、コンパイラは傾向にあったシングルパス、彼らがいなかったため業務一度にすべてのファイルの表現をメモリに保持する機能が必要です。

シングルパスコンパイラは、私たちと同じように、将来を見通すことができません。つまり、確実に知ることができるのは、コンパイルされるコード行の前に明示的に伝えられたものだけです。Func_i()ソースファイルの後半で宣言されているのは私たちのどちらにとっても明白ですが、一度に小さなコードチャンクで動作するコンパイラは、それが来る手掛かりを持ちません。

初期のC(AT&T、K&R、C89)では、foo()宣言の前に関数を使用すると、の事実上のまたは暗黙的な宣言になりましたint foo()。あなたの例Func_i()int、コンパイラがあなたに代わって宣言したものと一致するため、宣言されたときに機能します。他の型に変更すると、明示的な宣言がない場合にコンパイラが選択したものと一致しなくなるため、競合が発生します。この動作はC99で削除され、未宣言の関数の使用はエラーになりました。

では、戻り型についてはどうでしょうか?

ほとんどの環境でのオブジェクトコードの呼び出し規則では、呼び出される関数のアドレスのみを知る必要があります。これは、コンパイラーとリンカーが比較的簡単に処理できることです。実行は関数の先頭にジャンプし、戻るときに戻ります。それ以外のもの、特に引数と戻り値を渡す配置は、呼び出し規約と呼ばれる配置で呼び出し元と呼び出し先によって完全に決定されます。両方が同じ規則のセットを共有している限り、プログラムは、それらの規則を共有する言語でコンパイルされたかどうかにかかわらず、他のオブジェクトファイルの関数を呼び出すことが可能になります。(科学計算では、FORTRANを呼び出すCの多くに出くわし、その逆も同様です。これを行う機能は、呼び出し規約を持っていることに由来します。)

初期のCのもう1つの機能は、現在のプロトタイプが存在しないことです。関数の戻り値の型(例:)は宣言できますが、引数は宣言できint foo()ません(つまり、int foo(int bar)オプションではありません)。これは、上記で概説したように、プログラムが常に引数によって決定される呼び出し規約に固執するために存在していました。間違ったタイプの引数で関数を呼び出した場合、それはガベージイン、ガベージアウトの状況でした。

オブジェクトコードには戻り値という概念がありますが、戻り値の型はないため、コンパイラは戻り値を処理するために戻り値の型を知る必要があります。マシン命令を実行しているとき、それはすべてほんの一部であり、プロセッサは、比較しようとしているメモリにdouble実際にメモリがあるかどうかを気にしませんint。それはあなたが求めることをするだけで、あなたがそれを破るなら、あなたは両方のピースを所有します。

次のコードを考慮してください。

double foo();         double foo();
double x;             int x;
x = foo();            x = foo();

左側のコードは、への呼び出しにコンパイルされ、foo()その後、呼び出し/戻り規則を介して提供された結果をx格納場所にコピーします。それは簡単なケースです。

右側のコードは型変換を示しているため、コンパイラは関数の戻り値の型を知る必要があります。浮動小数点数をメモリにダンプすることはできません。メモリには、他のコードが期待するint場所があります。これは、魔法の変換が行われないためです。最終結果が整数でなければならない場合、保存前に変換を行うようにプロセッサーをガイドする指示が必要です。戻り値の型をfoo()事前に知らなければ、コンパイラーは変換コードが必要であることを知りません。

マルチパスコンパイラは、あらゆる種類のものを可能にします。そのうちの1つは、変数、関数、およびメソッドが最初に使用された後に宣言する機能です。これは、コンパイラーがコードのコンパイルに取り掛かったとき、すでに将来を見通しており、何をすべきかを知っていることを意味します。たとえば、Javaでは、使用後の宣言で構文が許可されるため、マルチパスが必須です。


ご回答ありがとうございます(+1)。コンパイラ、アセンブラ、プロセッサなどの内部動作の詳細はわかりません。私の質問の基本的な考え方は、使用後にソースコード内の関数の戻り値を伝える(書き込む)とその関数の言語は、コンピューターがエラーを与えることなくその値を見つけることを可能にします。さて、なぜコンピューターは同様にタイプを見つけることができません。の戻り値が見つかったため、Data_i のタイプが見つからないのはなぜですかFunc_i()
user106313 14

私はまだ満足していません。double foo(); int x; x = foo();単にエラーを出します。私たちはこれができないことを知っています。私の質問は、関数呼び出しでプロセッサが戻り値のみを見つけることです。戻り値の型も見つけられないのはなぜですか?
user106313 14

1
@ user31782:すべきではありません。のプロトタイプがfoo()あるので、コンパイラはそれをどう処理するかを知っています。
Blrfl 14

2
@ user31782:プロセッサには戻り型の概念がありません。
Blrfl 14

1
@ user31782コンパイル時の質問:コンパイル時にこの型分析をすべて実行できる言語を書くことができます。Cはそのような言語ではありません。Cコンパイラは、実行するように設計されていないため、実行できません。別に設計された可能性がありますか?確かに、そうするためには、はるかに多くの処理能力とメモリが必要になります。一番下の行はそうではありませんでした。それは、当時のコンピューターが最もよく処理できる方法で設計されました。
Mr.Mindor
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.