パート2:C言語ソースの解析
パート1でさわったmain.cファイルの内容を解析してみましょう。
ソースコード
main.cファイルの内容のように、なんらかの言語(今回ならC言語)ルールに従って記述された一連のテキスト(文字列)を、ソースコードと呼びます。
注釈
最初の数行は注釈またはレム(REM:Remark)と呼びます。
C言語には「//」出現以降は改行があるまで注釈とみなし、コンパイラはその部分の記述を無視するというルールがあります。これはプログラムを書く人がメモを残せるようにするためです。
![$テン*シー*シー-1](https://stat.ameba.jp/user_images/20120116/15/xcc/57/a1/j/o0528023811738185094.jpg?caw=800)
プリプロセス
次の
#include <stdio.h>
という行は、コンパイラに与えるプリプロセス(前処理)指令で、「stdio.hという名前のファイルの内容を、ここに記述した事にする。」というものです。
コンパイラによるソースコードからプログラムコードへの変換は
プリプロセス→コンパイル
というように2段階になっていて、このうち、プリプロセス指令は、CPUへの命令(プログラム)ではなく、ソースコードを加工をするためのコンパイラへの命令です。
![$テン*シー*シー-2](https://stat.ameba.jp/user_images/20120116/15/xcc/4f/50/j/o0513012211738188117.jpg?caw=800)
例えば、先ほどの注釈などは、このプリプロセスの課程で取りはぶかれます。注釈は人間に取って非常に役立つものですが、CPUを制御するプログラムコードとは無関係だからです。
そして
#include
というプリプロセス指令もまた、プリプロセスの課程で適用されます。
仮にstdio.hというファイルに以下の内容が記述されていたとしましょう。
int add (int a, int b)
{
return a + b;
}
{
return a + b;
}
この場合、プリプロセス後のmain.cは以下のような状態になります。
![$テン*シー*シー-3](https://stat.ameba.jp/user_images/20120116/15/xcc/be/68/j/o0528014311738190622.jpg?caw=800)
分割コンパイル
もっとも、実際にはこのような形で、別のファイルに分けた関数を取込んだりはしません。
リンカがあるので、別のファイルに記述した関数は、そのファイルをそのままコンパイルさせて、最後にリンカで結合させればいいからです。これを分割コンパイルと呼びます。
![$テン*シー*シー-4](https://stat.ameba.jp/user_images/20120116/15/xcc/9d/01/j/o0537016011738192455.jpg?caw=800)
関数宣言
別のファイルに記述されている関数を使う場合は、使う前に以下のような関数宣言が必要です。
![$テン*シー*シー-5](https://stat.ameba.jp/user_images/20120116/15/xcc/3b/b1/j/o0526009411738193807.jpg?caw=800)
関数宣言には処理部{…}が無い事に注目してください。
関数の入出力の特性だけを記述したものが関数宣言です。
この関数宣言により、main.cファイル内にadd関数の記述は無いが、別のファイルで記述されており、最終的にはリンクされる予定である事をコンパイラに知らせることができます。
ヘッダーファイル
この関数宣言を記述する事が、#includeされるファイルの主な役割りのひとつです。
このような特性から#includeされるファイルはヘッダー(頭につけるの意味)ファイルと呼ばれ、拡張子も.cではなく.hとするのが慣例です。
C標準ライブラリ
実際のstdio.hにはmain関数で使っているprintfの関数宣言が記述されています。
・・・
int printf(const char * __restrict, ...) __DARWIN_LDBL_COMPAT(printf); ←printfの関数宣言
・・・
stdio.hにはprintfだけでなくC言語に最初から用意されているさまざま関数群が宣言されています。
これらの関数群は、第1章で説明した補助プログラムコードのひとつで、C標準ライブラリと呼ばれます。
![$テン*シー*シー-6](https://stat.ameba.jp/user_images/20120116/15/xcc/73/6d/j/o0537013811738196627.jpg?caw=800)
ところで、肝心のstdio.hファイルは、今回のstudyディレクトリには存在しません。
これは、#includeの後ろにある<>アングルブラケットに理由があり、ファイル名が<>で囲まれている場合は、コンパイラにあらかじめ登録されたディレクトリを検索する事になっているからです。
![$テン*シー*シー-7](https://stat.ameba.jp/user_images/20120116/15/xcc/ab/81/j/o0523007311738197884.jpg?caw=800)
stdio.hの実際の内容に興味がわいた人は、以下のステップで内容を見ることができます。
注意)stdio.hの内容を書き換えたりしないように気をつけてください。
1、stdio.hを選択状態にする。
![テン*シー*シー-8](https://stat.ameba.jp/user_images/20120116/15/xcc/80/24/j/o0446007811738200271.jpg?caw=800)
2、controlキーを押しながら、選択されたstdio.h上でマウスのボタンをクリック。表示されたコンテキストメニューの"Jump to Difinition"を選ぶとstdio.hの内容が表示される。
![テン*シー*シー-9](https://stat.ameba.jp/user_images/20120116/15/xcc/4d/29/j/o0243010511738200272.jpg?caw=800)
main.cに戻りたい時は、戻るボタンをクリックしてください。
![テン*シー*シー-10](https://stat.ameba.jp/user_images/20120116/15/xcc/14/b0/j/o0224009411738200273.jpg?caw=800)
stdio.hファイルを見たい場合は、stdio.hのどこでもいいのでcontrolキーを押しながら、マウスのボタンをクリックします。表示されたコンテキストメニューの"Show in Finder"を選ぶとFinder上でstdio.hファイルが表示されます。
注意)Finder上で、stdio.hの場所を移動させたりしないよう気をつけてください。
![テン*シー*シー-11](https://stat.ameba.jp/user_images/20120116/15/xcc/32/6f/j/o0222017811738200274.jpg?caw=800)
あらかじめ登録されたディレクトリはXcodeインストール時に用意されたりします。興味のある人は「C標準ライブラリ」、「Xcode ヘッダ検索パス」などのキーワードでインターネットを検索してみてください。
その次の行からが、main関数の記述です。引数や戻り値がある事に注目してください。
int main (int argc, const char * argv[])
{
・・・
{
・・・
main関数はOSから呼び出されるわけですが、パート1で確認したように、OSからいくつかの情報を受け取るための引数が決まっています。
型
引数の記述部のargcやargvが引数名です。それらの前に現れているintやconst char*は型といい、引数のメモリ上で占める区画の大きさをコンパイラに伝えます。
![$テン*シー*シー-12](https://stat.ameba.jp/user_images/20120116/15/xcc/0a/64/j/o0529010711738205565.jpg?caw=800)
バイト
以前お話ししたように、メモリは区画整理され番地付けされています。
この番地付けされた1区画(最小区画)を1バイトと呼びます。
1バイトは
0~255
までの範囲の数値しか記憶できません。
そのためアルファベット1文字を記憶する程度なら十分なのですが、ちょっとした計算をさせようとしたら、すぐに範囲外になってしまいます。
例えば家計簿をつけようと思った時に、0~255円までしか記憶できないのでは話になりません。
この問題を解決するため、コンピュータはメモリの最小区画を複数使って、より大きな数値を記憶できるようにします。
2区画(2バイト)を使うと0~65535までの範囲で、数値が記憶できるようになります。
![$テン*シー*シー-13](https://stat.ameba.jp/user_images/20120116/15/xcc/e3/70/j/o0531019311738228631.jpg?caw=800)
2進数
0~255や0~65535など、ずいぶん歯切れの悪い範囲だと思われるかもしれませんが、これはメモリが、0か1の状態しか記憶できない装置を、8個組み合わせて1つの区画を構成している事に原因があります。
![$テン*シー*シー-14](https://stat.ameba.jp/user_images/20120116/15/xcc/e4/dd/j/o0546017911738229987.jpg?caw=800)
どうやって、0か1の状態しか記憶できない装置を8個使って0~255の範囲の値を記憶するかというと、私たちが日常、数字で使っている位付けを利用します。
例えば、110という数字を考えてください。
私たちはこの数字の並びを、左から順に位付けして大きさを把握しています。
![$テン*シー*シー-15](https://stat.ameba.jp/user_images/20120116/15/xcc/39/d5/j/o0547013811738230678.jpg?caw=800)
0から初めて9まで進むと、その次を表す文字が無いので、1つ位をあげて10と表記するといった具合です。
![$テン*シー*シー-16](https://stat.ameba.jp/user_images/20120116/15/xcc/3c/c1/j/o0300042111738232181.jpg?caw=800)
同じように考えると、0か1の状態しか保存できない装置では0、1ですぐに、その次の値が表現できなくなり位をあげる事になります。
![$テン*シー*シー-17](https://stat.ameba.jp/user_images/20120116/15/xcc/13/e4/j/o0490048611738233054.jpg?caw=800)
このようにする事で、8つの記憶装置で0~255までの範囲の数値を記憶するわけです。
![$テン*シー*シー-18](https://stat.ameba.jp/user_images/20120116/15/xcc/3b/4d/j/o0547014111738233894.jpg?caw=800)
0と1しか使わない数の数え方を2進数と呼びます。これに対し、私たちが日常使う数字表現は10進数です。
2進数は以下のように表記します。これを2進表記と呼びます。
![$テン*シー*シー-19](https://stat.ameba.jp/user_images/20120116/15/xcc/75/2b/j/o0433009711738234986.jpg?caw=800)
ビット
また、0か1を記憶する装置の単位を1ビットと呼びます。
コンピュータの性能を表す単位として使われているビットのことです。
64ビットコンピュータというのは、64ビットの大きさの数値を一度に処理できるコンピュータという意味です。黎明期のパーソナルコンピュータは8ビットでした。
64ビットコンピュータというのは、64ビットの大きさの数値を一度に処理できるコンピュータという意味です。黎明期のパーソナルコンピュータは8ビットでした。
一つの区画、すなわち1バイトは8つの記憶装置から構成されるので8ビット、これを2つ合わせて16ピットにする事で、記憶できる数値の大きさは0~255から0~65535へと広がります。
![$テン*シー*シー-20](https://stat.ameba.jp/user_images/20120116/16/xcc/a2/eb/j/o0536010311738237418.jpg?caw=800)
2進表記の他に、プログラムの世界では16進表記もよく使われます。9の次の数字としてアルファベットのA、その次をBというふうにFまで用意し、1桁で0~15の値を表現する表記方法です。
![$テン*シー*シー-21](https://stat.ameba.jp/user_images/20120116/16/xcc/dc/64/j/o0479009211738238468.jpg?caw=800)
2進表記の4桁が、16進表記の1桁に綺麗におさまり2進数への変換がしやすい事から、文字数が増えがちな2進表記のかわりに使われる事がよくあります。
![$テン*シー*シー-22](https://stat.ameba.jp/user_images/20120116/16/xcc/30/93/j/o0511041011738239182.jpg?caw=800)
![$テン*シー*シー-21](https://stat.ameba.jp/user_images/20120116/16/xcc/dc/64/j/o0479009211738238468.jpg?caw=800)
2進表記の4桁が、16進表記の1桁に綺麗におさまり2進数への変換がしやすい事から、文字数が増えがちな2進表記のかわりに使われる事がよくあります。
![$テン*シー*シー-22](https://stat.ameba.jp/user_images/20120116/16/xcc/30/93/j/o0511041011738239182.jpg?caw=800)
バイト数を増やすほど、より大きな値を扱えるわけですが、その分メモリを消費し、CPUの処理にも時間がかかる事になります。「どんな大きさの数値にも対応!」というわけにはいきません。そのためプログラマは、引数や戻り値としてやり取りしたいデータの種類をコンパイラに指定する(指定された種類によってコンパイラが、2バイト使うのか、4バイト使うのかなどを決定)必要があります。
これが引数名の前に記述する型の役割りです。
![$テン*シー*シー-23](https://stat.ameba.jp/user_images/20120116/16/xcc/77/c8/j/o0540021211738240869.jpg?caw=800)
>以下次回