C言語入門
シーげんごにゅうもん

PCinfo PCinfoトップページ > C言語の話 > C言語入門 Guest Book

ようこそCの世界へ

ちょっとだけC言語の歴史に触れてみましょう。 なぜ”ちょっとだけ”かというと、私にちょっとだけしか知識がないからです。 C言語はなぜゆえに”C”と呼ばれるのかというと、 B言語の次に誕生したからという単純な理由からです。

C言語はUNIXというOSを開発するために作られたのですが、UNIX自身はC言語が出来るより以前にありました。 C言語誕生以前のUNIXはアセンブラで書かれていた訳ですが、1972年にC言語が出来て以来現在までC言語(C++含む)はUNIXの主力言語となっています。

よく”中級言語”とか” 高級アセンブラ”と呼ばれたりしますが、これはC言語の特徴をうまく表現していると思います。 C言語は高級言語でありながらアセンブラのような低レベルな処理が出来るのです。 例えばインラインアセンブラといってCのコード中にアセンブラコードを埋め込むこともできます。これについては アセンブラの話の方で取り上げたいと考えています。

では早速簡単なプログラムを組んで見ましょう!

#include <stdio.h>

int main(int, char**)
{
	printf("hello world\n");
	return 0;
}

ほい、完成!それではコンパイルして実行してみて下さい。
画面上にhello worldと出力されたと思います。
結果から推測するに、printfとか書いてある行のダブルクォーテーションの中に書かれた文字(文字列)を画面上に出力するプログラムであることが分かると思います。

#include <stdio.h>

int main(int, char**)
{

	return 0;
}

この部分は”プログラムを書くときのお約束”とでも思っていてください。
詳しい話は気が向いたらします。今のところは大カッコ{ }の中にコンピュータにやらせたい命令を書けばいいんだなぁ程度に思っていれば十分です。

今回注目したいのはprintf()の部分です。
printf()は関数の1つで、数学でいう関数f( )と似たようなものだと思ってください。

関数はよく自動販売機に例えられます。
自動販売機(printf関数)に120円(hello worldという文字列)を入れると、ジュースが出てくる(hello worldと画面に出力される)という訳です。

printf関数の使い方は、今回のプログラムを見れば分かるように、出力したい文字をダブルクォーテーション""の中に書けばよい訳です。 今回はhello worldという英字でしたが、別に日本語であっても問題はありません。 ただし、ダブルクォーテーション""の中に書いたものが全て画面上に出力されるとは限りません。

今回の例がそうで、hello worldの後に\nという記号がありますがこれは画面上には出力されません。 \nはエスケープシークェンスと呼ば れるものの一つで、ちょっと”特別なことが出来る文字”とでも思ってください。 \nを使うと”改行”を行うことが出来ます。

エスケープシークェンスについても気が向いたら詳しく話したいと思います。

目次へ







3通りのループ処理

まずは「ループ(繰り返し)処理とは何ぞや?」という話から始めましょう。

前回、hello worldという文字列を画面上に表示するプログラムを作りましたが、もしこの 文字列を1回ではなく10回画面上に表示したい場合はどうすれば良いのでしょうか?

単純に考えると次のようなプログラムが考えられます。

#include <stdio.h>

int main(int, char**)
{
	printf("hello world\n");
	printf("hello world\n");
	printf("hello world\n");
	printf("hello world\n");
	printf("hello world\n");
	printf("hello world\n");
	printf("hello world\n");
	printf("hello world\n");
	printf("hello world\n");
	printf("hello world\n");

	return 0;
}

まぁ確かにこれで画面上に10回 hello world と表示されることは間違いありません。

ですが、素人目に見てもエレガントとは程遠い記述方法をしているとは思いませんか?仮に100回 hello world と 表示するとしたら printf関数 をひたすら100回書き続けていくことになるのでしょうか?もっと効率の良い方法が あって良いはずだと誰もが思うはずです。

そこでループ処理が登場する訳です。ループを実現する方法は for文 while文 do〜while文 を使う3通りの 方法がありますが、まずは for文 を使って上のプログラムを書き換えてみましょう。

#include <stdio.h>

int main(int, char**)
{
	int i;

	for(i=0;i<10;i++){
		printf("hello world\n");
	}

	return 0;
}

どうでしょう。随分とシンプルになりましたね。

少し話がずれますが、物書きの世界では「複雑で小難しい文章ほど素晴らしい」という誤解をもった人達が 少なからずいます。それはプログラムの世界でも同様で「複雑なプログラムは素晴らしい」と考えている 人達は少なくありません。

