C言語入門
Introduction of The C Programming Language
第2版 著作:日台健一 1996-1997

 

目次
はじめに

C言語は、(例えばBASICやFortranと比べて)非常に機械に近い非人間的な言語です。よって、コンピュータの内部構造まで教えたくなるのですが、ここではそこをぐっとこらえます。本稿の目標としては、大学の簡単な課題がこなせる程度になることとしましょう。(言語は道具であり、目的ではありません。C言語のすべての機能を網羅するよりC言語を学習するものにとって何が必要かを考えて編集しました。)

第1の難関

「C言語を覚えたい」という人にとって、最善の修得方法は、とにかくいろんなプログラムを見て自分で書いてみる、だと思います。興味があれば自分一人でも(資料さえあれば)自然に覚えるものです(私がそうでした)。

C言語を喋るということはコンピュータと対話することであって、そこにコンピュータがなければ意味がありません。何が言いたいかというと、例えば、「ディレクトリって何?」という人はいると思いますが、その人はまず何らかの形でコンピュータと関係し、コンピュータの知識を付ける必要があります。「コンピュータは使わないけどとりあえずC言語を覚えようかな」というのは、あまり意味のないことで、恐らく上達も難しいと思われます(まあ、個人的な教授はしますが)。

プログラミングレッスン1

この章では、一通りの基本的な知識を摘んでみることにします。詳しい解説は省きますが、だいたいの動作は理解できると思います。最初はとにかく簡単なプログラムを読むことで、そこに書いてあるコードが何を意味するのかを理解してください(説明文よりもソースコードをよく読んでください)。

簡単なプログラム
#include <stdio.h> /* この行は必ず書く、と覚える */ int main() /* 次からメインのプログラムですよ */ { /* 中カッコの中がプログラム本体 */ printf("hello, world\n"); /* hello, world と表示して改行する */ return 0; /* プログラムを終了します */ } /* ちなみにこれは注釈(コメント)です */

画面に好きな文字を表示するだけのプログラムです。コメントをよく読んでください。文の最後にはセミコロン(;)を書きます。また、printf()中の文字列の\nは、改行を表します。以下は出力結果。

hello, world
変数を使う
#include <stdio.h> int main() { int a, b=2; /* 整数型の aとbを宣言して、bを2で初期化 */ char c; /* 文字型のcを宣言 */ a = 3; /* 代入。変数bのように宣言時に初期化してもよい */ c = 'A'; /* cに、文字Aを代入( cは整数型でなく文字型である) */ printf("a = %d\n", a); /* a = 3 と表示して改行。%d は、整数 a と置き換わる */ printf("b = %d\n", b); /* b = 2 と表示して改行。%d は、整数 b と置き換わる */ printf("c = %c\n", c); /* c = A と表示して改行。%c は、文字 c と置き換わる */ /* 命令の前の空白は、TAB キーで入力するのが普通 */ return 0; }

今度は変数を使います。変数とは数を入れておける箱のようなものです。変数を使用するには、(中カッコの中の)一番はじめに宣言を書かねばなりません。宣言は、同じ型(「よく使うデータ型一覧」を参照)であるならカンマ(,)で区切っていくつでも書くことができます。変数の名前には、アルファベット、数字そしてアンダースコア(_)を使うことができます。ただし、先頭の1文字に数字は使えません。

変数の値をprintf()で表示するために、%dや%cといった文字列を使用しています。%で始まる文字列は、カンマで区切った後に書いてある変数の値と置き換わります。

よく使うデータ型一覧

扱える範囲 (16bitマシンの場合) printf中の表記
char -127〜128(文字) %c(文字を表示), %d(値を表示)
int -32768〜32767 %d
long -2147483648〜2147483647 %ld
float 有効桁数6桁の実数 %f(%e)
double 有効桁数15桁の実数 %f(%e)

型の前にunsignedと書くことで、扱える範囲を正の整数にすることができます(char/int/longの場合)。そうすると、負の整数は扱えなくなりますが、その分、正の範囲が2倍使えるようになります。unsignedをprintf()で出力するとき、intは%u、longは%luと表記します。

ワンポイント〜文字と文字列

