C言語

出典: フリー教科書『ウィキブックス(Wikibooks)』

Wikipedia
ウィキペディアC言語の記事があります。

情報技術> C言語


目次

[編集] 素朴な言語

C言語は1973年、AT&Tベル研究所で開発されたプログラミング言語です。昔から存在していながら、現在も多様な場面で活躍している言語です。この言語は可能な限り多くの機能を言語本体の仕様から切り離すことで、シンプルな言語仕様と極めて高い汎用性を持ち得ました。さらに、アセンブラとの関係が強く、高レベルの言語でありながらアセンブラのようなハードウェアに密着したプログラムや、トリッキーなコーディングが可能なことも、特徴の一つです。C言語はコンパイラを用いる言語です。C言語で書いたソースコードは、コンパイラでコンパイルしてマシン語に翻訳することではじめて実行可能になります。ちなみに、今日の新しいプログラム言語の多くはC言語の文法から派生しているため、C言語を知っているとそれらの言語の習得も容易になるかもしれません。

[編集] 概観

この項ではアウトラインを説明します。ここだけ読んでいまいち解らなくても、がっかりせずに次に進んでください。ここで簡単に説明した項目は、あとで改めて詳しく説明します。

[編集] 基本的な流れ

あれやこれやと説明する前に、とにかく一つのC言語のプログラムを紹介して、それを例に解説していったほうが理解できると思うので、まず例を紹介します。 プログラミング(プログラムを書くこと)はサンプルのソースコードを実際に書いて、実行しながら覚えたほうが解りやすいことが多いようです。百聞は一見にしかず、開発環境の整っている方には、実際にコンパイルしてみることをお勧めします。 ソースコードを書く際には大文字小文字の違いやスペースの位置にも気をつけてください。

int main()
{
return 0;
}

書いてコンパイルしてみたでしょうか? エラーは返ってこなかったでしょうか? エラーが返ってきた人は、もう一度スペースの位置や大文字小文字の違いなどを確認してみてください。エラーが返ってこなければ、あなたは今日から立派なC言語のプログラマ(プログラムを書く人)です。 コンパイルしてエラーが返ってこなければ実行できるので、そのまま実行してみてください。実行しても画面には何も変化がなかったと思います。この例は実行するとすぐ終了する儚いプログラムです。しかし最も簡単で基本となるC言語プログラムなのです。すべてはこのプログラムから始まる、重要なプログラムなのです。

この例文を説明する前に、C言語とはそもそもどういうものなのかを少し説明しなければなりません。 C言語とは計算機に命令を与えるための言語ですから、計算機への命令の羅列を記述していくことになります。 羅列の順番は、多くの種類のプログラム同様に、上から下へ順番に進行していきます。

しかしC言語はそれだけでなく、構造化という概念を取り入れています。これは、関数とよばれる名前をつけられた処理のまとまりをつくり、以後その名前を書くだけで纏められた処理を実行できるというものです。これにより、同じ処理を何度も書く必要がなくなってプログラムが簡潔になり、開発効率と読みやすさが向上します。 (構造化の概念のことを、構造化プログラミングと呼びます。プログラミング論で論じられることでしょう)

先ほどの例文は

int main()
{
 ...
}

という形をしていました。今後登場するすべてのプログラムが、実はこの形をしているのです。 ...の部分に今後紹介する様々な命令の羅列を並べることで、様々なプログラムを作ります。 この部分は、先ほどの構造化の話の「処理のまとまりをつくり」というその処理のまとまりそのものです。 「処理のまとまりをつく」ることを、関数と呼びます。

ということは、「関数とよばれる名前をつけられた」というのはどこに当たるのでしょうか? それが実は int main() に対応します。名前がつけられたとありますが、(ここでは頭ごなしに書きますが)mainという名前がつけられているのです。intの方の説明は後述します。 つまり、 int main(){...} というのは main という名前の付けられた関数(以後、これをmain関数と呼びます)です。 「以後その名前を書くだけで纏められた処理を実行できる」というのですが、この部分は後で説明します。

最初に実行されるC言語プログラムは、必ず int main(){...} という形をしています。これはなぜでしょうか?通常のC言語プログラムでは、プログラムの実行はmainという名の関数からスタートすることが定められています。そして関数内でreturnするとその関数を終了します。それが最初の例の3行目に書かれている return 0 の意味です。main関数が終了するとプログラムが終了します。

  • 注意

実はC言語プログラムでは、必ずmain関数から命令が実行されると決めているわけではありません。ホスト環境では、スタートアップルーチンからmain関数を呼ぶようになっています。しかしフリースタンディング環境ではどの関数を呼ぶかはその環境で決めればよいのです。必ずmain関数が必要ではなく、つまり必ずmain関数が存在するわけではありません。ホスト環境、フリースタンディング環境については[1]を参照してください。

ここまでで、既に頭が痛くなってきたでしょうか? 頭が痛くなったら理解しなくてもいいやと気軽に読んでください。一度に全てを理解しようと思わないで下さい。 (最初は斜め読みして、忘れた頃に再度この文書を読んで見てください。その時は驚く程に早く理解できると思います。) 実際にプログラムを書き、実行し、結果を目にすることで、やっと理解できると思います。 なので、読んだだけで理解するというのは、かなり難しいことです。実際にサンプルを書いてみることを再度お勧めします。 関数については重要な概念なので、後で詳しく説明をします。

[編集] 単語

最初の例での int 、 main 、 return 、 0 などは単語と呼ばれます。 単語というのは、ふだん私たちが使っている言語の単語と同様に、それ以上小さく分解することのできない、最小の単位です。

単語はスペース、改行を入れずに連続して書かなければいけません。また、大文字と小文字は区別します
例えば、 int を「in t」というように途中にスペースを入れて書くと、 コンパイラは int という一つの単語と認識せずに、in と t の二つの単語として認識し、コンパイラはエラーを返してきます。また return と Return と RETURN は全く別の単語として認識しますから、意味が変わってしまい、エラーを返してきます。 サンプルプログラムを打ち込む際には、このあたりに注意を払ってください。

[編集]

C言語でのとは、複数の単語からなっており、計算機に対しての一つ以上の命令を与える処理になります。上の例での「return 0」は一つの文です。

C言語では、文はセミコロンで区切ります。改行で1文とみなすBASIC等の言語と異なり、1つの関数を改行せずに1行にすべて書くこともできます。次の例は改行を節約してみた例(改行を説明するための例なので、内容については説明しません)です。

int foo(){return 0;}
int baa(){return 0;}
int main(){int a,b;for(a=0;a!=5;a++){b=((b+a)*b+a)/a;foo();baa();}return 0;}

[編集] インデント

1つの関数を改行せずに1行にすべて書くこともできますが、 それは読みにくいので、通常はインデントしながら改行して書き並べます。

どこで改行すればいいのでしょうか?  まず、絶対に単語の途中で改行してはいけません。途中で改行が入ることで、単語の解釈が変わってしまうためです。

文や計算式の途中で改行しても、コンパイラはエラーを返すことなくプログラムを生成します。では、次のようなプログラムを書いたとします(インデントを説明するための例なので、内容については説明しません)。