ですが、どの世界だろうが「シンプル イズ ベスト」である事には変わりないのです。
プログラムを組むときは常によりシンプルなプログラムを追及するように心掛けましょう。



では for文 の説明に移りましょう。何をやってるか言葉で説明すると、


 1.i を0から始めて

 2.i が10未満ならば次へ10以上ならばループ脱出

 3.for文の中カッコ{ }内を実行し

 4.{ }内を実行の度に i に 1 を足す

 5.2に戻る

 (i=0の部分)
 
 (i<10の部分)
 
 (この場合printf関数の部分)
 
 (i++の部分)
 
 (再び条件判定へ)
 



ってな感じです。注意すべき点としては i がインクリメント(i++)されるのは中カッコ{ }内が実行された後だと いうことです。あと条件判定が2番目に実行される点にも留意してください。

もし仮に次の様にした場合、

	for(i=0;i<0;i++){
		printf("hello world\n");
	}

ループは1度も実行されることはありません。
また、条件判定の部分を i<1 とすると1度だけ実行されることになります。

では以上 for文 で記述した処理を while文 do〜while文 で記述するとどうなるのか見てみましょう。

【while文】

#include <stdio.h>

int main(int, char**)
{
	int i=0;

	while(i<10){
	     printf("hello world\n");
	     i++;
	}

	return 0;
}



【do〜while文】

#include <stdio.h>

int main(int, char**)
{
	int i=0;

	do{
	     printf("hello world\n");
	     i++;
	}while(i<10);

	return 0;
}

どうでしょうか?for文の説明と比較しながら見ると理解できるのではないでしょうか。

ポイントとしては、初期化・条件判定・インクリメント の位置ですね。例えば do〜while文 ですと 条件判定が一番最後になっているからループ内が1度は必ず実行されることになります。for文 や while文 ですと条件しだいでは一度もループが実行されない可能性があります。

あと、他に特徴を言っておきますと、for文 はその性質上 あらかじめ実行回数が分っている場合 に使用します。わからない場合には while文 を使いましょう。


要はどのループ文を使うかは 「ケース バイ ケース」 ってことです。(^_^)

目次へ







構造体の話

途中の段階をふっ飛ばしていきなり構造体の話です。

構造体というのはそれぞれ関連する変数をパッケージとしてまとめたものと思ってください。 構造体として複数の変数をまとめることによって、それらを統一的に扱うことが出来るためいろいろ と便利なことが多いので、基本的に関連性のある変数は構造体としてまとめちゃうようにしましょう。

まずは構造体の作り方ですが、

struct タグ名{
	型 メンバ1;
	型 メンバ2;
}変数リスト;

メンバ1、メンバ2と書いてあるのがお互いに関連性のある変数です。ここではメンバは二つしかありませんが、 別にいくつあっても構いません。 変数に型があるように構造体にも型が必要です。(上記の書き方ならば省略可能ですが、タグ名(構造体名) は付けていた方が何かと便利です。)

構造体の型名(タグ名)は”自分で決めて”structの後ろに書きこみます。 変数にも言えることですが、無意味な名前を付けると後々自分が後悔することになるので誰が見ても理解 できるようなタグ名を付けるようにしましょう。

大雑把に構造体について話したところで実際にどのように使うのか見てみましょう。

#include <stdio.h>

struct student_data{
	int age;
	char name[100];
	char number[30];
	char address[300];
	char tel[40];
};

int main(int, char**)
{
	struct student_data card;

	printf("年齢を入力してください");
	scanf("%d",&card.age);
	printf("名前を入力してください");
	scanf("%s",&card.name);
	printf("学生番号を入力してください");
	scanf("%s",&card.number);
	printf("住所を入力してください");
	scanf("%s",&card.address);
	printf("電話番号を入力してください");
	scanf("%s",&card.tel);

	printf("あなたの年齢は%d才で、\n名前は%sで、\n学生番号は%sで、\n住所は%sで、\
		\n電話番号は%sの学生です\n",\
		card.age, card.name, card.number, card.address, card.tel);

	return 0;
}

まず構造体のメンバですが、学生データという点で関連性があることを理解できると思います。

今、学生カードというものを仮定している訳ですが、もしあなたが学校側(職場と仮定してもOK)から 「名前はこの紙に書いてね」、「住所はこっちの紙ね」、「それから電話番号はコレ!全て別々の用紙に記入してくださいね!」、 などと言われたらかなり面倒ですよね。学校側にしても整理ができず困るに違いありません。 それならば一枚の用紙に”名前を書く欄”、”住所の欄”、”電話番号の欄”とまとめてあった方が便利だと思いませんか?