とつぜんですが、文字はシングルクォーテェーション(')で囲み、文字列はダブルクォーテェーション(")で囲みます。char型に入るのは文字で、文字列ではありません。ここでちょっと難しくなりますが、文字について説明します。

一言で説明するならば、「文字は数字である」となります。例えば、'A'という文字は65という数字と等価であり、'B'という文字は66と等価であるのです。はたまた、'1'は49だったりします。前のソースで、c='A'という式がありますが、これはc=65と書き換えても全く同様に動作します。

文字にはこのような性質があるのですが、文字列はまた別の概念で表現されています。これは大変難しいので、本稿での説明は省略します。

計算をしよう
#include <stdio.h> int main() { double radius = 10; /* 半径 */ double pi = 3.1415926; /* π */ double circumference; /* 円周(こんなに長い名前も使える) */ double area; /* 面積(変数名には誰が見ても分かる名前を)*/ circumference = radius * 2 * pi; area = radius * radius * pi; printf("半径 = %f\n", radius); /* double を表示するには %f */ printf("円周 = %f\n", circumference); printf("面積 = %f\n", area); printf("\n\n"); /* 改行を2つ */ printf("半径 = %f\n", radius = 20); /* 今度は半径20(この場で代入)*/ printf("円周 = %f\n", radius * 2 * pi); /* この場で計算も可 */ printf("面積 = %f\n", radius * radius * pi); /* 式は文中のどこでも書ける */ return 0; }

今度は整数でなく、実数(小数点以下がある数)の計算をするために、double型(floatでも可)を使用しています。

また、計算の優先度もあるので、加算を乗算より先に行いたいときはカッコでくくるようにしましょう。

よく使う演算子一覧

算術演算
*乗算
/除算
+加算
-減算
%剰余(余り)
++1加算(単項)
--1減算(単項)
*=積の代入
/=商の代入
+=和の代入
-=差の代入
%=余の代入
論理/ビット演算
&&論理積(AND)
||論理和(OR)
==等しい
!=等しくない
<より小さい
<=以下
>より大きい
>=以上
!論理否定(NOT)(単項)
&ビットごとのAND
|ビットごとのOR
^ビットごとのXOR
~ビットごとのNOT(単項)

単項とは、1つの変数に対して効果のある演算子のことを指します(例:a++)。単項演算子以外は、常に2つの項を必要とします(例:a+b)。

論理演算とは、普段はif文(後述)などの条件式で使用します。また、「ビットごとの〜」演算子は普段は使いませんが、一応書いておきました。これについて本稿での説明は割愛します。

「〜の代入」演算子は、次のように使います。

a += 5; /* aに5を加算した値をaに代入 */ a = a + 5; /* 上と同じ動作をする式 */
キーボード入力を受け付ける
#include <stdio.h> int main() { int old; float weight; printf("あなたは何歳ですか? >"); scanf("%d", &old); /* oldにキーボードからの数字を代入。&が必要 */ printf("すると、10年後は%d歳ですね。\n", old+10); printf("あなたの体重は何キロ? >"); scanf("%f", &weight); /* ほかの型も同様。 &を忘れずに */ printf("ふーん、そうなんだ。\n"); return 0; }

scanf()は、キーボードからの入力を変数に格納する関数(命令)です。入力されるものが、整数なのか、文字なのか、はたまた実数(浮動小数点数という)なのかをコンピュータに教えなければならないのですが、それをprintf()と同じように%で始まる文字列で指定します。ここで注意しなければならないのは、格納する変数の頭に&をつけなければならないことです。これには意味があるのですが、今は「決まり」と覚えてください。

条件分岐をする (ifとswitch)
#include <stdio.h> int main() { int old; printf("あなたの年齢は? >"); scanf("%d", &old); if(old < 20){ /* oldが20未満だったら */ printf("あなたは未だ違法である。\n"); }else{ /* そうじゃなかったら */ printf("今度一緒に飲みましょう。\n"); } return 0; }

if文は、カッコの中の条件が成立していれば、次の中カッコを実行します。条件が成立していない場合は、else以降の中カッコを実行します。必要がなければelse以降は省略することが可能です。

if文の書式は次のようになります。

if(条件式) 条件式が成立した場合に処理する文 else 条件式が成立しなかった場合に処理する文 ※else以降は無くてもいい #include <stdio.h> int main() { char neta; printf("いらっしゃい!何にしましょうか?\n"); printf("a:とろ b:赤身 c:ホタテ d:げそ >"); scanf("%c", &neta); /* 数字ではなく文字を入力させる */ switch(neta){ case 'a': /* netaは文字型だから、ここは文字定数。 */ printf("へい!とろいっちょ!\n"); break; /* breakが無いと、次のcaseに続いて処理をしてしまう */ case 'b': printf("はいよー!赤身おまちどうさま。\n"); break; case 'c': printf("ごめんねー、ホタテきらしてるんだ。\n"); break; case 'd': printf("げそなんて、お客さんめずらしいねー。\n"); break; default: /* 変な文字を入力した場合 */ printf("冷やかしなら帰ってもらうよー!\n"); break; } return 0; }

switch文は、カッコの中の値に対応するcaseの位置にジャンプします。処理の終わりにはbreak文を書かないと次のcaseまで続けて実行してしまうので、必ずbreak文を書くようにしましょう(意図的に書かない場合もある)。どのcaseにも対応しない場合はdefaultにジャンプします。defaultも無い場合は何も処理しません。

switch文の書式は次のようになります。

switch(式){ case 定数式: 文 default: 文 } ※caseは何個あってもい。defaultは無くてもいい
繰り返し処理 (forとwhile)
#include <stdio.h> int main() { int i; for(i=0; i<20; i++){ /* 20回繰り返す。(iは0から19まで) */ printf("i = %d\n", i); /* ここには、繰り返したい文をいくつも書くことができる */ } return 0; }

まず、一番はじめにi=0が実行されます。次にi<20を評価し、条件が成立していればprintf()を実行します。それが終わるとi++を実行し、またi<20の評価に移ります。それが成立していれば、後はprintf() => i++ => i<20と、繰り返すことになります。もちろん、 i<20が成立しなくなったら、for文は終了します。

あまりいい例ではありませんが、forをwhileで書き換えると次のようになります。

#include <stdio.h> int main() { int i; i=0; while(i < 20){ /* i < 20が成立している間繰り返し */ printf("i = %d\n", i); i++; } return 0; }

最後に、do〜while文というのを見てみましょう。

#include <stdio.h> int main() { int i=0; do{ /* 条件式はループの最後に書く */ printf("i = %d\n", i); i++; }while(i < 20); return 0; }

for文の書式。

for(式1 ; 条件式 ; 式2) 文 ※ 式1 → 条件式 → 文 → 式2 → 条件式 → ・・・ の順番

while文の書式。

while(条件式) 文 ※ 条件式 → 文 → 条件式 → ・・・ の順番

do〜while文の書式。

do 文 while(条件式); ※ 文 → 条件式 → 文 → 条件式 → ・・・ の順番

上の3つのプログラムは全く同じ動作をしています。厳密に言えば最後のdo〜whileだけ少し違うのですが、それについては後述します。

しかし、同じことをする文が2つも3つも存在することに意味があるのでしょうか?実は、「動作は同じでも、それぞれの持っている意味(解釈)が違う」から意味があるのです。

for文は、「何回繰り返す」という意味合いで使いますが、while/do〜while文は、「条件が成立している間繰り返す」という意味合いを強く持ちます。これらの意味合いを使い分けられると、他人のソースコードを読むときに大変読みやすくなります。とりあえずは、forとwhileを使えるようにしましょう。

ところで、do〜whileですが、whileの双子の兄弟と思ってください。何がwhileと違うかというと、do〜whileは、評価する条件式が文の後ろにあるから、条件がどうであろうと最低1回は命令を実行することになる、ということです。

break文

break文を使うと、ループの途中でもそのループを終了することができます。使い方としてはswitch文中のbreak文と同じです(switchはループではないことが違う)。

goto文

BASICユーザにとっては当然のように使われているgoto文ですが、C言語の世界では嫌われ者として知れ渡っています。goto文はC言語にあるのですが、正直私は使ったことが(本当に)1度もありません。また、使ったプログラムもほとんど見たことがありません。どうしてなのか私はよくわからないのですが、とにかく「gotoは使うな」という風に覚えましょう。

ワンポイント〜文

if文では条件が成立した場合に処理する命令を中カッコで囲んでいましたし、for文でも繰り返す命令を中カッコで囲みました。しかし、この中カッコは省略できる場合があります。

C言語には「複文」という考え方があります。文とは式や宣言の最後にセミコロン(;)を付けたもののことを言います。それに対する複文とは、中カッコで囲まれた複数の(別に1つでもいい)文から成り立つブロックのことを指します。複文の閉じ中カッコ(})の後にセミコロン(;)は必要ありません。ifやforの後には文を書く決まりなので、通常の文でも複文でもいいのです(for文やwhile文の書き方の欄で、中カッコを使っていないのはそのため)。その例を下に示します。