int main()
{
int
a,b;
for( a = 0 ; a != 5 ; a++ )
   {
   b =
(( b + a ) 
* b + a )
/ a;
   }
return 0;
}

上のプログラムはコンパイラに掛けると、エラーはなく実行できるコードを返してくれます。しかし、これでは読みやすくするために改行しているというよりも、わざと読みにくくするために改行しているように見えます。人間が読みやすくするためには、次のように文の切れ目で改行することが望ましいでしょう(ただし、1行が長くなってしまった場合には、人によっては途中でわざと改行を入れる人もいます)。

int main()
{
int a,b;
for( a = 0 ; a != 5 ; a++ )
   {
   b = (( b + a ) * b + a ) / a;
   }
return 0;
}

なお、人によって何種類かのインデントが存在します。 またこの本は多くの人によって書かれるため、登場するプログラムごとにインデントのスタイルが異なることがあります。 しかし、インデントはプログラムには何の影響も与えないことに注意してください。

[編集] コメント

インデントと並んで、プログラムを読みやすくするためのものがコメントです。コメントはプログラム内に書くことができます。 特に他人が書いたプログラムや自分が昔に書いたプログラムを、コメントなしに理解することは非常に難しいため、理解を容易にするためにもコメントは欠かすことはできません。 コメントは、コンパイルされるときには無視されます。つまり計算機には何も命令を与えません。

C言語でのコメントは /* で始まり */ で終わります。 /* と */ はそれぞれ2文字で一つの単語ですので、"/ *"のように途中にスペースを入れることはできません。

ただ、無視されるからといって、何を書いてもよいかというとそういうわけではなく、プログラムを後で読むときに理解しやすくする、という目的にあったコメントを書くように心がけましょう。

また一部のコンパイラでは // で始まり 改行 で終わるコメントも書くことができます。 この書式はC++で定義されたもので、C言語もC++も解釈できるコンパイラの多くで、この書式のコメントが認められているようです。 そのためか、ISOの最新のC言語の規格(通称C99)では // の形式のコメントも正式に加えられました。

以下が一般的なコメントのサンプルです。

#include <stdio.h>

int main()
{
 /*これがコメントです。*/
 
 /*
 *複数行に渡るコメント
 *も
 *可能です。
 */
 
 /*    /*コメントのネスト(入れ子)はできません!*/    */

 //一部のコンパイラではこの様なコメントも可能です。
 
 return 0;
}

[編集] 演算子

C言語(に限らずプログラム言語全般)とは、命令の羅列だといいましたが、命令とはいったいどんなものなのでしょうか? 

例えば、電卓を使って1+2の計算をしたい時、 人間は1キーを叩き、次に+キーを、そして2キーを叩きます。 このままだと(いま現在市販されている普通の)電卓は何もしないでしょう。 =キーを叩くことで、計算した結果を返してくれます。 では、「=キーを叩く」とはどういうことなのでしょうか?  実は、=キーは電卓に「計算しろ」というのを、命令している操作だったのです。

1+2の計算をしたいということをC言語で書いてみると、次のようなプログラムになります。 しばらく(ここから3つの節では)はこの例を使って、C言語を解説していきます。

#include <stdio.h>
int main()
{
 int a;
 a = 1 + 2;
 printf("1+2=%d\n",a);
 return 0;
}

int a や a = 1 + 2 は、それぞれC言語の文といわれるものです。 return 0 という文は既に説明しました。 この節では a = 1 + 2 の 1 + 2 の部分についてのみ説明します。

1 + 2 を数学的に解釈すれば「1に2を加える」ということです。まさしくC言語でも同様に、計算機に1に2を加えろという命令そのもののなのです。

この計算には普通の四則演算(+,-,*(掛け算×の代わりにアスタリスクを使います),/(割り算÷の代わりにスラッシュを使います))のほか整数のあまりを求める%などが使えます。+,-,*,/,%らのことを演算子と呼びます。

そして演算子と数(そして後から出てくる変数、関数値)を組み合わせて、計算する文(これを以下、計算式と呼ぶこともあります)を構成します。文なので、最後には必ずセミコロンが必要になります。

1 + 2;

と掛けば「1 に 2 を加えろ」という命令になります。複数の演算子を組み合わせることもできます。

1 + 2 - 3;

というように複数の演算子が並んだ場合には、左から解釈します。「1 に 2 を加えて、その結果(3)に 3 を引け」

しかし、

1 + 2 * 3;

というように+-と*/の演算子が並んだ場合、通常の数学での四則演算同様に、*/が先に解釈されて、+-が後から解釈されます。 従って「2 に 3 を掛けて、1 にその結果(6)を加えろ」となります。 もし、「1 に 2 を加えて、その結果(3)に 3 を掛けろ」と命令させたい場合には、小括弧を用いて

(1 + 2) * 3;

と書くことで、意図する解釈が行われます。

高校までの数学では小括弧で足りない場合には中括弧、大括弧を用いますが、C言語では、全て小括弧で対応します。

(2 + 3) * (3 + (4 - 2 * 3)) / 2;

[編集] 代入演算子と変数

この節でも次の例を用いて説明します。

#include <stdio.h>
int main()
{
 int a;
 a = 1 + 2;
 printf("1+2=%d\n",a);
 return 0;
}

前の節では a = 1 + 2 の 1 + 2 の部分だけを説明しました。なんだか、1 + 2 というのが計算に対応しているという説明を、前の節で受けたわけだから5行目は a = 1 + 2; じゃなくて 1 + 2; という文でもよさそうに思えてしまいます。

しかし、1 + 2; という文は「1に2を加えろ」というだけの命令です。実はこれだけでは「計算した結果をどうしろ」という命令は何にも書かれていないことになってます。 計算途中で文が終わった場合には、計算機は結果を忘れて、次の文の命令に取り掛かります(文が終わってない場合には、ちゃんと結果はどこかに保存されています)。 計算機は与えられた命令を、忠実に高速に行うことには長けてますが、与えられた命令以上のことは、絶対に行わないという欠点があります。 そのために、それより後の行でこの計算結果を使いたい時には、計算機が結果を忘れないようにする為に「計算した結果をどこかに保管しろ」という命令が必要になります。

まず、どこに保管するのでしょう?金庫は、普段私たちがお金を保管する時に使うように、計算機には、数を保管する時に、変数というものを使います。(変数は後に数だけではなく、文字などのデータも保管する時に使います) 金庫に「私の金庫」「ウィキメディア財団の金庫」というように名前があるように変数にも名前があります。 次に「保管しろ」という命令ですがこれは代入演算子=というものを使います。

先ほどの例の5行目で出てきた a = 1 + 2 はまさしく、「1 + 2という計算した結果をaという名前の変数に保管する」という命令になります。 「aという名前の変数」というのは長いので以下では変数aと呼びます。

金庫からお金を取り出せば、金庫の中のお金が使えるように変数に保管されている数も、取り出して使うことができます。 その例を次のプログラムを使って説明します。

int main()
{
 int a,b;
 a = 1 + 2;
 b = a + 1;
 return 0;
}

このプログラムの5行目の b = a + 1 という文は、「変数aに入っている数と1を足して、その結果を変数bに保管せよ」という命令です。 「変数aに入っている数」というのも長いので、これも以下変数aと略します。