構造体とはまさにこの”一枚の用紙”にあたるものなのです。

面倒になったのでとりあえず今回の話はここまで。続きは気が向いたときにでも・・・

目次へ







ファイル・アクセスの基礎

基本的なファイル・アクセスの方法を学びましょう
次に示すプログラムはテキストファイルから文字を読みこみ、画面上に表示するものです。

#include <stdio.h>
#include <stdlib.h>

int main(int, char**)
{
	FILE *fp;
	char str[256];

	/*ファイルオープン*/
	fp = fopen("myfile.txt", "r");
	if (fp == NULL){
		printf("ファイルオープンに失敗しました");
		exit(1);
	}

	/*ファイルの表示*/
	while (fgets(str, 256, fp) != NULL)
		printf("%s", str);

	fclose(fp);
	return 0;
}

まずはこのプログラムに出てくる3つの関数について説明しましょう。<近いうちに詳しく書きます>

FILE *fopen(char *filename, char *mode);
char *fgets(char *string, int n, FILE *stream);
int fclose(FILE *fp);

ファイルを読み書きするためにはfopen関数によってファイルをオープンする必要があります。
filenameに対象となるファイルの名前を入力し、modeにはファイルのアクセス方法を指定します。 ファイルから読みこむ場合は"r"、ファイルに書きこむ場合は"w"、追加して書きこむ場合は"a"、としてください。 この他にも色々ありますが、基本はこの3つです。気が向いたら他のモードも表にしてまとめておきます。

fgets関数によってファイルから指定の配列へ文字列を書きこみます。

fclose関数によってファイルをクローズします。

参考までにVC++ではFILE構造体がどのように定義されているか見てみましょう。

struct _iobuf {
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname;
        };
typedef struct _iobuf FILE;

処理系によりFILE構造体の内容は異なります。
基本的にプログラマがFILE構造体の詳細を意識する必要はありません。

目次へ








乱数の基礎

今回は乱数についての話です。特にゲームプログラミングなどをしようものなら、この”乱数”とは 「もうイヤ!」というほどお付き合いすることになります。もっとも乱数はゲームに限らず 幅広く利用できますので、ちゃっちゃと覚えてサクサク使えるようになりましょう。

まずはただ単に乱数を作るだけのプログラムを作ってみましょう。

#include <stdio.h>
#include <stdlib.h>

int main(int, char**)
{
	int i;

	for(i=0; i<10; i++)
		printf("%d\n", rand());

	return 0;
}

では実行してみてください。ランダムな数字が10個表示されるはずです。このランダムな数字を 作り出している部分が rand( ) なのです。なおrand関数の使用にはstdlib.hをインクルード する必要があります。

では次の話に移りますが、その前にこのプログラムを何回か繰り返し実行してみてください。

はい、もうお分かりのように毎回どういう訳か同じ数値ばかりが表示されてしまいますね。 これは一体どういうことでしょうか?実行する度に同じ数値が表示されたのではランダムとはいいかねます。

実は、この現象は乱数の種(seed)が実行の度に同じであることに起因しているのです。 rand関数は”乱数の種の値”を基準としてランダムな数値を作り出す仕組みになっているのです。そのため毎回 違う値を表示するためにはこの乱数の種をいじってやる必要があるのです。

まずは乱数の種を作り出す関数を紹介しましょう。

#include <stdio.h>
#include <stdlib.h>

int main(int, char**)
{
	int i;

	srand(1);/*乱数の種を作り出す関数*/

	for(i=0; i<10; i++)
		printf("%d\n", rand());

	return 0;
}

これを実行しても無駄なあがきであることは理解していただけるでしょうか。上記のプログラムでは 実行の度に”1”という数値でrand関数が初期化されることになり、やはり毎回同じ結果になってしまいます。 逆に考えると実行のたびにsrand(ここの部分)が変わるようにしてやればOKということになります。

では実行のたびに変わるものとは一体何があるのでしょう。やはり単純に考えて、 答えは「時間」ではないでしょうか。 ということで、システム時間を基に乱数を作り出せるように上の プログラムを改造してやりましょう。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>/*time()を使うために必要です*/

int main(int, char**)
{
	int i;

	srand((unsigned int)time(NULL));/*システム時間を基に乱数の種を作る*/

	for(i=0; i<10; i++)
		printf("%d\n", rand());

	return 0;
}

とりあえず実行の度に表示結果がびみょ〜に変わるようにはなったと思います。time関数の説明自体はまた別の機会にやりますので、 今のとこは(unsigned int)time(NULL)をコピペでOK!と納得してください。

目次へ