/* 下のif文はどちらも同じ */ if(i != 0) printf("This is a pen.\n"); if(i != 0){ printf("This is a pen.\n"); } /* 次の例の場合、中カッコは取り除けない */ for(i=0; i<10; i++){ printf("%d >", i); /* 複数の文を扱うときは */ scanf("%d", &p[i]); /* 中カッコで囲む */ } for(i=0; i<10; i++) ; /* ちなみにこれは空文(何もしない) */
虫取り教室

ここまで、簡単なプログラムを組める程度の基礎知識を付けてきました。さて、これからいろいろなプログラムを作っていくわけですが、当然ながらプログラムは正確に書かねばなりません。どこか少しでも間違っているところがあると、コンパイル時にエラーがでます。それの原因が分からないときはコンピュータのせいにしがちですが、99.9%以上は人間の間違いでしょう(コンパイラの間違いも存在します)。そんなプログラムの間違いのことをバグ(bug:虫)といい、バグを取り除く作業のことをデバッグと言います。デバッグ作業には様々なやり方がありますが、ここで代表的なデバッグの方法を紹介しましょう。

コメントアウト

「なぜかコンパイル時にエラーがでる」という場合は怪しそうな部分を/* */で囲み(コメントアウトするという)、再度コンパイルしてみましょう。それでコンパイルが正常に終了したら、コメントアウトした部分が間違っていることになります。うまいやり方としては、はじめは大きな部分をまとめてコメントアウトし、その後だんだん小さくして追いつめていきます。