最初の例プログラムをみるとなんだか6行目にも a というのがありますね。 つまり5行目で1+2の計算した結果を6行目で使いたい為に、最初の例プログラムでは、計算した結果を保管したかったわけです。

変数は、プログラミング言語には必ずといっていいほど存在する要素でありながら、最初につまづくポイントです。

さて、最初の例の4行目のint a;という文は、実は変数に関係するのですが、これは後述ということで、今はおまじないだと思ってください。

変数について、詳しくはC言語 変数を参照してください。

[編集] 各機能の解説

[編集] 制御構造

コンピュータ言語は実行の順序を変更する命令をいくつか備えていることが普通です。命令の実行順序を変更することを、実行制御と呼び、それによって作られる命令の固まりを制御構造と呼びます。 C言語にはいくつかの実行制御用の命令があります。ここでは、特に、if文、switch文、for文、while文, do - while 文、 goto文等について述べます。


[編集] if文

if文はある式が真であるかどうかを見て、その結果によって命令の実行順序を変える働きがあります。if文は次のように用います。

if (式){
    文
}

または、

if (式)
   文

上の形式では、文として複数の文を用いることが出来ます。一方、下の形式では、ただ1つの文しか用いることが出来ません。そのため、実行する命令が複数あるときには、必ず 上の形式を用いる必要があります。

int a = 1;
if (a == 1)
    a = 2;

この例では、aが1と等しいかどうかを調べ、aが1に等しい時には、aに2を代入しています。ここで、aが1と等しいかどうかを調べるために、a == 1と書いていることに注意が必要です。この部分を間違えてa = 1と書いてしまうと、その命令は、a = 1を実行してその式の値が真であるかどうかを判断する命令になります。実際には代入文は変数に代入を行なうと同時に、代入された値を返り値としてかえします。そのため、この条件は常に1をかえすため、常に真となり、元々の意図と異なった命令となってしまいます。このような間違いは気づきにくいことが多いので、注意する必要があります。

if文は、ある条件が満たされたときにある文を実行する性質をもつ命令です。一方、ある条件が満たされないときに文を実行するという命令も用いられます。これは、if - else文と呼ばれます。これの発展としてif - else if - else 文という命令も用いられます。一般的にはこれらは、

if (式1)
   文1
else if (式2)
   文2
else if (式3)
   文3
...
else
   文n

というように用いられます。この命令は、最初に1番上の式1の真偽が確認され、真であった場合には文1が実行されます。仮に式1が偽であったときには次に式2の真偽が確認され、これが真であったときには文2が実行されます。以下同じ様に式3以降がチェックされ、対応する文が実行されます。仮に全ての式が偽であったとすると、そのときには最後のelse以降の文nが実行されます。ただし、最後のelseは省略することも出来ます。

int a = 3;
if (a ==1)
    printf ("1\n");
else if (a ==2)
    printf ("2\n");
else if (a ==3)
    printf ("3\n");
else
    printf ("?\n");

上の例では、aの値が1, 2, 3のどれかであるかを順に調べています。このうちのどれでもないときには、?が出力されます。

else if文を用いる上で必ず知っておかねばならないことが、ぶら下がりelse文の問題です。ぶら下がりelse文とは、if文が連なったときに対応するelse文が曖昧になる問題です。この時、else文に対して最も近いif文が対応するif文として扱われます。インデントを間違えてつけるとわかりづらい間違いとなるので注意が必要です。

int a = 2;
int b = 1;
if (a < 6 ) 
    if (a > 4)
        b = 5;
else 
    b = 2;

この例では、if文が連なっており、ぶら下がりelse文の問題が現われます。まず、最初にはa=2が与えられています。このプログラムを見る限り、a < 6 の条件は満たされますが、a > 4 の条件が満たされないので、プログラムが終わった時点でbには1が代入されているように思えます。(else文はa<6に対応するものなので、ここでは実行されないように見える。)しかし、実際にはこのプログラムが終わった時点でbには2が代入されています。これは、else文が最も近いif文に対応するものとして解釈されることによります。つまり、このプログラムは実際には、

int a = 2;
int b = 1;
if (a < 6 ) 
    if (a > 4)
        b = 5;
    else 
       b = 2;

と書かれるべきもので、a>4の式が正しくないことからb=2の文が実行されます。このような間違いは時としてわかりづらいものとなるので、実際にプログラムを作成するときには注意が必要です。


[編集] switch - case 文

switch - case文はある式の値を見て、その値に対応する文を実行する命令です。実際にはこの命令はif - else ifを書き連ねることで代用することも出来ます。しかし、この命令を用いた方が何を行っているかが明確になる場合が多いので、この命令がよく用いられます。この命令は次のように用いられます。

switch (式){
    case 定数式1:
       文1
       break;
    case 定数式2:
       文2
       break;
    case 定数式3:
       文3
       break;
    ...
    default:
       文n;
}

この場合、式で与えられる値が式1, 式2などと一致するかどうかを調べ、一致したときには対応する文を実行します。もし、どの式とも一致しないときには、最後のデフォルトに対応する文nが実行されます。

break文はswitch文から直接出たい時に使います。break文を書かなかった場合、次のcaseを(そのcaseの定数式に関係なく)実行します。

[編集] while文

while文は繰り返しを行うために用いられる制御文です。while文は、


while (式) {
    文
}

もしくは、

while (式)
文

の形で書かれます。ただし、下の記法は実行したい文がただ1つの文である時にしか用いることが 出来ません。一方、繰り返して行いたい動作が複数個ある時には、常に上の記法を用いることが必要となります。 実際には、2種類の動作を繰り返して行いたい場合には、

while (式) {
  文1
  文2
}

だけでなく、

while (式) 
  文1
while (式) 
  文2

としても実行することが出来ます。しかし、実際には実行速度の問題から、ほとんど常に上の書き方が用いられます。実は繰り返しの処理をすることは割合手間がかかる作業で、出来る限り1つにまとめた方が実行が速くなります。そのため、特に事情がない限り、上の書き方を用いるとよいでしょう。

while (式) でいう式とは、返り値を持つものならどんなものでも用いることが出来るため、いろいろな表現を含むことが起こりえます。while文は、式の値が真ならば、対応する文を実行し、偽であるなら、実行しません。式の値が最初から偽であった時には、対応する文は1度も実行されないことに注意が必要です。

while文の基本的な用いられ方は次のようになります。

int a = 0;
while (a < 5)
  ++a;

この例では、while文の中で、変数aの値を変化させ、その値が5より小さい間aの値を1つだけ増やすという処理がなされます。これは、何回かの処理を行うための基本的な記法であり、非常によく用いられます。そのため、この内容をより簡潔に書くことができるfor文と呼ばれる文も、C言語は提供しています。

int a = 0;
while (a++ < 5);

これは、上の例と全く同じ内容を表していますが、少し書き方が複雑になっています。まず、++の演算子は、<演算子よりも優先度が高いことに注目します。ここで、変数の後につけられた++演算子は変数に1を加える演算を行うのですが、返り値として足し算が行われる前の変数の値を返すことに注目すると、 a++ < 5 は、最初は、 0 < 5 に対応することがわかります。次に、++演算子の効果でaに1が加えられます。0 < 5 が真であることから、対応する文が実行されるのですが、ここでは実行される分は空の文なので、再び、a++ < 5 が評価されます。このようにして、この記法が上の例と同じ内容を表していることがわかります。

