"プログラミングの勉強を始めたときに、文系が挫折しやすい7つのポイント"への返答例
こちらの記事を拝読しました。
まさに私がC言語を勉強し始めたときに感じたことばかりで、強く共感します。なので、このへんをそれなりにまとめてみようと思い記事を書きました。 説明する必要に駆られたときは参考にしてみてください。また、よければご意見をください。
そもそも我々はなぜ"Hello World"と出力する必要があるのだろうか
C言語の本で最も売れた本として、俗に「K&R」と呼ばれる本があります(初学者には向かない)。
この本の中で紹介されたサンプルとして"Hello, World!"が使われていたので、いつのまにか皆が倣うようになったという経緯があります。要はなんでもいいのです。"C言語の勉強を始めます"でも、"C言語はクソだ"でも、"今日の晩御飯何にしよう"でも何でもいいのです。この辺は、ループカウンタの変数名に"i"からアルファベット順に使う風習にも通じます。
「#include <stdio.h>
」とは何なのか。
端的に言えば、printf()
関数を使うために必要な宣言です。
ここでstdioとは、STandarD Input/Outputの略で、日本語では標準入出力などと呼ばれます。つまり、コンソール画面に出力したり、入力を受け取ったりという機能をさしているのです。そして、stdio.hというのはファイルであり、例えばこのような内容になります。
#include
とは、「含める」という英単語の意味の通り、指定したファイルの内容をそのまま展開する動作をします。そのおかげでstdio.hというファイルの中に書かれている宣言が今のソースファイル内に含まれることになり、printf()
関数をはじめとした標準入出力関連の関数などを使うことができるようになるのです。
(ちなみに、.hのhはヘッダ(header)のhです)
(余談ですが、stdio.hはじめ様々な標準ヘッダの実装はWebで見つけることもできます。たとえば共通するキーワードを並べて「stdio.h setvbuf stderr SEEK_END define NULL FILE ifdef」のように検索すると多くの実装が見つかるでしょう。)
fは何なのかよく分かんないし、何で要るのかよく分かんない
fとは、formattedを指します。printf()
関数はprintf("今日は%d月%d日です", month, day);
のようにフォーマットを指定して文字列を出力できるのですが、それを指しています。
なんかmainも、intとか、(void)とか、よく分からん
まず、C言語は関数をひとつの処理の単位として分ける思想があります。そのほうが変更・追加・拡張が容易だからです。具体的には
#include <stdio.h> #include <stdlib.h> // 重複しない乱数を取得する(手抜き) int GetUnappearedRandomNumber(){ static bool isAppeared[RAND_MAX] = {}; int tmp; do{ tmp = rand(); }while(isAppeared[tmp]); isAppeared[tmp] = true; return tmp; } int main(void){ for(int i = 0; i < 10; ++i){ printf("%d : %d", i, GetUnappearedRandomNumber()); } }
このようにすることで、あとから関数の仕様を変えたり、関数の仕様を変えずに使う側の使い方を変えたりということがやりやすくなるからです。
main()
関数もこの関数の一部で、mainと名前の付いた関数からプログラムの実行が始まるという決まりがあります。
intというのは整数が入る型のことであり、main関数はこの型を指定するのが一般的です。voidは省略可能で、「ない」ことを意味します。int main()
と書くのと同じです。(初学者に向けては、この部分のvoidは無い方が良いと思います。)
浮動小数点って??浮くの???
C言語で浮動小数点数というとfloat型とdouble型がありますが、これらは数値の内部表現に関連した話になります。
具体的には↑のページが詳しいのですが、簡単に言うと浮動小数点数は値を「何×2の何乗」という形で保持しており、この「何」の部分が同じであっても、「何乗」の部分の値によって小数点の位置が変わることから浮動小数点と呼ばれています。
何でも入る型一個作ったらそれでいいのでは
その思想に基づいたプログラミング言語もありますが、そのような型はメモリや計算などのリソースを使います。C言語では、int型が4バイトの環境では本当に4バイトしか使いません。64bit変数なら本当に64bit(一般に8バイト)しか使いません。PHPではint型にそれ以上のメモリを必要とします。
www.slideshare.net
↑のスライドの34~35ページです
このように、本当に必要なメモリだけ使用したい、計算に必要な時間を最小限にしたい、という要望に応えられるというメリットがあります(無論操作性は劣ります)。
何で要るんだこのセミコロンは。ひと手間増えるじゃないか。行の切れ目が文の切れ目とかいうことでいいじゃないか。
Pythonなどはまさにその思想に基づいており、文末のセミコロンは不要です。C言語では、多様な書き方ができるように、セミコロンが文の区切りとして使用されています。これもやはり一長一短で、多様性ができる代わりに、実に今問題になっている、初学者に負担を負わせるという面もあります。
「a = 0;」お前はまず……何でほとんどここで0を設定されるんだ。1じゃ駄目なのか。
Luaなどの言語では、学習コストを抑えることなどを目的に1から始まる1-Originの考え方を取り入れている部分もあります。が、基本的にプログラムの動作は基点があり、その基点を元に計算などの操作するため、0-Originのほうが都合がよいことが多いです。これは学び始めのころはメリットがなかなか分からないのですが、例えにポインタを使ってよいなら、ポインタを使って特定の値で100バイトを埋める処理は、0から始めれば
char* p; // ここにどこかしらのポインタが入っていると仮定する for(int i = 0; i < 100; ++i){ *p = 42; // 何かの値 p++; }
とできますが、1から始まると
char* p; // ここにどこかしらのポインタが入っていると仮定する for(int i = 1; i <= 100; ++i){ *(p - 1) = 42; // 何かの値 p++; }
などのように、面倒な書き方を強いられます。 なので、プログラムの基本は0-Originのほうが便利なのです。 無論、単にループするだけなら1から始めても良いですしそう書いたほうがいい場合もありますが、0からの考えを身に着けるために0始まりの作法に慣れたほうがよいと思います。
「a++」はセミコロンいらないって、もうどういうことなん
基本的にかっこの中の文はセミコロンが不要で、先ほど出た「セミコロンが文の区切りになる」というルールによって、3つの文に2つのセミコロンが必要になっています。
繰り返すことに何の意味があるのだ
そもそも配列にしまう必要性も分からない
この辺は上に記した例など実例を実行してもらえればおのずと理解されると思います。
アスタリスクはお前……お前さっきまで掛け算の演算子みたいな顔してたくせに何なんだよアドレスって……&もお前……さっきまで論理積みたいな顔してたくせに……。
まさに私も学習時に困った点です。
int a = 100; int* p = &a; *p = 200;
宣言するときのアスタリスクは、「これはポインタ型ですよ」と示すためのアスタリスク、実際に使うときの*p
などのアスタリスクは、「pの中身」という意味になります。
また、上の&a
は「aのアドレス(メモリ上の位置)」という意味になります。
この「同じ記号で意味が違う」というのがC言語の理解の妨げとなりますが、そのように設計されてしまっているので仕方ありません。
用例としては、ローカル変数を外部の関数から変更したいときやメモリを直接書き換えたいときに使用されます。 例えば卑近な例で言えば、ぷよぷよ風ゲームを作るときにはポインタを使いました。参考↓
まとめ
いくつかはしょりましたが、一通り説明してみました。
C言語は説明することの多い言語ですが、コンピュータの内部で処理がどのようになっているかの理解につながったり、また多くの言語はC言語をもとにしているので、C言語が分かると由来や思想がわかって他の言語の理解を促したり、またゲームや組み込み機器、より機械に近い部分やパフォーマンス・メモリ節約が最重要視されるエキスパートな部分など一部では現在でも実用される言語でもあります。知識のつぶしが利くので学んで損はないと思います。
最後に宣伝ですが、C言語学習者のために、C言語勉強会を月一で開催しています。まだ始めたてなので資料・運営も手探りですが、興味のある方はぜひお越しください。