電子書籍の厳選無料作品が豊富!

はじめまして。

【苦しんで覚えるC言語】(Web版)でC言語を学んでいる初学者です。

【第1部:C言語基礎編】>【関数の作り方】>【自作関数を作る】の部分(http://9cguide.appspot.com/11-01.html#S2)でつまづいています。

下記のような、1から100までの数字の合計を表示するプログラムが例題として取り上げられています。

しかし、ここが理解できないので、次の【プロトタイプ宣言】に進めずにいます。



例題1  sum関数をmain関数の後に記述した場合(プログラムが実行されない)。

#include <stdio.h>

int main(void)
{
return 0;
}

int sum(void)
{
printf("%d\n",(1 + 100) * 100 / 2);
return 0;
}



例題2  sum関数をmain関数の先に記述した場合(プログラムが実行される)。

#include <stdio.h>

int sum(void)
{
printf("%d\n",(1 + 100) * 100 / 2);
return 0;
}

int main(void)
{
return 0;
}



例題1の場合、【まずmain関数を作り、次にsum関数を作りました。この場合、main関数を解析している段階では、sum関数は発見されておらず、従って、main関数の中では、sum関数を使うことは出来なくなってしまいます。】との説明がありますが理解できません。

コンパイラがどのような順番でプログラムを処理しているか理解できていないので分からないのだと思います。

例題1の場合と例題2の場合の両方について、コンパイラがどのような順番でプログラムを処理しているのかを具体的に教えてください。

どうかよろしくお願い致します。

A 回答 (5件)

たぶん、この例のmain()は


int main(void)
{
sum();
return 0;
}
の間違いなのでしょうか。質問に書いてある通りでは、なにもしない関数になってしまいますから。

で、質問の答えですが、
「C言語では、main()は最後に書くのがルールです。」
の一言で済ませてしまってもよかったのですが、このサイトでは、もう少しそれらしい説明をしているのでしょう。

Cコンパイラは、それほど何度もパスを繰り返していません。なので、
「使う前に定義を書かないといけない」
というのが原則になっています。main() の中で使う前に、sum()の定義が必要なのです。

本気で勉強するなら、サイトでなく、本を買ったほうがよいですよ。
    • good
    • 0
この回答へのお礼

早速ご回答いただき、ありがとうございました。

例題が間違っており、すみませんでした(例題1・例題2ともに実行されないプログラムを載せてしまいました)。

また、関連する新しい疑問も出てきました。

頭を整理してから再度質問をし直します。

お礼日時:2012/07/07 16:01

これはC言語の仕様です。

使用する変数や関数は先に定義または宣言する必要があります。コンパイルを少しでも早くするため(今から考えると当時のミニコンは遅かった)構文解析を1Passにしたのだと思います。
アセンブラ言語は普通2Passで解析します。後から出てきたシンボルがUndefineになる事はありません。
コンパイラ言語の構文解析は先頭から順に行います。解析結果はグラフ/ツリーのような形で表現されるらしいです。
    • good
    • 2
この回答へのお礼

早速ご回答いただき、ありがとうございました。

例題が間違っており、すみませんでした(例題1・例題2ともに何も実行しないプログラムを載せてしまいました)。

また、関連する新しい疑問も出てきました。

頭を整理してから再度質問をし直します。

お礼日時:2012/07/07 16:00

まず、ここで書かれた例ではsum()をどこからも呼び出されていないのでどちらでも実行されません。




>main関数を解析している段階では、sum関数は発見されておらず、従って、main関数の中では、sum関数を使うことは出来なくなってしまいます。
この解説は間違い、後述の関数を使用出来ないのはCの言語仕様です。(出来ないと決めているから出来ない)

コンパイラは頭から逐一翻訳していくのではありません。
コンパイラによって違いますが通常は数回から十数回のパスをへてオブジェクトファイルを出力します。
具体的にコンパイラがどのような解析をしているかは簡単には書けませんから専門書を参照してください。

なぜそのような仕様になっているかですが恐らくCの開発当時のコンピュータの演算能力や使用料の低減のために人間への負担を増やすことで少しでもコンパイラを簡易なものにするためでしょう。
    • good
    • 0
この回答へのお礼

早速ご回答いただき、ありがとうございました。

例題が間違っており、すみませんでした(例題1・例題2ともに実行されないプログラムを載せてしまいました)。

また、関連する新しい疑問も出てきました。

頭を整理してから再度質問をし直します。

お礼日時:2012/07/07 16:01

例題があまり良くないですねぇ。



>コンパイラがどのような順番でプログラムを処理しているのか

プログラムを処理…ではなく、構文解析…というべきでしょうかね。
とりあえず、プリプロセスで疑似命令とか処理して1つのファイルとして、先頭から構文解析を進めているハズです。
ちなみに、例題1も例題2もsum()はコールされていませんからどっちも「実質なにもしないmain()」が実行されるだけ…ですよ。

どっちもmain()が
int main(void)
{
 sum();
 return 0;
}
だった場合、
例題1だとsum()の情報(引数の数や型、戻り値の型などの情報)が無い為、コンパイルエラーか警告となります。
# そういう場合の規定があった…かと。(引数、戻り値もint型とする…でしたかね)

例題2だとmain()からsum()をコールする時にはsum()の情報は解釈済みですのでコンパイルエラーや警告になることはありません。
# もちろん、正しく使っている場合に限りますが。

つまり、呼び出す前に呼び出し先の情報があればいい。
ということになります。

main()からfunc1()をコールしているからfunc1()をmain()より前に書けばいい。
func1()からfunc2()とfunc3()とfunc4()をコールしているからfunc1()の前にfunc2()とfunc3()とfunc4()をfunc1()より前に……とかやっていけば万事解決です。
そういう依存関係を考慮してソースを書く必要がある。ってことですね。

依存関係の結果入れ子になったらどうしましょうか。
func2()からfunc3()とfunc4()をコールしている。func3()からはfunc4()をコールしている。func4()からは条件によってはfunc3()をコールすることもある。
さて、どうしましょう?
そんな依存関係も把握して書かないといけないなんて面倒ですね……。

ってことでプロトタイプ宣言ってものがあるんです。
    • good
    • 1
この回答へのお礼

早速ご回答いただき、ありがとうございました。

例題が間違っており、すみませんでした(例題1・例題2ともに実行されないプログラムを載せてしまいました)。

また、関連する新しい疑問も出てきました。

頭を整理してから再度質問をし直します。

お礼日時:2012/07/07 16:02

先頭から順に解析しています。



例えば、変数で考えてみます。

main関数の中で

int main(void)
{
i++;

int i;
}

こうだと、エラーが出ます。変数の定義の前に変数を使用しているからです。

この場合、使用する前に必ず変数を定義する必要があります。

int main(void)
{
int i;

i++;
}


同様に、関数も定義するまえに使用してはいけないのです。ですからmainの前にプロトタイプ宣言するか、実態そのものを定義しておくか、プリプロセッサ(#include)で読み込んでおくかの必要があります。
    • good
    • 0
この回答へのお礼

早速ご回答いただき、ありがとうございました。

例題が間違っており、すみませんでした(例題1・例題2ともに実行されないプログラムを載せてしまいました)。

また、関連する新しい疑問も出てきました。

頭を整理してから再度質問をし直します。

お礼日時:2012/07/07 16:03

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!