while (1, 0);

これは、何も実行しない文です。()内で用いられた,は、カンマ演算子と呼ばれ、最も右の式の値を返り値とする働きがあります。ここでは、最も右の値は0であり、偽に対応するので、while文は1度も実行されません。

[編集] do - while 文

do - while文はwhile文と似た性質を持っていますが、while文と違い条件の真偽を見る前に、1度だけ対応する文を実行します。具体的には、

do {
 文
} while (式)

もしくは、

do 文 while (式)

のような書き方になります。後者の書き方では、例えば、

int a = 0;
do 文 while(++a <5);

のようにして同じ内容を5回繰り返す文を作ることが出来ます。しかし、このような書き方をするよりも後に述べるfor文を用いた方がよいでしょう。

[編集] for文

for文はwhile文と似た性質を持っていますが、より回数を指定して動作を繰り返す制御がしやすい記法を持っています。for文は次のように用います。

for (式; 式; 式) 文

ここで、3つの式は、左から初期条件、終了条件、カウンタの増減のように用いられます。最も頻繁に用いられるfor文の使い方は、

int i ;
for (i = 0; i<5; ++i)
  printf("aaa");

のように、回数を指定する条件を真ん中に置き、左側で用いる変数を初期化し、右側でループの数を数える変数を増加させます。この条件では、iは、0, 1, 2, 3, 4の5つになり、printf文は5回だけ実行されます。実行する文が複数ある時には、全体を{}で囲む必要があることに注意してください。

[編集] goto文

goto文は実行順序を入れ替える命令としては基本的なものであり、古くから様々な言語で用いられています。しかし、gotoを多用するプログラムは、プログラムがどのように動作するかを追うのが困難になるため、あまり好まれていません。逆に、gotoを多用せず、if文とwhile文だけを用いて様々なプログラミングが出来ることが知られているため、近年ではこれらの制御文だけを用いてプログラムを作成することも増えています。このような手法でプログラムを作成することを、(やや不正確な用語で)構造化プログラミングと呼びます。

実際にはgoto文はlinux kernelなどの有力なソフトウェアでもしばしば使われており、今後もなくなることはないでしょう。

goto文を用いるには、まずラベルを設定する必要があります。 ラベルは、次のようにして導入されます。

ラベル名:;

goto文はラベル名を用いて次のように用いられます。

goto ラベル名;
a = 0;
aaa:
 if (a >5)
       goto bbb;
printf ("1¥n");
++a;
goto aaa;
bbb:;

上の例ではgotoは、aaa, bbbというラベルに制御を移しています。goto aaa;の実行を止めるための条件はif文で与えられており、if文の条件が満たされた時には、制御はラベルbbbにとびます。ここで実際に行われていることは、while文の例等と同じで、ある定まった動作を任意の回数だけ行うことです。(ただし、ここではこれまでの例と違い、6回だけ文が実行されています。)しかし、このような目的のためには、whileやforを用いた方がより簡単であるといえるでしょう。

[編集] 関数呼び出し

この節でも次の例を用いて説明します。

#include <stdio.h>
int main()
{
 int a;
 a = 1 + 2;
 printf("1+2=%d\n",a);
 return 0;
}

さて、この例で4行目、5行目、7行目の3つの文は既に紹介しました(4行目は一応後述として先送りしましたが…)。残る6行目は何なのでしょうか?

今、この例は「1+2の計算をしたい」ということをC言語でやりたいという例でした。既に1+2の計算をするということは、前までの節でわかったのですから、6行目など必要なさそうに思えます。 では、そう思うならためしに6行目を抜いたプログラムを書いて実行してみてください。実行した結果はどうなりました?何にも表示されなかったはずです。

電卓では=キーを叩くことで計算結果を表示してくれましたが、C言語は違います。5行目では「足し算しろ(+)」「保管しろ(=)」という命令はあるのですが、実は6行目を抜いたことで「画面に表示しろ」という命令がなくなって、何にも表示されなかったわけです。つまり6行目は「画面に表示しろ」という命令なんだな~ってことが判ってもらえたと思います。 しかし、今までの + や = などとなんだか文の形が違いますね。実はこれは演算子ではありません。

実は「画面に表示しろ」という命令は、コンピュータにとって「足し算しろ」とか「引き算しろ」という命令よりもずっと難しい命令なのです。概略の一番最初で、関数とよばれる名前をつけられた処理のまとまりをつくり、以後その名前を書くだけで纏められた処理を実行できるという構造化の話をしました。

で、実は「画面に表示しろ」という命令は実に複雑な命令の組み合わせでできるものなので、それらの処理を実はまとめてあるのです。従って、その名前を書くだけで纏められた処理を実行できるようになっているのです。

その「名前を書くだけで纏められた処理を実行」する行為を関数呼び出し(または単に呼び出し)と呼びます。 関数呼び出しというのは「ここでこの関数を使いなさい」という命令と思ってください。つまり6行目は、「画面に表示する」関数printfをここで使いなさいという命令なのです。

さて、6行目の括弧の中は何を表しているのでしょうか?これを話すには「関数を作る」ことを説明してからの方がわかってもらえるでしょう。

[編集] 関数を作る

今までの例プログラムは

int main(){
 ... 
}

という形をしていて、これが関数(ひいてはmain関数という名前の関数)というものであるという話は以前にしました。

今まではmain関数だけを作ってきましたが、もう一つ別の関数をつくってみることにします。ま、今までmain関数というものは作ってきたのですからそれに似せて

#include <stdio.h>
int function1(){
 ... 
}
int main(){
 ... 
}

と書けば、別の関数は作れるのだろうな~(しかもそれはfunction1関数という名の関数)ってのは、賢い人なら察しがつくかと思います。

まさにその通りです。これで別の関数が作れました。下の関数がmain関数という名前の関数ならば、上の関数はfunction1関数という名前の関数であるのも察しの通り。そしてこの関数をこの場で使って欲しければ、main関数を次のように書けばいいのも以前の説明から分かると思います。

int main(){
 ... 
 function1();
}

[編集] 引数と返し値

さて、関数のご利益は、同一の処理を纏められるだけにありません。似た処理を纏めることができるというご利益があるのです。似たというのはどういうことでしょう。

例えば「今ある円の半径がわかっているときに、その円の面積を求めたい(円周率は3.14とする)」。計算方法は既に知ってますね?半径をrとすれば、円の面積は 3.14 * r * r。今、半径1としたときの円の面積を計算するプログラムとして次のようなものを書いたとしましょう。

#include <stdio.h>
int main()
{
 double a;
 a = 3.14 * 1 * 1;
 printf("%f\n",a);
 return 0;
}

では、半径1と半径2の円の面積を計算したいときにどうすればいいのでしょう?ここで関数の登場です。「半径1の円の面積の計算」と「半径2の円の面積の計算」と考えると二つの違う処理なのですが、「円の面積の計算」と考えると同じ処理だなーって考える事が出来ます。 そこで半径が1だとか2だとかを伝える必要があります。その時に使うのが引数と呼ばれるものです。