これは初歩的なデバッグの方法ですが、実はC言語では実現しにくいこともあります。例えば、カッコの対応が間違っているとき(大変多いバグである)には通用しないかもしれません。

printf()デバッグ

「プログラムは動いているけれども、結果がおかしい」という場合は、計算途中の変数の値を随時printf()で出力するようにしてみましょう。どこまでが正常でどこからが異常なのかを観察することができます。

プログラミングレッスン2

さて、話を元に戻してさっそく次の段階に進みましょう。いままでは本当の基礎でしたが、この章は大学の課題をこなす程度に重要な内容を含んでいます。

配列変数
#include <stdio.h> #define MAX 5 /* これ以後の MAX を 5 に読み替える命令 */ int main() { int i; int point[MAX]; /* MAX 個の要素を持つ配列 point を宣言 */ int total=0, avr; for(i=0; i<MAX; i++){ /* MAX 回の繰り返し */ printf("%d人目の点数は? >", i+1); scanf("%d", &point[i]); /* 配列の場合でも & を忘れずに付ける */ } for(i=0; i<MAX; i++){ printf("%d人目 = %d\n", i+1, point[i]); total += point[i]; } avr = total / MAX; printf("平均点 = %d\n", avr); return 0; }

これは、何をしているプログラムか意味を読みとれるでしょうか?文を先に読み進める前にまず考えてみてください。

C言語での配列は、ほかの言語でよく使われる小カッコ(())による表現ではなく、大カッコ([])によって表現されます。その点をのぞけば、宣言もプログラム中での参照の方法もイメージ通りに行えることが読みとれると思います。

ここで注意しなければならないのは、配列の添え字(何番目かを示す数字。この場合はiを使っているところ)は0からMAX-1まで使用できる、ということです。point配列変数の、MAX番目(point[MAX]のこと)は存在しません。宣言のところに用いる数は、その配列が「何個の要素を持つか」であって「何番目までの添え字が使えるか」ではありません。このことは、for文のiの値を見てもわかります。iは、0から始まりMAXに達する1つ手前で終わります(for文中の条件式がi<=MAXではないことに注目)。

また、配列の要素数の指定には#defineを使うのが定石です。

このプログラムでは配列の内容をキーボードから入力させていますが、あらかじめ初期化しておくことも可能です。その例を次に示します。

#include <stdio.h> #define MONTHS 12 int main() { int day[MONTHS+1] = /* 配列の初期化は中カッコでくくる */ { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int input; printf("今日は何月ですか? >"); scanf("%d", &input); printf("すると今月は %d日までだから、", day[input]); if(day[input] == 31) /* 単文だから中カッコを省く */ printf("月末にはダイクマへ行きましょう\n"); else printf("サーティーワンの日はないのですね\n"); return 0; }

多次元配列

#include <stdio.h> #define MAX1 10 #define MAX2 10 int main() { int i, j; int kuku[MAX1][MAX2]; /* 2次元配列 */ for(i=0; i<MAX1; i++) /* 実は次のfor文も単文です */ for(j=0; j<MAX2; j++) kuku[i][j] = (i + 1) * (j + 1); return 0; }

