プログラムの続きの質問していきます。ポインタの仕組みを教えてほしいというところからこのプログラムを書いてくださったのですが、1行目で膨大な質問、2行目もまた質問です。
#include <stdio.h>
int main() {
int value = 3610; // あなたのCPUの型番です
printf("i7-3610QMのメモリ住所を覗きます...\n");
printf("住所: %p, 値: %d\n", (void *)&value, value);
return 0;
}メイン関数の型の変化(うん十年前と今・・・)
int main() {
}ん?なんかわたしが習ってたうん十年前と様子が違います。int使ってます。なんで?
void main(){
}または
void main(void){
}って書いてました。こ、これはジェミニ先生に聞くしかない・・・。
メイン関数とは?
メイン関数とは?
では、2行目の int main() { ... } についてですね。
これは一言で言うと、「プログラムの『玄関』」です。
1. 「ここからスタート!」という目印
C言語のプログラムには、たくさんの行(コード)を書くことができますが、コンピュータはどこから読み始めていいか分かりません。
そこで、「必ず main という名前の場所から始めてね」という世界共通のルールがあります。これが「玄関」や「スタート地点」と呼ばれる理由です。
2. 「int」はプログラムの「最後のご挨拶」
main の前についている int は、「整数(Integer)」という意味です。
プログラムが無事に終わったとき、最後にOSに対して「0(異常なし!)」という数字を返す決まりになっています。「最後は数字で返事をするよ」という宣言なんです。
3. 「 { 」と「 } 」は「お家(部屋)」の壁
{ から始まり、} で終わる。この間に書かれたものが、一つの処理のまとまりになります。
「玄関を入ってから、最後のご挨拶(return 0)をするまでの活動内容」をこの中に書くわけです。
どんなプロジェクトのプログラムにもメイン関数は1つですか?
どんなプロジェクトのプログラムにもメイン関数は1つですか?
結論から言うと、「最終的に1つのソフト(実行ファイル)として動くもの」の中には、必ずメイン関数は1つだけです。
どれだけ巨大なプロジェクトでも、コンピュータが「どこから始めればいいの?」と迷わないように、スタート地点は必ず1つに絞るというルールがあるからです。
1. 100個のファイルがあっても「玄関」は1つ
例えば、何万行という巨大なプログラムが100個のファイルに分かれていたとしても、int main() が書かれているのはその中のたった1つのファイルだけです。他のファイルには「部品」としての機能だけを書いて、メイン関数がそれらを呼び出す形になります。
2. もし2つあったらどうなる?
もし間違えて2つのファイルに main を書いてしまうと、コンパイルした最後に「玄関が2つあるよ!どっちから入ればいいか分からない!」とコンピュータに怒られてしまいます(これを「二重定義エラー」と呼びます)。
3. 「複数のメイン関数」があるように見えるケース
ただし、開発の現場では以下の理由で「1つのプロジェクト内に main が複数ある」ように見えることがあります。
- テスト用: 本番の
mainとは別に、部品が正しく動くか試すための「テスト用main」を一時的に作ることがあります。 - 複数のソフトを作る場合: 1つのプロジェクト(フォルダ)から「Aというソフト」と「Bというソフト」の2つを作る設定なら、それぞれに
mainが必要になります。
私は以前void main(){}か、void main(void){}で習ってました。なぜintになったのですか?
私は以前void main(){}か、void main(void){}で習ってました。なぜintになったのですか?
それは非常に鋭い質問ですね!ブログに書くと「昔と今の違い」という面白いネタになります。
結論から言うと、「OSに対して、プログラムが正しく終わったかどうかを報告する」というルールが厳格になったからです。
理由は以下の3つです。
1. 「報告」が必要になった
今のOS(WindowsやLinux)は、プログラムが終了した瞬間に、そのプログラムから「終了ステータス(数字)」を受け取ります。
- 0を返す: 「無事に終わりました!」
- 0以外を返す: 「エラーが起きて途中で止まりました!」
この報告を受け取るために、最後に戻す値の型(int = 整数)を明示するのが現代の標準(C言語の規格)になりました。
2. void main は「方言」だった
以前使われていた void main は、実は一部のコンパイラや、OSがない環境(組み込み系)で独自に許されていた、いわば「方言」のようなものです。
しかし、現在世界中で使われている「C言語の公式ルール(C99やC11といった規格)」では、「main関数の戻り値は int にしなさい」と決められています。
3. (void) の有無について
int main():引数(ひきすう)については「とりあえず今は気にしない」という書き方。int main(void):「この関数は、外から何もデータを受け取りません!」と厳格に宣言する書き方。
最近の教科書では、より正確なint main(void)が推奨されることが多いですが、どちらもintから始まるのは共通しています。- もしあなたのPC(VS Code)で
void mainと書くと、コンパイラによっては「intじゃないよ!」と警告(Warning)が出るかもしれません。
リターン(return 0;)の正体を暴く
型がintなのだから最後に何らかしら数字を返さないといけないことはわかりました。しかし0とか1とかよく見ますが数字の違いは何ですか?
型がintなのだから最後に何らかしら数字を返さないといけないことはわかりました。しかし0とか1とかよく見ますが数字の違いは何ですか?
1. 0 と 1(それ以外の数字)の違い
OSにとって、プログラムからの返信(戻り値)は「結果報告」です。
return 0;(成功): 「すべて計画通りに終わりました。異常なし!」という合図です。return 1;(失敗/異常): 「何らかの問題が発生して、最後までやり遂げられませんでした」という合図です。- ※1以外でも、2や-1など「0以外」はすべて「何かがおかしい」という意味になります。
なぜ分けるの?
例えば、あなたがブログの更新を自動化するプログラムを作ったとします。「画像のリサイズ」に失敗して return 1 が返ってきたら、次の「記事の投稿」を中止する、といった判断をOSや別のプログラムができるようにするためです。
たまにreturn;みたいなやつ見ますがC言語で使いますか?
たまにreturn;みたいなやつ見ますがC言語で使いますか?
2. return;(数字なし)はC言語で使う?
結論から言うと、「void」と書かれた関数では使いますが、main関数では使いません。
- 使うケース: 戻り値が
void(空っぽ)と定義された関数の中で、「ここで処理を切り上げて帰ります」という時にだけreturn;と書きます。 - main関数の場合:
int mainと宣言している以上、「数字を返す」という契約なので、必ずreturn 0;のように数字を添える必要があります。
3. 最近のC言語の「ちょっとした魔法」
実は、最新のC言語のルールでは、main 関数に限って「return 0; を書き忘れても、勝手に 0 を返したことにしてくれる」という親切機能があります。でも、プロの現場では省略せずにしっかり書くのがマナーとされています。
ちなみに数字の違いは何なの?C言語で設定されているルールがあるのですか?0は異常なしとC言語のルールで決められているんですよね?
ちなみに数字の違いは何なの?C言語で設定されているルールがあるのですか?0は異常なしとC言語のルールで決められているんですよね?
結論から言うと、「0が成功」というのはC言語だけのルールではなく、OS(WindowsやLinuxなど)を含めたコンピュータ界の「世界共通の約束事」です!
1. 0=成功(Success)の理由
C言語の規格(公式ルール)では、main関数が 0 を返すと「実行が成功したとみなす」と明確に決められています。
なぜ「1」ではなく「0」なのかというと、「成功のパターンは1つ(0)だけど、失敗の理由はたくさんあるから」です。
2. 0以外の数字=失敗の種類(Error Codes)
0以外の数字には、「何が原因でダメだったのか」というメッセージを込めます。これらはOSやプログラムごとに決めることができますが、一般的なイメージはこんな感じです。
return 1;: 一般的なエラー(何かがおかしい)return 2;: ファイルが見つからないreturn 13;: 権限がない(アクセス拒否)
もし成功を「1」に固定してしまうと、失敗した時に「どう失敗したか」を数字で使い分けるのが難しくなってしまうため、「0だけを特別な成功の印」にしたのです。
3. C言語の魔法の言葉(マクロ)
実は、数字を直接書かなくてもいいように、C言語にはこんな「名前」が用意されています。
EXIT_SUCCESS(中身は 0)EXIT_FAILURE(中身は 1)
これを使うと、数字の意味がより分かりやすくなります。
プロの現場のエラー処理事情
まって、じゃあこの数字だったら何のエラーとか自分で決めて定義しておくということですか?
まって、じゃあこの数字だったら何のエラーとか自分で決めて定義しておくということですか?
その通り、大正解です!「どの数字がどんなエラーか」は、プログラマー(あなた)が自由に決めていいルールなんです。
1. プログラム独自の「マニュアル」を作る
例えば、あなたが「ファイルを読み込んで計算するプログラム」を作った場合、こんな風に自分勝手に決めてOKです。
return 2;だったら「読み込むファイルがなかったよ」return 3;だったら「ファイルの中に数字じゃない文字が入ってたよ」return 99;だったら「理由は不明だけどとにかくダメだった!」
このように、数字に意味を持たせることで、後からそのプログラムを使う人が「あ、2が返ってきたからファイル名を確認しよう」と判断できるようになります。
2. 共通の「辞書」も存在する
自由といっても、デタラメすぎると混乱するので、OS側であらかじめ「この数字はこういう意味で使われることが多いよ」という共通の辞書(エラーコード)が用意されていることもあります。
(例:Windowsでは 13 は「アクセス拒否」など)
プロは、この「OSの辞書」に合わせつつ、自分だけのルールを追加したりします。
開発の現場では多くの人とこのエラーを共有すると思いますが別ファイルか何かにして読み込むのですか?現場ではどうしてるのですか?
開発の現場では多くの人とこのエラーを共有すると思いますが別ファイルか何かにして読み込むのですか?現場ではどうしてるのですか?
鋭い視点ですね!まさにその通りで、現場では「エラーコード専用の辞書ファイル」を作って共有します。
数字を直接書くと「この13って何だっけ?」と混乱するので、以下のような方法で管理しています。
1. 「ヘッダーファイル」で一括管理
.h(ヘッダーファイル)という種類のファイルに、数字と名前のペアを定義して、プロジェクト全員で使い回します。
// error_codes.h (エラー辞書ファイル)
#define ERR_FILE_NOT_FOUND 2
#define ERR_ACCESS_DENIED 13
#define ERR_INVALID_CPU 100こうしておけば、プログラムの中では数字(100)ではなく、return ERR_INVALID_CPU; と名前で書けるようになります。これなら誰が見ても意味が一発でわかりますよね。
2. 「ドキュメント(仕様書)」との連携
大規模な開発(銀行のシステムや車の制御など)では、Excelや専用のツールで「エラーコード表」という一覧表を作ります。
- 0: 正常
- 101: データベース接続失敗
- 102: タイムアウト
このように、プログラムの数字とマニュアルの記述を完全に一致させ、保守担当者がすぐに原因を特定できるようにします。
3. 現場あるある:エラーの「たらい回し」
自分が作ったプログラムがエラーで止まったとき、そのエラーコードをそのまま「親」のプログラムに伝え、最終的にユーザーの画面に「エラー番号:100」と表示させることもあります。
実際にプログラムで書く場合、エラーの場合分けをすべて分岐させて表記しておくのですか?
実際にプログラムで書く場合、エラーの場合分けをすべて分岐させて表記しておくのですか?
はい、基本的には「エラーが起きる可能性がある場所」すべてで分岐(if文など)を書いて、適切に処理を分けるのがプロの書き方です。
ただ、すべてのエラーを1つずつ手書きするとコードが埋まってしまうので、現場では以下のような工夫をしています。
if (file == NULL) {
return ERR_FILE_NOT_FOUND; // ダメならすぐ帰る!
}
// ここからは「ファイルがあった場合」の正常な処理これなら、ズルズルと長い else を書かずに済み、読みやすくなります。
2. 「共通の処理」にまとめる
「エラーが起きたらログを書いて終了する」という流れは共通していることが多いので、「エラー処理専用の関数」を1つ作っておき、どこでエラーが起きてもその関数を呼び出すだけにします。
3. 全部書くのは大変じゃない?
正直、大変です!プロのプログラムの半分以上は「エラー処理(例外処理)」でできていると言われるほどです。
「100回に1回しか起きないエラー」を無視せずに書くのが、銀行や車のシステムのような「絶対に止まってはいけないソフト」を作るプロの仕事なんです。
今回のプログラムの結論
では改めて今回のプログラムを見てると分岐は一切なく、強制で0を返してますね?これは学習用とはいえこんな短いプログラム間違うわけねーだろ、正常終了だ!ということですか?
では改めて今回のプログラムを見てると分岐は一切なく、強制で0を返してますね?これは学習用とはいえこんな短いプログラム間違うわけねーだろ、正常終了だ!ということですか?
まさにその通りです!「この内容なら失敗する要素がないから、自信満々のノーチェックで0を返すぜ!」という状態ですね。
1. 「失敗する可能性」がゼロに近いから
今回のプログラムが行っているのは「自分の手元にある数字(3610)を表示するだけ」です。
- 外部のファイルを読み込むわけではない
- ネットに繋ぐわけではない
- ユーザーに何かを入力してもらうわけではない
つまり、「途中で予期せぬエラーが起きる隙(スキ)」がほとんどありません。そのため、わざわざif文でチェックせずに、最後にドーンとreturn 0;だけを書いているのです。
2. 学習用プログラムの「お約束」
入門書などの短いサンプルコードで if 文をたくさん書くと、本当に伝えたい「表示の仕組み」が埋もれて読みづらくなってしまいます。
そのため、最初は「とりあえずハッピーエンド(0)」で終わる形にするのが、教える側のマナー(暗黙の了解)でもあります。
3. 実は「隠れたエラー」も……?
実は、厳密に言えば printf (画面表示)ですら、稀に失敗することがあります。
(例:表示先の画面が突然閉じられた、メモリが限界、など)
究極に厳しい現場なら、printf の結果すら if でチェックしますが、学習用ではそこまでやると「やりすぎ」になってしまいます。
リターンreturnについて深堀
正直リターンの説明をちゃんとしてくれないのでリターンの数字の意味もなにも分からなくなってしまうのです。もっとちゃんとしてくださいね。こんなんじゃあ超初心者は理解できません
大変失礼いたしました。おっしゃる通りです。
「0は成功、1は失敗」という言葉だけで片付けてしまい、「そもそもその数字はどこへ行くのか?」「なぜその数字が必要なのか?」という、初心者が一番知りたい「リターンの本質」を疎かにしてしまいました。
超初心者の方が「あ、そういうことか!」と100%納得できるレベルまで、改めてリターンの正体を掘り下げて説明させてください。
1. リターンの数字は「誰」が受け取るのか?
プログラムが return 0; と言ったとき、その「0」という数字を受け取るのは、「そのプログラムを起動した人(OSや別のソフト)」です。
- イメージ: プログラムは、OS(Windowsなど)から「この作業をやっておいて」と頼まれた「使い走り」のような存在です。
- リターンの役割: 作業が終わったあと、使い走りがOSのところへ戻ってきて、「結果はどうだった?」と聞かれた時に指で示す「サイン」がリターンの数字です。
2. なぜ「0」が成功で、他の数字は「失敗」なのか?
これは「テストの点数」ではなく、「異常(エラー)の数」だと考えるとスッキリします。
return 0;→ 「異常は 0 個でした!完璧です!」return 1;→ 「異常が 1 個ありました。ダメでした……」
「成功」という状態は「何も問題がない」という1パターンしかありません。でも、「失敗」には「ファイルがない」「ネットが繋がらない」など、たくさんのパターンがあります。
だから、「0以外(1個以上の異常あり)」をすべて失敗というルールにしたのです。
3. この数字が分からないと、次に進めない
なぜこの報告が必要かというと、OSは「前の作業が成功したかどうかで、次の作業をやるか決める」からです。
- 例:
- プログラムA:ログインする
- プログラムB:メールを送る
- もしプログラムAが
return 1(失敗)を返したら、OSは「あ、ログインできてないなら、Bのメール送信はやめよう」と判断できます。この「バトンタッチ」のために数字が必要なのです。
4. void main がダメになった本当の理由
以前使われていた void main は「リターン(報告)をしない」という書き方でした。
これは、使い走りが「仕事が終わったのに、何も言わずにいなくなる」のと同じです。頼んだ側(OS)は「え、今のうまくいったの?」と不安になりますよね。
だから、現代のC言語では「ちゃんと数字(int)で報告しなさい」というルールに厳格に変わったのです。
突然return 0;って書かれてわかる人いないと思います。ようやくよくわかりました
次はようやくポインタの説明を聞けますね!長くなったので今日はここでおしまいです。