#include <stdio.h>
double calc(double r){
 double a;
 a = 3.14 * r * r;
 return a;
}
int main()
{
 double a,b;
 a = calc(1);
 printf("%f\n",a);
 b = calc(2);
 printf("%f\n",b);
 return 0;
}

引数というのは、calc関数の後ろにある()の中の変数rのことです。main関数の3行目のcalc(1)ではrが1となり、半径1の円の面積を計算し、5行目ではcalc(2)ではrが2となり、半径2の円の面積の計算を行っているということになります。 ちょっと待ってください。calc関数の中にある、return a;というのはなんなのでしょう。以前にも言いましたが計算機は、命令されたことしかやらないので、もしreturn a;がなければ、計算しっぱなしで計算したことを誰にも告げずに結果はどこかに葬られてしまいます(この話は以前に変数のところでもしたと思います)。なので、関数を呼び出したところに計算した結果を返してあげなければなりません。それが返し値と呼ばれるものです。

 calc(1);

で、「半径1の円の面積」の計算を行ってその結果が返ってくるのですが、これもまた変数の話どうよう、誰かが覚えておかなければ闇に葬られるので、それを変数aにいれることでmain関数の中で「半径1の円の面積の計算」の結果が使えるようになるのです。

画面に表示する関数printfでも、画面に表示するものを引数として、指定することによって、表示されるものを変えることができるのです。


[編集] ポインタ

ポインタはC言語の中でも割合難しいとされている機能です。しかし、ポインタの考え方はどの言語でも用いられており、プログラミングを行う上で非常に重要な概念です。

ポインタは、ある情報が蓄えられている場所を指し示す値を与えます。例えば、ある変数aに対して、aが蓄えている情報には必ず番地がふられています。ポインタはその番地の番号を蓄える変数型です。これらの番地はそれ自体を何らかの整数であるかのように解釈することが出来、これによって情報が蓄えられている番地をprintfなどを用いて実際に見ることも出来ます。しかし、実際にポインタを使う時には、細かい番地について知る必要はなく、どのような変数に対応したポインタであるかを把握していればすむようになっています。

具体的には、ポインタ型の変数は次のように宣言します。

型 * 変数名;

例えば、int型に対応するポインタは、

int * a;

などとして宣言されます。次に、ある値を変数を取り、その変数の場所を与えるには、

&変数名

という式を用います。この値はポインタ変数に代入することが出来、

int *a;
int b;
a = &b;

のように書くことが出来ます。これによって、bの場所がaによって指し示されています。aを用いて、bの内容を取り出すことも出来ます。具体的には、

*a

という式は、aが指し示す変数の内容(この場合はb)を取り出す式です。このような操作を、デリファレンスと呼びます。

ここで、ポインタの宣言の仕方で、

int *a, b;

などという書き方は、

int *a;
int b;

という内容と同じであることに注意が必要です。2つのポインタを宣言したい時は、

int *a, *b;

と書く必要があります。

ポインタは多くの場合いくつかの決まった用途で利用されます。ここでは、それらを順にあげていきます。

  • 関数に複数の返り値を持たせる

数値計算でよく用いられるFortran言語では、サブルーチンに与えられた引数は、メインルーチン内で用いられている変数と記憶場所を共有しており、サブルーチン内で与えられた変数の値が変更されると、その変更はメインルーチンにも伝わります。実際にはこれを用いて複数の値を関数内から返すことが出来る訳です。C言語の引数は基本的に値渡しであるので、サブルーチン内で値を変化させてもその変更はメインルーチン内では用いることが出来ません。そのため、複数の返り値を与える手法として、ポインタを用いる必要が出てきます。

具体的には、int型の答えを2つ返す関数fを定義する時には、

void f (int * res1, int * res2) { ... }

などと値を与えることが出来ます。ポインタ型の変数はこれをデリファレンスすることで、メインルーチン内の変数を変更できるので、このような使い方が出来ます。ここで、この関数を用いる時には、

int a, b;
f (&a. &b);

のようにして、a, b などの変数ではなく変数の番地を与える必要があります。

実際には、後に導入する構造体を用いると、例えば、

struct aaa {
  int res1;
  int res2;
}

のように新しい型を定義することが出来るため、値を返す時にも、

struct aaa f ();

の様にして明示的に複数の変数を返すことも出来ます。これらは状況によって使い分けるとよいでしょう。

  • 大きな構造体を関数に与える

C言語は基本的に値渡しの言語であるので、大きな構造体を関数に与える時には、それらを全てコピーして用いようとします。これは時間のかかる作業となるので、より高速な処理を行いたい時には、構造体のポインタを与えることで、このコピーの作業を行わないようにしむけることが出来ます。具体的には、

void f (struct aaa * a) { ... }

のように関数を定義することで、このような方法を用いることが出来ます。

  • 用いる領域を動的に定める

C言語にはmallocという標準関数があります。この関数は、利用者が用いることができるメモリ領域から、指定された大きさの領域を切り出す関数です。この関数は切り出した領域のポインタを返すため、それらの領域を用いるために、ポインタを用いる必要があります。mallocは多くの場合、sizeof演算子と組み合わせて用いられます。sizeof演算子はC言語の型を引数に取り、型の大きさを表すバイト数を返すオペレータです。例えば、int型へのポインタを返すmalloc文は次のようになります。

#include <stdlib.h>
int *a;
a = malloc (sizeof(int));

mallocは、用いたい領域の大きさをプログラムの中で定めたい時などに用いられます。例えば、利用者の入力を見て用いる領域を変化させる場合等がこの例です。 また、mallocによって切り出された領域は、プログラマによって切り出された領域であるので、例え使われなくなっても自動的に解放されることはありません。(ただし、プログラムが終了した時は別で、この時には全ての領域が解放されます。)そのため、切り出した領域を解放する方法が必要となります。この目的では、freeという標準関数を用いることが出来ます。ここでは、上の例の続きで、

free(a);

とすることで、切り出した領域を解放することが出来ます。

  • 関数ポインタ

C言語では、関数へのポインタを用いて、関数を変数に'代入'することが出来ます。これは、別の関数に、引数として関数を与えたい時等に用いられます。例えば、標準関数qsortは、引数として2つの要素の比較を行う関数を取ります。このような要求を満たすために、関数へのポインタが用いられます。関数へのポインタは次のように宣言されます。例えば、void型の返り値を持ち、引数を持たない関数へのポインタpfは、

void (*pf) ();

となります。ここで、*pfの周りの()は、演算子()の優先順位が、演算子*の優先順位より高いことから、省くことは出来ません。

    • 注意

演算子の優先順位は、UNIX系のOSでは、

$man operator

で見ることが出来ます。

例えば、関数void f () {} が定義されている時には、変数pfに対して、

pf = f;

という代入が出来ます。ここで、ポインタへの代入なので、右辺は&fとすることも出来ます。しかし、上の代入と、&fとした時の動作は、同じになります。ポインタを用いて関数を呼び出すには、デリファレンス演算子*を用いて、

(*pf) ();

となります。