多次元配列はkuku[i][j]のように扱い、kuku[i,j]ではありません。これは2次元の例ですが、n次元になっても同様です。

関数を作る
#include <stdio.h> /* ただの足し算をする関数plus() */ /* 数学で言うところの関数のように使える */ int plus(int a, int b) { return a + b; /* aとbをもらって、その和を返す */ } /* printf()を呼ぶだけの関数func() */ /* 返値も引数もなし */ void func() { printf("関数func()の中です。\n"); /* 返値が無いから、return文は不要。(あっても問題ない) */ } /* これはいつものメイン関数。これも関数なのです */ int main() { int a, b, c; a = 5; b = 2; c = plus(5, 2); printf("5 + 2 = %d\n", c); /* 上の例は変数cが無駄。下の例は簡潔でよい */ printf("5 + 2 = %d\n", plus(a, b)); func(); return 0; }

関数とは、大きな計算処理を小さな仕事に分割するための構造です(似たようなものにサブルーチンという用語があります)。C言語での関数は、数学の世界で定義されるように「いくつかの値をもらって1つの値を返す」ものです。数学ではそれが数式で表現されますが、C言語では文(命令)の集まりで表現されます。

関数は、使う前に定義をしなければなりません(本当はそうしなくてもいいのだが、今はこう覚える)。つまり、main()の前に書きます。関数の定義の書式は次のようになります。

返値の型  関数の名前(引数の宣言) { 宣言と文 }

関数の名前には、変数の名前と同じ規則が適用されます(「変数を使う」の節を参照)。

返値は、どの型でも自由に使えます。返値が必要ない場合は、void型を指定します。省力するとvoid型が適用されます。返値の(値の)指定にはreturn文を使います。

return文

return文が実行されると、その時点で呼び出しもとの関数に実行が戻ります(main()関数ならプログラムが終了します)。return文は関数内にいくつあってもよいのですが、返値がvoidでない関数には必ず1つ以上のreturn文が必要です。次にreturn文の書式を示します。

return式; ※返値がvoidの場合、式は必要ない

余談ですが、printf()やscanf()も関数です。C言語には、標準の関数が用意されていて、多くの処理はそれらの関数を用いることによって実現されます。

変数のスコープ
#include <stdio.h> int g; /* 関数の外に書くと、どの関数からも使える */ void f(int a) { int b; /* この関数の中でのみ有効 */ a += 2; /* この変更は、main() の中のaには関係ない */ g += 2; /* この変更は、gの値を変更する */ printf("in f() : a = %d\n", a); printf("in f() : g = %d\n", g); } int main() { int a=10; /* main() でのみ有効 */ g = 100; printf("in main() : a = %d\n", a); printf("in main() : g = %d\n", g); f(a); /* 変数a(aそのものでなく、aのコピー)をf() に渡す */ printf("in main() : a = %d\n", a); printf("in main() : g = %d\n", g); return 0; }

変数には、その変数の有効な範囲(スコープ)が決められます。例えば、ある関数内で宣言した変数はほかの関数から参照することはできません。そのため、同じ名前の変数を別々の関数内で書くことができます(例えば、iという変数はループカウンタ用によく使います)。

また、関数の外に変数の宣言をおくと、どこの関数からも参照できる変数(グローバル変数)になります。グローバル変数は一見大変便利に見えます。しかし、グローバル変数が本当に必要な場合はそれほどありません。はじめのうちは変数をグローバルにしてしまいがちですが、グローバル変数は極力使わないようにしましょう。変数の値をほかの関数で使いたいときは引数で渡すようにします。

ワンポイント〜引数の渡し方

関数に渡すのは変数の値のコピーで、変数そのものではありません。よって、引数で渡した変数が、その関数内で変更されることはありません(逆に、変数そのものを渡す方法もあります)。

上のプログラムの実行結果は次のようになります。

in main() : a = 10 in main() : g = 100 in f() : a = 12 in f() : g = 102 in main() : a = 10 in main() : g = 102
数値処理の達人

さて、ここまで勉強してきましたが、これほどでは大学の課題がこなせないでしょう。何が足りないか?足りないものはいろいろあると思いますが、その中の1つに「複雑な計算ができない」というのがあると思います。ここでは数値処理に必要な知識について解説します。

数学関数
#include <stdio.h> #include <math.h> /* これが必要!! */ int main() { double rad; for(rad=0; rad < 3.1; rad += 0.1) printf("sin(%f) = %f\n", rad, sin(rad)); return 0; }

