息子の高校のC言語の教科書を貸してもらう
息子が高校で使っていたC言語の教科書をもう終わったというので貸してもらうことにしました。ジェミニ先生はわからないところだけをかいつまんで聞くような形で使ってますが、おさらいで教科書の内容を一からさらっていくことにしました。「新・明解C言語 入門編」柴田望洋先生の書かれた本です。学習していて引っかかったところだけをジェミニ先生に深堀聞いてみたいと思います。
14ページ・変数の宣言
宣言だけしてから値を代入するタイプ
変数の宣言において2種類あることを学びました。一つは変数を宣言し、そのあとその変数に何か値を入れる。もう一つは宣言と同時に初期値を入れ初期化もする宣言である。
#include<stdio.h>
int main(void){
int x,y; //宣言だけ
x = 1; //1を代入
y = 2; //2を代入
printf("xの値は%dです。\n",x);
printf("yの値は%dです。\n",y);
return 0;
}最初宣言しただけの代入する前のx,yには何が入ってるんでしょうか?試しに宣言のすぐ下にprintf関数をいれて何が入っているか見てみました。
#include<stdio.h>
int main(void){
int x,y; //宣言だけ
printf("xの値は%dです。\n",x);
printf("yの値は%dです。\n",y);
x = 1; //1を代入
y = 2; //2を代入
printf("xの値は%dです。\n",x);
printf("yの値は%dです。\n",y);
return 0;
}
えー、めっちゃ変な数が入ってる!
宣言時に初期化もするタイプ
変数は作られたときに不定値が入っているので変数宣言の際には初期化子を与えて初期化をするとよいそうです。
#include<stdio.h>
int main(void){
int x = 1; //宣言して1で初期化
int y = 2; //宣言して2で初期化
int a = 3, b = 4; //まとめて1行にも書ける
printf("xの値は%dです。\n",x);
printf("yの値は%dです。\n",y);
printf("aの値は%dです。\n",a);
printf("bの値は%dです。\n",b);
return 0;
}
ちゃんと指定した数字が入っていました!!2行に分けても1行でまとめても、どちらの書き方でもできてました。
ジェミニに質問してみた
変数の宣言を、宣言してから代入で値を入れるのと、宣言時に初期化子を与えて初期化するのとどう違いますか?速さとかですか?
変数の宣言を、宣言してから代入で値を入れるのと、宣言時に初期化子を与えて初期化するのとどう違いますか?速さとかですか?
現代のプログラミング(特にC言語)では、人間が書いたコードをコンピュータが読み取る際、「最適化」という工程でどちらの書き方も全く同じ効率的な命令に変換されるのでどちらも処理の速さは変わりません。
ただし、プログラミングの「作法」としては、「初期化子(宣言と同時)」で書くほうが圧倒的に推奨されます。その理由は3つあります:
- ゴミデータ(バグ)の防止
int x;とだけ書くと、中身には「その時メモリに残っていた適当な数字(ゴミデータ)」が入ってしまいます。初期化を忘れて使うと大バグになりますが、int x = 0;ならその心配がありません。 - コードが短くなる
1行で済むので、読みやすくなります。 - constが使える
値を後から変えないconst int x = 10;のような書き方は、宣言と同時でないと使えません。
結論
- 処理速度: どっちも同じ(気にしなくてOK)
- 書きやすさ・安全性: 「宣言と同時に初期化」の圧勝
特別な理由(「後で入力を受け取ってから値を決める」など)がない限りは、int a = 15; のようにまとめて書くのが「デキるプログラマー」の書き方ですよ!
エラーの少なさで初期化子の方がいいんですね!
エラーの少なさで初期化子の方がいいんですね!
その通りです!「処理速度」の差はほぼゼロですが、「バグ(エラー)の出にくさ」で初期化子が圧勝します。
C言語では、変数を宣言だけして放置すると、中身に「メモリの残りカス(デタラメな巨大な数字など)」が入ったままになります。これをうっかり初期化せずに計算に使ってしまうと、原因不明の変な結果が出て、初心者は特にはまりやすいです。
「変数は、生まれた瞬間に値を決めてあげる(初期化する)」
という癖をつけておけば、それだけで防げるエラーがたくさんあります。
ちなみに宣言と代入を別で行う場合ってあるんですか?
ちなみに宣言と代入を別で行う場合ってあるんですか?
はい、あります!主に「宣言した時点では、入れる値がまだ決まっていないとき」です。
具体的には、以下のようなパターンでよく使われます。
1. キーボードから入力を受け取るとき(scanf)
ユーザーがどんな数字を打つかは、プログラムを動かしてみるまで分かりません。そのため、まず「箱(変数)」だけ用意して、後から値を入れます。
int num; // まず宣言
printf("数字を入れてね:");
scanf("%d", &num); // 後から代入(入力)2. 条件によって入れる値を変えたいとき(if文)
「テストの結果によって、合格なら1、不合格なら0を入れる」という場合、宣言と代入を分けます。
int score = 80;
int result; // まず宣言
if (score >= 60) {
result = 1; // 後から代入
} else {
result = 0; // 後から代入
}3. ループ(繰り返し)の中で使うとき
繰り返し計算の途中で何度も値を書き換えたいときは、最初に宣言して、中身を何度も入れ替えます。
まとめ
- 値が決まっているなら: 宣言と同時に初期化(
int a = 10;) - 後から決まるなら: 宣言と代入を分ける
基本は「宣言と同時」を心がけて、「どうしても今は値が決まらないな」という時だけ分けるのが、一番エラーが少ない書き方です。
実数値を入れる場合の宣言
大学時代、そういえば小数点のつく数字を入れるときは「double型」使ってたな。でもあやふやだからジェミニ先生に聞いてみよう!
変数に小数点の数字を入れるときはdouble型ですか?
変数に小数点の数字を入れるときはdouble型ですか?
はい、小数点を含む数字(実数)を扱うときは double 型を使うのが一般的です。
実は他にも float 型というものがありますが、現代のプログラミングでは基本的に double を使っておけば間違いありません。
なぜ double なのか?
- 精度が高い:
floatよりも扱える桁数が多く、計算のズレ(誤差)が少ないです。 - 標準的: C言語の数学関数(サイン、コサインなど)も、基本は
doubleで計算するように作られています。
書き方の例
double weight = 55.5;
double height = 170.2;画面に表示するときの注意点
printf で表示するとき、int 型は %d でしたが、double 型は %f を使います。
printf("体重は %f kgです。\n", weight);※ 小数点第2位まで表示したいなら %.2f と書くとスッキリ綺麗に表示されます。
%.2f と書くと四捨五入ですか?切り捨てですか?
%.2f と書くと四捨五入ですか?切り捨てですか?
結論から言うと、「基本的には四捨五入」されます!
正確には、表示したい桁の次の数字を見て、近い方の値に丸めてくれます。
例:%.2f で表示した場合
1.234→1.23(切り捨てっぽく見える)1.236→1.24(切り上げっぽく見える)
このように、ちょうどいい感じに四捨五入に近い処理をしてくれます。
例えば、double x = 3.145; という数字があった場合、%.2f で表示すると次のようになります。
%.2fで出力 →3.15(小数点第3位の「5」が四捨五入された)
注意点:たまに予想とズレることがある
コンピュータは小数を「ピッタリ」保存するのが苦手で、内部で 1.235 が 1.2349999... のように保存されていることがあります。そのせいで、「5なのに切り捨てられた!」という現象がごく稀に起こります。
ちなみに、「四捨五入じゃなくて、無理やり切り捨てたい!」 というときは別の関数(floor関数など)を使いますが、今は「表示を整えるなら %.2f」と覚えておくだけで十分ですよ。
まとめ
- 見た目を整えるだけなら
%.2f(四捨五入) でOK。 - もし数学的に「絶対に切り捨てたい!」という場合は、別の計算(
floor関数など)が必要。
float と double の違いが気になります。教えてください。
float と double の違いが気になります。教えてください。
float と double の違いは、一言でいうと「精度(細かさ)」と「扱えるデータの大きさ」です。
身近なもので例えると、「普通の定規」と「精密な測定器」のような違いがあります。
1. 精度(桁数)が違う
float(単精度浮動小数点数)- 有効桁数は 約7桁。
- 「3.141592」くらいまでしか正確に覚えられません。
double(倍精度浮動小数点数)- 有効桁数は 約15桁。
- その名の通り
floatの double(倍) くらいの細かさまで覚えられます。
2. メモリのサイズが違う
float: 4バイト(省スペース)double: 8バイト(ちょっと場所をとる)
どっちを使えばいいの?
現代のパソコンはメモリがたっぷりあるので、基本的には double を選ぶのが正解です。
理由は、float だと計算を繰り返すうちに、後ろの方の小さな数字がどんどんズレてしまう(計算誤差)ことがあるからです。
じゃあ、いつ float を使うの?
じゃあ、いつ float を使うの?
何万個、何億個という大量の数字を扱うゲームグラフィックスやAIの計算など、「精度よりスピードやメモリの節約が命!」という特殊な場面で使われます。
float と doubleの豆知識:書き方の違い
C言語で scanf を使うとき、ここだけは型によって書き方が変わるので注意が必要です。
floatの入力を受け取る:scanf("%f", &x);doubleの入力を受け取る:scanf("%lf", &x);(Long floatの略)
printfはいいの?%fで
結論から言うと、printf の場合は double も %f でOK です!
ここがC言語のちょっと紛らわしい(けれど面白い)ルールなのですが、printf と scanf で扱いが違います。
なぜ printf は %f でいいの?
printf に float や double を渡すと、コンピュータが自動的に「とりあえず全部 double として扱う」というルール(既定の引数昇格)があるからです。
そのため、printf では以下のようになります:
floatを表示したい →%fdoubleを表示したい →%f(%lfと書いても動きますが、普通は%fで書きます)
おさらい(ここが混乱ポイント!)
| 型 | printf (表示) | scanf (入力) |
|---|---|---|
float | %f | %f |
double | %f | %lf (必須!) |
「入れる時(scanf)はサイズに厳しいけど、出す時(printf)はだいたいで大丈夫」 と覚えると分かりやすいかもしれません。
もし double を使っているのに scanf で %lf の l を忘れると、変な数字が入ってバグの原因になるので、そこだけは注意してくださいね!
ちなみにint型に小数点入れたらどうなりますか?
ちなみにint型に小数点入れたらどうなりますか?
int 型(整数型)の変数に小数を入れると、小数点以下が「切り捨て」られて、整数部分だけが保存されます。
四捨五入ではなく、無慈悲にバッサリとカットされるのが特徴です。
実際にどうなるか?
int a = 3.99;
printf("%d", a); // 3 と表示されるたとえ 3.99 のように 4 に限りなく近くても、.99 はなかったことにされ、結果は 3 になります。
なぜそうなるの?
int 型という「箱」は、整数の形(小数のない形)でしかデータを保存できない仕組みだからです。小数を入れると、無理やり整数の形にはめ込むために、小数点以下のデータが捨てられてしまいます。これをプログラミング用語で 「型変換(キャスト)」 と呼びます。
注意:コンパイラによっては怒られる
最近の親切なコンパイラ(gccなど)だと、
「小数を int に入れるとデータが消えちゃうけど大丈夫?」と Warning(警告) を出してくれることがあります。
もし「四捨五入して整数にしたい」場合は、少し工夫した計算(round 関数を使うなど)が必要になります。