関数へのポインタは、C言語を用いたオブジェクト指向プログラミングなどで頻繁に用いられます。

  • オブジェクト指向プログラミング

オブジェクト指向プログラミングは、データそのものに動作を与えるプログラミング手法です。例えば、スタックを考える時に、我々が行う動作は、push(置く)とpop(取り出す)だけで、そのような動作と適切なデータ領域を持ったデータを作れば、それを汎用的に用いることが出来ます。

オブジェクト指向プログラミングは、Java, C++などのオブジェクト指向をサポートする言語を用いないと行うことが出来ないと思われがちです。しかし、実際にはC言語でもオブジェクト指向を用いたプログラミングは割合普通に行われます。重要な例では、X Window System のアプリケーションや、Gtk+のウィジェット等は、C言語を用いてクラスを定義することで作成されています。

具体的には、C言語の構造体を用いてデータと関数へのポインタを1つにまとめます。先ほどの例では、

struct stack {
  int * data;
  void (*push) (struct stack *, int);
  int (*pop) (struct stack *);
}

の様にして、data領域とpushとpopの2つのメソッドを持ったオブジェクトを作成することが出来ます。ただし、Javaのように、変数に対するアクセス制限を行うことは一般には出来ないため、これらは構造体を扱う人間が注意する必要があります。

[編集] 配列

配列は複数のデータを蓄える性質を持ったデータ型です。これらは同じ種類の型のデータを複数記録する時に用いられます。

例えば、整数型のデータをいくつか記録しておきたい時に、整数型の変数を複数定義することはあまり勧められません。これは、変数ごとの名称が複雑になることがあるからです。このようなときには、配列を用います。配列は次のように定義します。

型 変数名 [大きさ]

実際に値を代入したり、代入した値を用いる時には、

変数名[添字]

の記法を用います。ここで、添字は整数で表します。ただし、先頭の添字は1ではなく0から始まることに注意が必要です。そのため、大きさ=xとしたとき、許される添字は0, 1, ... ,x-1です。

int v[5];

宣言は上のように行います。これは整数型の配列です。

v [0] = 3;

これは代入の例です。

v[1] = 5; v[3] = 6;
printf ("%d", v[1]);

printf文は配列の中身を取り出す方法の例です。ここでは添字1に対応する要素を取り出しています。許される添字が0, ... ,4であることに注意が必要です。

配列に含まれるデータは数が多い場合があり、それぞれを個別に代入していくのは面倒な作業になります。そのため、これを効率的に行う手法が用意されています。例えば、1,2,3が代入された整数型の配列v2を得るためには、

int v2[3] = {0, 1, 2};

と書くことで代入がなされます。

配列はポインタと深い関係があります。まず、ポインタは演算を通じて配列同様複数のデータを管理することが出来ることを述べます。次に、ポインタを用いて、配列を扱えることについて述べます。

まず、ポインタは演算を行うことが出来ます。具体的には、int型のポインタpに対して、p+1もint型のポインタです。これは、元のポインタpが指す場所から、int型の大きさだけずれた場所を表します。この性質とmallocを用いて、複数のデータを扱うことが出来ます。int型に対応する大きさを確保するには、sizeof(int)を用いることを述べました。ここでは、複数のint型変数を確保するために確保したい変数の数をかけ算した値をmallocします。具体的には、

int * p;
p = malloc(5*sizeof(int));

とすると、5つの整数だけの場所が確保されます。このとき、

*p = 1; 

としてmallocされた領域の先頭に1を代入することができます。それ以降の領域に書き込む時には先ほどのポインタの演算を用います。例えば、2番目の領域に書き込む時には、

*(p+1) = 3;

とすると2番目の領域に書き込みがなされます。このように配列だけでなくポインタを用いても、複数のデータを管理することができることがわかります。

次に、配列の内容をポインタを用いて得ることが出来ることを述べます。配列で蓄えられているデータは、配列の最初に対応する番地から順に並べられています。そのため、配列の番地に対応するポインタがあれば、そのポインタを用いて配列の中身を取得することが出来ます。具体的にはint型のポインタpvとint型の配列vを用いて、 int *pv; int v[3]; pv = v; のようにポインタpvに配列vを代入することが出来ます。これは、vの先頭の場所をpvに代入する働きがあります。このとき、v[0]と*pv, v[1]と*(pv+1), ... などがそれぞれ同じ場所を表しています。

通常の利用では配列とポインタを混ぜて用いると混乱の元になることが予想されます。そのため、特殊な状況を除いて配列なら配列だけを用いる方がよいでしょう。

[編集] 文字列

[編集] 基本的な用法

既に何度もC言語で文字列を扱ってきました。ここでは、それらをまとめ、またいくつかの標準関数について扱います。

文字列は、文字を複数個組み合わせたものとして扱います。C言語では通常文字といえば1バイトで扱われる文字のことを指します。一方、日本語等の文字は通常の用いられ方では複数のバイトで表される情報なので、ここでいう文字の扱い方とは異なった扱いを受けます。ここではまず、1バイトで表される文字を扱います。文字コードについて詳しくはアスキー高等学校情報Cなどを参照してください。


1バイトで表される代表的な文字コードとして、アスキーコードがあげられます。これは128個の文字に対応する数値を定めており、それぞれの文字に対する対応は、アスキーや、UNIX系のシステムでは、

$man ascii

によって見ることが出来ます。C言語では、これらの文字を扱うために、

'文字'

という表現を用います。

'a'
'b'

などは文字型の定数です。

更に、一般には文字は複数組み合わせて用いられます。複数組み合わせられた文字のことを、文字列と呼びます。ここでは、複数のデータを扱うために、ポインタが用いられます。定数としての文字列は、

"文字列"

で定義されます。

"abc"
"abd ef"

などが文字列の例です。スペースはアスキーコーディングで指定されているので、Cの文字列の一部として扱うことが出来ます。ここで、C言語では、これらの文字列の長さを見るための手法として、文字列の最後に

'\0'

という文字を付加しています。 これはヌル文字と呼ばれ、アスキーでは数値0に対応する文字です。そのため、文字列の長さは文字の数に1だけ加えた数になります。

これらの定数を代入するためには、charという型(文字型)を用います。文字、文字列はそれぞれchar, char *に代入することが出来ます。

char c;
c = 'a';
char * s;
s = "abc"

[編集] ポインタのポインタ

C言語でかかれたプログラムを実行する時に、コマンドラインからプログラムに引数を与えることが出来ます。

$./a.out aaa bbb

この例は、UNIX系のシェルを用いた例です。ここでは、a.outという名のプログラムに対して、第1引数aaaと、第1引数bbbを与えています。

これらの引数は、プログラム中で扱うことが出来ます。これは、プログラムの動作を実行時に制御する目的等で用いられます。引数を用いるためには、main関数で次のような宣言を行います。

int main(int argc, char ** argv){ ... }

ここで、argcはプログラムの名前自身も含めた引数の数で、argvは、プログラムの名前と引数を文字列として与えています。