これは0.1ラジアン刻みのsinを求めるプログラムです。特に難しいところはありませんが、2行目の#include <math.h>を忘れずに書きましょう。次に、よく使われるであろう関数をいくつかあげます。どれも、1つあるいは2つ以上のdouble引数をとり、doubleを返します。

関数名内容
sin(x)xのsin。単位はラジアン。
cos(x)xのcos。単位はラジアン。
atan2(y,x)y/xのarctan。単位はラジアン。
exp(x)指数関数ex
log(x)xの自然対数(基数はe)。(x>0)
log10(x)xの普通対数(基数は10)。(x>0)
pow(x,y)xy
sqrt(x)xの平方根。(x>=0)
fabs(x)xの絶対値。

桁をそろえて表示

前のソースでは、出力結果(一部)は以下のようになります。

sin(0.000000) = 0.000000 sin(0.100000) = 0.099833 sin(0.200000) = 0.198669

無駄な(必要のない精度の)0の表示が目立ちますが、これをきれいに表示するにはprintf()の文字列を次のようにします。

printf("sin(%3.1f) = %010.8f\n", rad, sin(rad));

これで、次のようになります。

sin(0.0) = 0.00000000 sin(0.1) = 0.09983341 sin(0.2) = 0.19866933

変わったのは、%fを%3.1fや010.8fにした点です。小数点を境にして、左側の数字は、全部で何桁表示するかを指定します(この中には、小数点を表示する桁数も含まれる)。そして右側の数字は、全部の桁数のうち小数点以下は何桁表示するかを指定します。また、(余分な)頭の0は、(小数点以上の)出力する必要のない桁にもとりあえず0を出力させることを意味します(上の例はナンセンスであるが)。出力結果と見比べてください。また、intやlongなどの整数(小数点以下がない)の場合は、小数点以下の指定は不要です。

今度は、%fを%eにしてみましょう(「よく使うデータ型一覧」を参照)。コードと出力結果は次のようになります。

printf("sin(%7.1e) = %e\n", rad, sin(rad)); sin(0.0e+00) = 0.000000e+00 sin(1.0e-01) = 9.983342e-02 sin(2.0e-01) = 1.986693e-01

e-01とは、×10-1を意味します。また、全体の桁数の指定は、小数点と指数部を含んだ桁数になります(この場合は7)。

乱数
#include <stdio.h> #include <stdlib.h> /* これが必要!! */ #include <time.h> /* これも必要!! */ int main() { int i; srand((unsigned)time(NULL)); /* 乱数の初期化はこう書く */ printf("rand() は、%dから%dの値を返す関数\n", 0, RAND_MAX); for(i=0; i < 10; i++) printf("%d\n", rand() % 100); /* 0から99までの乱数を表示 */ return 0; }

乱数を使うには、rand()関数を使います。rand()は、0〜RAND_MAXの間の値を返値とします。RAND_MAXとは、#defineであらかじめ定義されている定数のことです。具体的には215-1に変換されます。よって、必要な範囲の乱数にするには加工が必要です(剰余を使うのはその常套手段)。

また、乱数はrand()を呼び出す前に初期化しなければなりません。乱数の初期化にはsrand()関数を使います。srand()は、上記のソースのように使います(このように書けばまず間違いない)。

#include <stdlib.h>と#include <time.h>を書くのを忘れないように。

8進数と16進数

C言語では、普段我々が使っている10進数以外にも、8進数と16進数を扱うことができます(残念ながら2進数は扱うことはできません)。

8進数は、数の頭に(一見、意味のなさそうな)0を付けることで表現します。例えば、010は10進数の8になります(余談ですが、0という数は10進数で表現できないことになります)。

16進数は、数の頭に0x(または0X)を付けることで表現します。例えば、0x1fは10進数の31になります。

printf()やscanf()での8進数の扱いは%o。16進数は%xになります。

最後に

ここまででC言語入門はおしまいです。これだけの量で終わらせるC言語入門はあまりないと思いますが、冒頭でも書いたように「大学の課題をこなせる」程度にはまあまあの量だと思います(専攻にもよりますが)。

しかし正直なところ、これではせっかくC言語の学習を始めたのに中途半端すぎます。もっともっと説明したいところは山のようにありますが(特にポインタ関連は全滅)、今後の学習は独学もしくは直接私に質問をする形で行ってください。文章での解説は専門の入門書に譲ることにします。しかし、本稿の目的が「課題をこなせる程度」とかなり特化した範囲になったこともあって、要望があれば応用編としてさらに各分野に特化したC言語入門を行うこともあるかもしれません。

参考文献