argvのデータ型はcharのポインタのポインタとなっており、やや扱いが難しい型です。例えば、argvをデリファレンスするとプログラムのの名前を表す文字列へのポインタが得られます。これは、argvはそれぞれの文字列へのポインタを蓄えたデータであり、先頭に蓄えられているデータは関数の名前であるからです。 第1以降の引数を表す文字列へのポインタを得るには、argvを先に進めることが必要です。このためにポインタの演算を用いることが出来ます。例えば、第1引数へは、*(argv + 1)で文字列へのポインタを得ることが出来ます。以降第n引数へは、*(argv + n)で文字列を得ることが出来ます。

通常文字列は文字列として扱われることが多いのですが、文字列から文字を得ることも出来ます。上の例では、

**argv

は、*argvで表される文字列へのポインタの最初に位置する文字を返します。上の例では、./a.outの先頭にある.が返されます。同様に

*(*argv +1)

は、./a.outの2文字目である/が返されます。このようにして引数として与えられた全ての文字を得ることが出来ます。

[編集] 文字列の標準関数

文字列に関しては様々な標準関数が用意されています。そのうちのいくつかは簡単に書くことが可能です。ここでは、いくつかの簡単な文字列関数を紹介します。

文字列に関する関数のヘッダファイルは、string.hと呼ばれます。そのため、文字列の関数を用いるには、

#include <string.h>

と書くことが必要になります。stringにはいくつかの関数が含まれています。詳細な説明は個々のマニュアルを参照してください。UNIX系のシステムでは、

$man string

で関数の内容を閲覧できます。

ここでは、標準関数の1つである、strlenを取り上げます。strlenは、char *型の引数を取って文字列に含まれる文字数を数えます。数えた数は、関数の戻り値として返されます。基本的な使い方は、

int a;
char * s = "abc";
a = strlen(s);

となります。ここでは、sには3文字が蓄えられているので、aには3が代入されます。

実際にはstrlenは、与えられた文字型ポインタから始めて'\0'が出るまでにいくつの文字があるかを数えています。そのため、これを実際に書こうとすると、割合短く収まります。

int st_len (char *c ) {
 int i=0;
  while (*c != '¥0'){
   ++i;
   ++c;
  }
 return i;
}

ここでは、受け取った文字列へのポインタcを進めながら*cが'\0'でないかを調べています。同時に整数iをインクリメントし、文字の数を数えています。 このように文字列の標準関数のいくつかは割合簡単に書くことが出来ます。しかし、実際には自分で書く手間を考えると、標準関数を用いる方がよいでしょう。

[編集] プリプロセッサ

C言語ではプリプロセッサと呼ばれるプログラムがコンパイルの過程で用いられます。これは、コンパイルされるプログラムの機能を制御する時などに用いられます。また、プリプロセッサが扱う命令の中にはマクロと呼ばれる機能を用いるために使うことが出来る命令もあるため、プリプロセッサはいくつかの方法で用いられるプログラムとなっています。ここでは、よく用いられる例について解説します。

プリプロセッサの命令でよく用いられる命令には、

#if ... #endif
#include
#define

などがあります。

  • #if ... #endif

これは、コンパイルの際にこの命令で挟まれた部分を取り除くかどうかを定める命令です。#ifの直後に真偽が定まる式が続き、それが真である時には、#if ... #endifは、命令だけが取り除かれます。しかし、偽だった時には、#if ... #endifの命令を含めて、その間に書かれているプログラムの全体が取り除かれた上でコンパイルが行われます。

#if 0
aaa
#endif

これは、不必要な命令をプログラム中から取り除く例です。不必要な命令としては、コメントや使われなくなった関数などがあげられます。これは、/* */を用いたコメントとほとんど同じ用いられ方をします。

  1. if ... #endifに似た命令として、#ifdef ... #endif、#ifndef ... #endifがあります。これらは、#defineやコンパイル時のオプションによって与えることができるパラメータが設定されているかどうかを見分け、それによってこれらの命令に挟まれた部分をコンパイルするかどうかを定めます。
#ifdef DEBUG
printf (...);
#endif

これは、デバッグを行っていて、プログラムで実行されている内容が知りたい時によく用いられる手法です。デバッグを行いたい時だけ、DEBUGという名前の#defineを定義します。

#ifdef i386
...
#endif

これは、(おそらく)インテルCPUを用いているコンピュータにおいてだけ用いられる機能を挟むために用いられます。対応するCPUに対してコンパイルを行う時には、i386という#defineを用います。 このようなコンパイル手法は、あらゆるハードウェア間の互換性を実現するために用いることができ、実際に大きなオープンソースプロジェクトでは、必須の技術となっています。

  • #include

これは既に何度か出てきた命令です。#includeは、

#include <ファイル名>

または、

#include "ファイル名"

の形で用いられ、ファイル名で指定されたファイルをまるごとその位置に読み込む働きをします。これは、多くの場合、プログラムで用いる関数のヘッダファイルをインクルードするために用いられます。

実際には、ヘッダファイルには関数のプロトタイプ宣言が書かれているのが通例です。プロトタイプ宣言は、用いられる関数の引数の型と数、及び戻り値の型だけが与えられた形です。実際の関数の中身は、ここでは与えられません。例えば、標準関数を用いるためには、それらは既に実行ファイルとしてコンパイルされていることが通例です。この時、それらを呼び出すためには、それらにどのような引数を与える必要があるかなどを指定する必要があります。プロトタイプ宣言はそのような情報をコンパイラに与えるために用いられます。

用いる関数のプロトタイプを知っているなら、 (UNIX系のシステムでは、$man 命令の名前 で見ることができます)ヘッダファイルをインクルードしなくても、プログラムの最初の方で用いる関数のプロトタイプを直接書き込むことで、#include なしですませることもできます。しかし、通常は#includeを用いるのがよいでしょう。

#include <stdio.h>

これは、stdio.h(standard Input/Output)に関するヘッダファイルをインクルードするための命令です。UNIX系のシステムでは、このヘッダファイルは、/usr/include/stdio.hに置かれています。

#include "aaa.txt"

<>ではなく、""で囲まれたファイル名が用いられた時には、インクルードされるファイルの場所として、最初にカレントディレクトリ内が探されます。また、#includeは、対応するファイルが.cや.hなどのよく用いられる拡張子を持っているかをチェックすることなくインクルードを行うので、注意する必要があります。

  • #define

これは、マクロや定数を定義するための命令です。これは用いられ方によっては混乱を引き起こすため、C++では、この機能を用いなくてもよいようにconst, inline, テンプレートなどの機能が導入されました。これらの機能はより新しいC99と呼ばれるCの標準ではCの側にも取り入れられ、マクロを用いる必要性は減少しています。しかし、依然としてよく用いられる機能でもあります。

この命令は、#defineの後に書かれた語句を、定義された命令によって置き換える働きをします。これは、関数などと似た働きですが、#defineの場合はコンパイルされる命令自体が書きかえられる点が関数の場合と異なっています。(ファイルの内容は書きかえられないことに注意。)特に、関数に似た性質を持つ置き換えを、マクロ関数、もしくは単にマクロと呼ぶことがあります。

定数

この命令は、定数を定義するためによく用いられます。

 #define ARRAY_SIZE 100 #defineの基本的な使い方です。これは、この命令が置かれた場所以降でARRAY_SIZEという語句が用いられたとき、これを100に置き換える働きをします。これは、(おそらく)配列の大きさを後で変更する目的で用いられています。ARRAY_SIZEは、次のように用いられます。

int v[ARRAY_SIZE];

ここで、コンパイルされる時には、プリプロセッサの働きで、この部分は

int v[100];

に置き換えられます。

定数をマクロによって定義する時には、対応する語句は全て大文字で書くことが通例です。しかし、実際には小文字で書いても問題はありません。

マクロ関数

マクロ関数は関数呼び出しをさけることなどを目的として用いられます。現代的には似た機能を持つインライン関数を用いた方がよいでしょう。インライン関数についてはC++などを参照してください。 #defineを関数として用いる場合には、

#define 関数名(引数名1,引数名2, ... ) 置き換えたい命令

の形を取ります。

[編集] キーワード

▲C言語のキーワード一覧
auto break case char const continue default do
double else enum extern float for goto if
int long register return short signed sizeof static
struct switch typedef union unsigned void volatile while

キーワードとはあらかじめ制御のための単語として規定されており、変数名その他に使用できない文字列のことです。C言語では殆どの機能がライブラリによって言語の機能から切り離されているため、言語自体のキーワードはとても少なくなっています。

[編集] return

呼び出しもとの関数にreturnの後ろの数値または変数の値を渡して関数を終了します。

[編集] char,int,long,float,double,void

変数宣言時に用いられる型名と呼ばれる物で、型によって1つの変数に割り当てられるメモリのサイズと割り当てられたメモリ上での値の表現方法が異なります。C言語では宣言という作業でこれから使用する変数を使用する前に明示する必要があり、これはその際に使用されます。

[編集] long,short,signed,unsigned,extern,static,volatile

変数宣言時に用いられる修飾詞と呼ばれる物で、その変数に様々な条件を付けて宣言するために用います。

[編集] if,else,switch

この後で扱う条件分岐を作るためのキーワードです。条件分岐はその時点での変数の状態等によって、これから行う処理を分岐する処理です。

[編集] do,while,for

この後で扱うループを作るためのキーワードです。ループとは特定の条件が成り立つ限りプログラムの中の指定された範囲を繰り返す処理です。

[編集] goto

無条件にプログラム上の特定の箇所へジャンプします。C言語の処理の流れを無視して移動するため、C言語誕生以来バグのもとになるとされあまり使われていません。

[編集] 計算式に始まる

言語というと文章のような物を連想するかもしれませんがプログラムに用いられる言語という物は数式の延長です。全ては計算であり、プログラムは計算手順を指定しているに過ぎません。というわけで、C言語でコンピュータに計算をさせてみます。尚、プログラムという物はサンプルのソースコードを実際に実行しながら覚えたほうが解りやすいことが多いようです。百聞は一見にしかず、開発環境の整っている方には実際にコンパイルしてみることをお勧めします。とりあえず、どんなプログラムを書くにもまずmain関数を用意してください。
int main()
{

}

[編集] 変数を宣言する

(修飾詞(任意)) 型名 変数名(=初期値(任意));
e.g.
long double mee = 40.1;
unsigned short int woo;
extern char foo = 2;
int main()
{
int laa;
}

3+2は5です。雨が降っても月に行っても解は常に5です。定数のみで構成される式はどういう条件下で実行しても常に解が同じなのです。解が常に定まる式をわざわざ実行する度に計算する必要はありません。よって殆どの場合式には変数が含まれます。というわけで早速変数を使ってみたいところですが、C言語では変数を使う前に宣言する必要があります。宣言の書き方は左の通りです。
←とりあえず宣言してみました

[編集] int型

整数型。もっとも標準的な整数を扱うことができる型という位置付けで、データ長は基本的にそのマシンのアーキテクチャのビット長と関係があります。通常16bit以下のCPUのマシンでは2バイト(16bit)、32bitCPUのマシンでは4バイト(32bit)、64bitCPUのマシンでは8バイト(64bit)となります。shortを付けると通常時と同じかそれより1段階小さい容量に、longを付けると通常と同じかそれより1段階大きい容量になります。が、そうなっていない環境も存在するため、互換性を考慮する場合int型が特定のサイズであることを期待してはいけません。

[編集] char型

文字型。半角文字1文字を保持するために作られた型です。C言語は文字を内部では文字コードの番号で数値として扱います。この時C言語は保持されている数値がただの数値なのか、文字コードなのかを見分ける手段を持ちません。よってchar型は整数型として使用することも可能です。多くの環境では半角文字コードは8bitなためchar型は1バイト(8bit)ですが、実際には9bitや16bitになっている環境も存在します。

[編集] float型

単精度浮動小数点型。小数を扱える型で、一般的な環境ではIEEE 754に準拠し、サイズは32bitです。後述のdouble型より処理が速いと誤解している人もいるかもしれません。確かに、浮動小数点演算がソフトウェアで行われていたような時代は、float型を使ったほうが速かったようです。しかし、今日のCPUはdouble型と同じかそれ以上の高精度な浮動小数点演算をハードウェアでサポートしており、そういった環境ではfloat型は計算のたびにdouble型に変換されるためdouble型より遅くなります。 メモリ容量がぎりぎりな場合を除いてdouble型を使用した方がいいかもしれません。

(補足:ここで言う「高精度な浮動小数点演算」とはsinやsqrtなどの数学関数計算のことです。 足し算や掛け算では速度に差はなく、変換もされませんので誤解されぬよう。 大きなデータを扱った時の四則演算ではキャッシュのヒット率によりfloatの方が速い場合があります。 SIMDによって、doubleの倍の数のfloatを同時に計算できるプロセッサでは倍の速度が出せる場合があります。)

[編集] double型

倍精度浮動小数点型。小数を扱える型で、一般的な環境ではサイズは64bitです。サイズが大きい分扱える値の精度がfloat型より上がります。また、longを付けることでさらに精度の高い小数が扱えるようになりますが、この時のサイズは80bitの環境と128bitの環境があるようです。

[編集] signed修飾子とunsigned修飾子

ここで、signed修飾子とunsigned修飾子の意味を考えましょう。C言語の変数は型に規定された一定の数までしか表現できません。

標準的なbit数 範囲
char 8 -128~127又は0~255
short 16 -32768~32767
int 16又は32 16bitの時は-32768~32767、32bitの時は-2147483648~2147483647
long 32又は64 32bitの時は-2147483648~2147483647、64bitの時は-9223372036854775808~9223372036854775807


この中で、short、int、longの3つの型は既にsigned修飾されています。signed修飾とは「この変数は符号付きですよ」という修飾です。逆にunsigned修飾を行うと「この変数は符号なしですよ」という修飾になります。そうすると、範囲は次のようになります。

範囲
unsigned short 0~65535
unsigned int 16bitの時は0~65535、32bitの時は0~4294967295
unsigned long 32bitの時は0~4294967295、64bitの時は0~18446744073709551615


char型が符号付きと扱われるか符号なしと扱われるかはコンパイラに依存します。このため、明示的にどちらかに扱わせたい場合はsigned、unsigned修飾子を付けてください。

また、float、double、long double型にはsigned、unsigned修飾子は付けられません。

[編集] 関連項目

ヘルプ