はじめに

このページでは、C言語のメモをある程度分類分けし、整理したものです。準拠しているC言語の規格は明言できませんが、Microsoft Visual Studio 2010(時々、gcc)でビルド&実行の確認をしています。また、自身のためのメモなため何か起きても保証しません。

まえおき

このページではC言語のプログラムソースを掲示しますが、指示がなければ以下のソースコードのようにmain関数の中にある(A)の部分に記述しています。記述する際は /* (A) */の部分を削除してから、プログラムを記述してください。

#include <stdio.h> 
int main(){
	/* (A) */
	return 0;
}						

コメント

//は一行コメントであり、一行コメント以降はコメント扱いとなりプログラムに影響しません。/* ~ */は/*と*/の間がコメント扱いとなり、プログラムに影響を与えません。

準備

C言語の簡単なソース(プログラムを記述したもの)を示します。

#include <stdio.h> // コンパイルはここから開始

int toBig(char c);

int main(){ //実行時はここから開始
    char ch;
    printf("小文字のアルファベットを入力してください");
    ch = (char)getchar();
    printf("%c => %c\n",ch,toBig(ch));
    return 0;
}

int toBig(char c){
    return c + 'A' - 'a';
}

人がC言語の記法で書いたプログラムは、コンピュータがそのまま利用することはできません。そこでコンパイルという作業をします。コンパイルは人間がプログラミング言語の記法にしたがって書いてたものを、コンピュータ用の言葉に翻訳する作業です。コンパイルをするためのソフトウェアをコンパイラといいます。このコンパイラがC言語の文法チェックなどを行い、間違っていれば警告やエラーを返して知らせてくれます。ただし、プログラムの内容が合っているかは教えてくれません

C言語で作ったプログラムはmain関数から上から順に実行されます。しかし、コンパイラはソースファイルの一行目から順に処理していきます。処理とコンパイルの順は違うということを頭の片隅に置いてください。

変数の種類

C言語では何かの情報を記憶する際に、記憶する領域に名前をつけて管理する変数という仕組みがあります。このような仕組みがないと、コンピュータのメモリの番地に直接記録しなければなりません。

変数には種類があるのですが、コンピュータの情報は全て0と1の二つの値で表現できます。何故、変数に種類があるのかというと、同じ0と1の情報でも、解釈に違いがあるためです。このため、変数の種類を誤ると記憶した値が、全く違うものへと解釈されてしまうので注意が必要です。また、変数の種類のことをといいます。

以下に代表的な型を示します。

意味記録範囲
int整数(32bit)を記憶できる-2147486948 ~ 2147483647
float実数(32bit)を記憶できる-3.4E+38 ~ 3.4E+38
doublefloatの倍(64bit)の精度で記憶できる-1.7E+308 ~ 1.7E+308
char文字を記憶できる(8bit)-128 ~ 127 例えば'A'は数値65で表せられる
void値を記憶しません(関数の戻り値が無いときなどに利用)なし

変数を使う

変数宣言

変数を利用するときは、関数最初に変数宣言を行わなければいけません。

int a;	//整数型の宣言
double b;	// 実数型で宣言
char c;	//文字型で宣言

上記の宣言のように 型名空白変数名として利用します。変数名は自由な名前を付けることができます。ただし、C言語が予め予約している名前は使えませんし、変数名にアンダーバー( _ )以外の記号の類は使えません。また、先頭に数字を入れることはできません。また、空白はコンピュータに読み込ませた時点で一つの区切りとして認識されますので、1つでも複数いれてもかまいません(限度はあるのでしょうけど)

Important変数は同じ変数名で複数宣言することはできません。また関数名と同じ変数も宣言することができません。詳しくは構文範囲を参照してください。宣言は始めの一回のみで、利用するときは宣言は必要ありません。

記憶する

用意した変数を実際に値を記憶させる場合、代入( = )を利用します。

int a;	//整数型の宣言
double b;	// 実数型で宣言
char c;	//文字型で宣言

a = 256;
b = 256.1111;
c = 65;	// 'A'と同値		

変数宣言時にも値を入れておくことができます。

int a = 256;

文字を記憶する (代入)

文字はコンピュータの内部では整数で取り扱われる。文字と数値の対応表はこちらを参照ください。

char c = 'A';
char d = 65;

上記のソースコードの変数c,dにも65が記憶されています。文字Aは数値に直すと65なため同値となるわけです。文字を記憶するときは'A'のようにシングルクォート(')で文字をはさみます。こうすると、コンピュータが文字を数値へと変換します。

呼び出す (参照)

せっかく記憶した値を、そのままにしていては意味がありません。変数に入った値を引き出して、色々利用しましょう。

int a;	//整数型の宣言
int z; // 整数型で宣言

a = 256;
z = a	// 呼び出し、代入	

上記のサンプルのように数値のかわりに変数名を記述して、zへaの値をコピーしました。ここで代入はコピーであり、この時点でzとaの値は同じ256が記憶されていることに注意しなければなりません。また、今回は同じ型で代入を行いましたが、異なった型でやる場合は、自分で型をそろえてやるか、キャストという仕組みを利用する方法があります。

キャスト

異なった型で変数に代入する場合、キャストという仕組みを利用します。ただし、キャストを行う場合、型の記録範囲外の値の場合、正しくコピーされないので、注意が必要です。

キャストを行うときは、(型)変数もしくは数値などといった具合に利用します。

int a; //整数の変数を宣言
double b; //実数型で変数を宣言

b = 256.1111;
a = (int)b; // 実数型を整数型に変換してaへ代入  ( A )
a = (int)3.141592; //実数を整数に変換してaへ代入 ( B )

(A)は変数をキャストする例です。この時256.1111が整数型に変換され、少数が切り落とされ、256となります。また、(B)は直接数値を整数へ変換した例です。この時、aには少数が切り落とされ3が記憶されています。

配列

変数をいくつも作るのは非常に手間がかかるので、必要な個数を指定して、配列として扱います。また、配列は繰り返しと非常に相性がよく、大量の情報を取り扱うときは、配列と繰り返しをセットで使うことが多いです。

配列を利用するときは、変数宣言時に配列として宣言します。宣言方法は空白配列名[配列長]で宣言します。配列長は数値を指定し、変数で配列長を指定することはできません。また宣言時に予め値を入れることができます。

int a[10]; // 整数型配列を10個宣言 (A)
int b[] = {1,2,3,4,5}; // 整数型を宣言して初期化 (B)
int c[10] = {}; // 整数型で10個宣言し、すべて0で初期化 (C)

上記のように宣言します。(A)では整数型を10個宣言(用意)し、中身は不定(決まっていない)状態です。(B)は配列長は指定していませんが、{値,値,値,...値}とすえれば、値の個数分の長さで宣言したうえに、指定した値で配列の先頭から順に初期化されます。(C)のように{}とすると、配列のすべてに0が代入されます。

利用する際には、配列名[添字]で通常の変数と同じように利用することができます。この時添字は、数値を指定しもいいですし、整数型の変数で指定してもかまいません。添字の範囲は0〜配列長-1となります。それ以外の添字で配列にアクセスすると、大変な問題がおこります。言及はしません。

int a[10]; //整数型 配列名a 長さ10
flaot b[20]; // 実数型 配列名b 長さ20
int c[] = {1,2,3,4,5}; // 整数型 配列名c 長さ5 (初期化)

int i = 2;
printf("%d %d¥n",c[4],c[i]);
c[3] = 10;
printf("%d",c[3]);
 結果 */
> 5 3
> 10

ポインタ

ポインタはメモリのアドレスを記憶するための変数です。そもそもコンピュータが計算に使うすべての情報はメモリ上にあります。このメモリは数値を記憶しておくための領域で、その領域にはアドレス(住所)が割り当てられています。イメージとしては以下の様になります。ちなみにアドレスは適当な数字を割り当てました。一つのブロックで1Byteとしています。

この章で扱ってきた変数は、利用する前に変数宣言を行いますが、これはメモリのこの領域を使いますよと宣言しているわけです。

int a = 10;
char c = 5;

int型は4Byte(=32bit)、char型は1Byte(=8bit)として扱いました。ですので、イメージでは変数aは4ブロック、変数cは1ブロックで表しています。この時、コンピュータの状況により自動的に空いているメモリ空間を確保するので、アドレスは実行してみるまでわかりません。今回は便宜上のメモリアドレスを利用します。

変数宣言をした変数に割り当てられたアドレスは

&変数名

で表すことができます。試しに以下のようなプログラムを実行すると確認できるはずです。この時アドレスはコンピュータにより都合良いメモリが割り振られるので、アドレスは同じ数字にはなりません。

int a = 10;
printf("value:%d¥taddress:%d¥n",a,&a);
value:10	address:1580034868

本題のポインタです。アドレスを入れるためのポインタも宣言して利用します

型 *ポインタ名

以上のように宣言します。利用は通常の変数と同様です。が、アドレスを記憶するための専用変数であるということを忘れないでください。また、宣言時は格納するアドレスの変数の型と同じ型のポインタ変数を宣言してください。

int a = 10;
int *pa;
pa = &a;
printf("a = %d¥n",a); // aの値を表示
printf("*pa = %d¥n",*pa); // paに格納されているアドレスの先の値
printf("&a = %d¥n",&a); // aのアドレス
printf("pa = %d¥n",pa); // paに格納されているアドレス
printf("&pa = %d¥n",&pa); // paのアドレス
a = 10
*pa = 10
&a = 1352940324
pa = 1352940324
&pa = 1352940312

以上のように、アドレスを格納することができます。この時、*paのようにしていますが、これは乗算ではなく、格納されているアドレスの先の値という意味になります。そして、このようにアドレスを記憶するためのポインタにも当然のようにメモリが割り当てられているので、アドレスが存在しています。

ポインタが使いづらい一つの原因は*や&の変数前に配置する記号で表される内容が理解しづらいことではないでしょうか。

変数ポインタ
アドレス
*使えないアドレス先の値
&変数のアドレスポインタ変数のアドレス

このように概念で理解することが大切です。

変数を使った実例

値を入れ替える

int a = 10;
int b = 5;
int temp; // 一時保管(テンポラリ)用変数を宣言
temp = a; // ( A )
a = b; // ( B )
b = temp; // ( C )		

値の代入は上書きしてしまうため、値を交換するときは、一時的に記憶しておく同じ型の変数を用意します。

ステップ変数a変数b変数temp
(A)10510
(B)5510
(C)51010

概要

定義

構造体は以下のように定義します。

struct 構造体名{
	メンバー変数宣言;
	メンバー変数宣言;
	.
	.
	.
};

宣言

定義した構造体を、利用できるようにするために以下のように宣言をします。

struct 構造体名 変数名;

利用

利用する場合は宣言した変数名にドット"."を付け、メンバを指定します。指定したメンバは通常の変数と同様に扱うことができます。

変数名.メンバ変数 = 10;//(メンバが整数だった場合の代入)
scanf("%d",&変数名.メンバ変数名); // (メンバ変数が整数で、キーボードから入力する例 )
// ダメな例 (&変数名).メンバ変数
a = 変数名.メンバ変数名; // (他の変数に値を代入する例)

関数の引数として、アドレスが渡されるなどした場合、ドット"."ではなく、アロー"->"を使います。もちろん使わない方法もあります。

変数名->メンバ変数 = 10; //メンバ変数が整数だった場合
(*変数名).メンバ変数 = 10; // 上記と同様な意味 (アローを使わない例)
scanf("%d",&変数名->メンバ変数); // メンバ変数が整数で、キーボード入力する例
scanf("%d",&(*変数名).メンバ変数); // 上記と同様な意味 (アローを使わない例)
a = 変数名->メンバ変数; // 代入例
a = (*変数名).メンバ変数 // 上記と同様な意味(アローを使わない例)

使用例

構造体はパッケージを作る機能です。例えば、アドレス帳を作成しようとした場合、

  • 氏名
  • 郵便番号
  • 住所
  • 電話番号

などの情報を記録します。この際、構造体を利用しないと

char name[81];
int zip;
char address[81];
int phone;

strcpy(name,"Ken");
zip = 1234567;
strcpy(address,"西京都西京市1丁目1番地");
phone = 1234567890;

このように宣言、格納します。アドレス帳は一件ではあまり意味が無いので、複数件(例えば3件)にしようとすると

char name[3][81];
int zip[3];
char address[3][81];
int phone[3];

strcpy(name[0],"Ken");
zip[0] = 1234567;
strcpy(address[0],"西京都西京市1丁目1番地");
phone[0] = 1234567890;

strcpy(name[1],"Tom");
zip[1] = 1234568;
strcpy(address[1],"西京都西京市2丁目1番地");
phone[1] = 1234567891;

strcpy(name[2],"Taro");
zip[2] = 1234569;
strcpy(address[2],"西京都西京市3丁目1番地");
phone[2] = 1234567892;

これではアドレス帳ではなく、名前帳、郵便番号帳、住所帳、電話番号帳を別々に用意しているようなもので、一貫性がありませんし、添字の番号を間違えただけで間違った情報が提示されてしまします。ここで構造体を利用すると、とたんに見やすくなります。まず、アドレス帳に必要な要素をひとまとめにして、構造体として定義します。

struct Address{
	char name[81]; //氏名
	int zip; // 郵便番号
	char address[81]; // 住所
	int phone; //電話番号
};

これをテンプレートとして、利用します。以下が、一件だけアドレスを格納して、表示した例です。

#include <stdio.h>
#include <string.h>

struct Address{
  char name[81];
  int zip;
  char address[81];
  int phone;
};

int main(){
	struct Address data;
	strcpy(data.name,"Ken");
	data.zip = 1234567;
	strcpy(data.address,"西京都西京市1丁目1番地");
	data.phone = 1234567890;
	
	printf("氏名:%s\n",data.name);
	printf("郵便番号:%d\n",data.zip);
	printf("住所:%s\n",data.address);
	printf("電話番号:%d\n",data.phone);
	return 0;
}

これを複数件(今回は3件)にすると以下のようになります

#include <stdio.h>
#include <string.h>

struct Address{
  char name[81];
  int zip;
  char address[81];
  int phone;
};

int main(){
	struct Address data[3];
	
	strcpy(data[0].name,"Ken");
	data[0].zip = 1234567;
	strcpy(data[0].address,"西京都西京市1丁目1番地");
	data[0].phone = 1234567890;
	
	strcpy(data[1].name,"Tom");
	data[1].zip = 1234568;
	strcpy(data[1].address,"西京都西京市2丁目1番地");
	data[1].phone = 1234567891;
	
	strcpy(data[2].name,"Taro");
	data[2].zip = 1234569;
	strcpy(data[2].address,"西京都西京市3丁目1番地");
	data[2].phone = 1234567892;
	
	printf("氏名:%s\n",data[0].name);
	printf("郵便番号:%d\n",data[0].zip);
	printf("住所:%s\n",data[0].address);
	printf("電話番号:%d\n",data[0].phone);
	
	printf("氏名:%s\n",data[1].name);
	printf("郵便番号:%d\n",data[1].zip);
	printf("住所:%s\n",data[1].address);
	printf("電話番号:%d\n",data[1].phone);
	
	printf("氏名:%s\n",data[2].name);
	printf("郵便番号:%d\n",data[2].zip);
	printf("住所:%s\n",data[2].address);
	printf("電話番号:%d\n",data[2].phone);
	return 0;
}

このように、構造体を利用すると、アドレス帳一つにまとめられ、間違いが少なくなります。

Important構造体のメンバを利用する時は、構造体名.メンバ変数と記述しますが、これを「○○の△△」と読み直すと簡単に意味が表せられます。例えば、構造体変数がdataでメンバ変数がnameであったとすると「dataのname」とすると読みやすくなります。

表示(標準出力)

画面(標準出力)に対し、文字を表示させるに手頃な方法は以下の通りである。

  • putchar関数
  • printf関数
  • puts関数

putchar関数

putchar関数は文字を一つ表示する関数です。

putchar( 文字(コード) )

引数に文字や変数を渡すと、画面に文字が表示されます。例えば、

char c = 'a';
putchar((int)c);
putchar('A');
putchar(65);
/* 結果 */
> aAA					

のようになります。引数は厳密にはint型ですのでキャストを行なっていますが、大概コンパイラが判断してくれます。

printf関数

printf関数はフォーマット指示付きの表示関数です。文字列数値変数の組み合わせ(フォーマット)を指定して複雑な文字列を表示することができます。

printf(フォーマット指定文字列, フォーマットに指定された(複数の)変数...)

フォーマット指定文字列は文字列なのでダブルクオート(")で加工必要があります。以下に例を示します。

"Aさんの身長は%.2f,体重が%.2f,出身地は%sです。"

%で始まる部分、上記の例だと%.2f,%sは実行時に、数値や文字列が置き換わり,これを変換指定子と呼びます。これらの変換指定子は

%[引数順][フラグ][最小フィールド幅][.精度][長さ修飾子]変換指定子
引用元:wikipedia - printf http://ja.wikipedia.org/wiki/Printf#.E5.A4.89.E6.8F.9B.E6.8C.87.E5.AE.9A

で、指定します。ちなみに[]で囲われている部分は任意です。代表的な指定子は以下に挙げます

変換指定子説明代表的な型
d整数を表示整数型 int , charなど
f実数を表示実数型 float , doubleなど
c文字を表示整数型 char , intなど
s文字列を表示文字列

変換指定子の指定は複雑なので、よく使う用例を示します

用例結果
%d123 (左詰め)
%5d__123 (_は空白 指定した桁(最小フィールド幅)未満であれば空白が、
以上であったら通常どおり)
%05d00123 (空白部分を0で埋める)
%.f (=%.0f)123 (小数点以下の人気ためを四捨五入)
%.5f 123.00000 (指定桁(精度)まで表示、それよりも下の桁は四捨五入)

変換指定子を利用した場合、printf関数には引数としてフォーマット指定文字列の次に値や変数を渡す必要があります。

"Aさんの身長は%.2f,体重が%.2f,出身地は%sです。"

このフォーマット指定文字列では3つの変換指定子が、含まれています。このときprintf関数にはフォーマット文字列の次に、3つの変数または数値を渡す必要があります。例えば、以下のように

printf("Aさんの身長は%.2f,体重が%.2f,出身地は%sです。",170.5, weight, "tokyo");

とすると、結果は

Aさんの身長は170.5,体重が65.3,出身地はtokyoです。

となります。このように変換指定子が出てきた順に、変数や数値を順番に渡します。

puts関数

puts関数は引数に指定した文字列を表示する関数です。

puts( 文字列 )

puts関数は自動的に改行するので、注意が必要。

puts(str);
puts("AbC");
/* 結果 */
> tokyo
> Abc					

この時、文字列配列strには"tokyo"が入っているものとする。

入力(標準入力)

キーボードから値を入力するには以下の様な方法があります

  • getchar関数
  • scanf関数
  • gets関数

それぞれの特徴と使い方は以下のとおりです

getchar関数

getchar関数はキーボード(標準入力)から文字を一文字だけ受け取り、戻り値として返す関数です。
int getchar()

戻り値は整数型のint型ですので、注意が必要になります。文字型だと思いchar型変数に代入する際にはキャストを利用し以下のように利用してください。

char c;
printf("キーボードから一文字入力&エンター:");
c = (char)getchar();
printf("%c",c);			
/*結果*/
> キーボードから一文字入力&エンター:a
> a						

scanf関数

フォーマットを指定し、入力する数値、文字列など様々な値を入力することができます。

scanf(フォーマット指定文字列, フォーマットに指定された(複数の)変数のアドレス...)

変換指定子はprintf関数に似ていますが、異なりますので注意が必要です。

% [代入抑止][最大フィールド幅][長さ修飾子]変換指定子
引用元:wikipedia - scanf http://ja.wikipedia.org/wiki/Scanf#.E5.A4.89.E6.8F.9B.E6.8C.87.E5.AE.9A.E5.AD.90

となっており、ここで代表的な変換指定子は

変換指定子説明受け取り側の型
d整数を入力int
f実数を入力float
lf倍精度の実数を入力double
s文字列を入力char配列

フォーマット文字列の後ろには、キーボードから入力された値を格納する変数をしていします。が、格納する領域のアドレスを指定しなければなりません。そのような場合は、変数の前に&記号をつけると、変数のアドレスとなります。また、文字列を入力するときにはchar型の配列を指定しますが、この場合&は必要ありません

Important戻り値として、読み取りに成功した個数を返します。もし、入力にエラーがある場合は戻り値にNULLが返ってきます。

int a;
float b;
char str[81];

printf("整数を入力してください:"); scanf("%d",&a);
printf("実数を入力してください:"); scanf("%f",&b);
printf("文字列を入力してください:") scanf("%s",str); // 文字列の時は&いらない
printf("整数は%d, 実数は,%f, 文字列は%sです。",a,b,str);
整数を入力してください:123
実数を入力してください:123.56
文字列を入力してください:asdf
整数は123, 実数は,123.559998, 文字列はasdfです。

また、スペースタブ改行は区切りとして扱われるので注意が必要です。

int a,b;
printf("整数をスペースを挟んで二つ入力してください:"); scanf("%d%d",&a,&b);
printf("入力した整数は%dと%dです。",a,b);
整数をスペースを挟んで二つ入力してください:1 2
入力した整数は1と2です

gets関数

キーボードから入力した文字列を配列に格納する関数

gets(文字列を格納する配列)

gets関数は入力に失敗した場合、NULLを返す

char str[81];
printf("文字列を入力してください:");
gets(str);
printf("%s",str);
文字列を入力してください:abc
abc

ファイルの入出力

入出力は、キーボードと画面だけではありません。様々な入出力がありますが、ファイル入出力をよく使います。キーボードや画面とは違い、下準備が必要なので、下準備の説明をします。

ファイルを開く

ファイルとは名前が示す通り、文章が詰まったものです。本やノートをイメージしても良いかもしれません。ノートを読んだり、書いたりするためにはまず開くという作業をしなければなりません。コンピュータも同じように開く作業をしなければなりません。何故、そのような作業が必要かとなると、もしかするとそのファイルは自分以外の誰かが開いてるかもしてないし、開くかもしれません。それでは不具合がおきるので、ファイルを開く=ファイルの独占宣言をするってことになります。

ファイルを開くためにfopen関数といったものが用意されていますので、それを利用します。

fopen(ファイル名, モード)

fopen関数は、第一引数にファイル名を文字列で与え、第二引数にモードを文字列で与えます。開くことができればファイルポインタが、開けなければNULLが返されます。

モード記号動作ファイルがあるときファイルがないとき
"w"テキスト書き込み初期化新規作成
"r"テキスト読み込み開くエラー
"a"テキスト追加追加する新規作成
"w+"テキスト書き込み、読み込み初期化新規作成
"r+"テキスト読み込み、書き込み開くエラー
"a+"テキスト追加、読み込み開く新規作成

ファイルポインタを記憶しておくための変数には、FILEという特別な型(本当は構造体です)が用意されています。

FILE *FP

今まで使っていた宣言と違いポインタ変数宣言です。が、あまり気にせず、ファイルを使うときには*を変数名の前につけると覚えておいてください。もちろんファイルポインタ用の変数の名前は任意で決めてもらってもかまいません。

ファイルを閉じる

開いたファイルは閉じなければいけません。つまり、開く=独占宣言から閉じる=開放宣言です。この時注意が必要なのは、ファイルが開けなかった場合、ファイルを閉じる作業をしてはいけません。閉じている本を、閉じるようなもので現実ではむりですよね。

ファイルを閉じるためにfclose関数といったものが用意されていますので、それを利用します。

fclose(ファイルポインタ)

以下はファイルを開いてから、閉じるまでの例です。

FILE *FP; // ファイルポインタを格納する変数
FP = fopen("test.txt","w"); // ファイル名:text.txt を書き込みモード("w")で開く
if(FP == NULL){ // ファイルが開けているかチェック
	printf("ファイルが開けませんでした"); // ファイルが開いていないというメッセージ
	return 1; // エラーでプログラムを終了するときは0以外の値を入れる
}
/* ファイルが開いた時の処理 */
fclose(FP); // ファイルを閉じる

ファイル出力

画面だけでなく、ファイルに出力することもできます。代表的な手段は

  • fprintf関数
  • fputs関数

fprintf関数

fprintf関数printf関数とほぼ同じ使い方で利用することができます。

fprintf(ファイルポインタ, フォーマット指定文字列, フォーマットに指定された(複数の)変数...)

printf関数との違いは第一引数のファイルポインタです。ファイルポインタはファイルを書き込みモードで開いたものを使用してください。

FILE *FP;
FP = fopen("output.txt","w");
fprintf(FP,"test%d",1);
fclose(FP);

出力先のファイルの中には

test1

と記録されています。ここでfopen関数はファイルを開く関数です。fclose関数はファイルを閉じる関数です。

fputs関数

fputs関数は文字列を指定のファイルへ出力する関数です。

fputs(文字列, ファイルポインタ)

puts関数との違いは第二引数のファイルポインタです。ファイルポインタはファイルを書き込みモードで開いたものを使用してください。

FILE *FP;
FP = fopen("output.txt","w");
fputs("test",FP);
fclose(FP);

出力先のファイルの中には

test

と記録されています。ここでfopen関数はファイルを開く関数です。fclose関数はファイルを閉じる関数です。

ファイル入力

ファイルから情報を入力することができます。代表的な手段を以下に挙げます。

  • fscanf関数
  • fgets関数

fscanf関数

使い方は基本的にscanf関数と同じで、フォーマットを指定し、入力することができます。

fscanf(ファイルポインタ, フォーマット指定文字列, フォーマットに指定された(複数の)変数のアドレス...)

scanf関数との違いは第一引数のファイルポインタです。ファイルポインタはファイルを読み込みモードで開いたものを使用してください。

Importantもし、入力にエラーや、ファイルの終端まで到達したら、戻り値にEOFが返ってきます。

以下のようなファイル(intput.txt)を作り

abc 124 xyz

プログラムは

FILE *FP;
char str[81];	// 文字列を格納しておくための配列
int num;	// 整数を格納しておくための配列
int check;	// fscanf関数がきちんと動いているかチェックしておくための変数

FP = fopen("input.txt","r"); // ファイルを開く

if(FP == NULL){ // ファイルが開けたかチェック
	printf("ファイルが開けませんでした");
	return 1;
}

check = fscanf(FP,"%s",str); // 一つ目の単語(文字列)を読み込み
if(check == EOF){ //読み込めたかチェック
	printf("読み込みに失敗したか、ファイルの最後に到達しました");
	return 1;
}
printf("%s\n",str); // 読み込んだ文字列を表示

check = fscanf(FP,"%d",&num); // 2つ目の単語(整数)を読み込み
if(check == EOF){
	printf("読み込みに失敗したか、ファイルの最後に到達しました");
	return 1;
}
printf("%d\n",num);

check = fscanf(FP,"%s",str); // 3つ目の単語(文字列)を読み込み
if(check == EOF){
	printf("読み込みに失敗したか、ファイルの最後に到達しました");
	return 1;
}
printf("%s\n",str);

check = fscanf(FP,"%d",&num); //4つ目の単語を読み込み。ファイルの終端まで読んでも4つ目はない。
if(check == EOF){
	printf("読み込みに失敗したか、ファイルの最後に到達しました");
	return 1;
}
printf("%d\n",num);

fclose(FP);				

として、用意する。四つ目のfscanf関数はファイルの終端に達しているため、check変数にはEOFが格納されているため結果は以下のようになる

abc
123
xyz
読み込みに失敗したか、ファイルの最後に到達しました

4つ目は読み込まれず、きちんと終端に達しているのが確認できる。

fgets関数

ファイルを指定した長さで、文字列として取得できる関数です。

fgets(文字列を格納する配列, 長さ, ファイルポインタ)

ファイルの先頭から、一行もしくは指定した長さの文字-1の文字を文字列に格納する。次に呼び出した時に続きから読み込み始める。必ず配列長>長さとなること。Importantエラーもしくはファイルの終端にくるとNULLが返されるので、それを利用したファイルを最後まで読むのに使う。

以下のソースは3文字読み込む例。読み込むファイルはfscanf関数でも利用したinput.txtを利用している

FILE *FP;
char str[81];

FP = fopen("input.txt","r");
if(FP ==NULL){
	printf("ファイルが開けませんでした\n");
	return 1;
}

fgets(str, 4, FP) ;
printf("%s",str);

fclose(FP);				

結果は以下のとおり

abc

入出力を使った実例

編集中

算術演算

C言語ではプログラムの中で、変数や数値を使って計算をすることがその中でも馴染みのあるのが、加減乗除なはずです。少しプログラミング言語特有なものがありますが、ほぼ数学と同じような働きをします。

演算子優先順位意味
*1乗算a * 2 (aに2を掛ける)
/1除算a / 2 (aを2で割る)
%1剰余 (整数のみ)a % 2 (aを2で割った余り)
+2加算a + 2 (aに2を加える)
-2減算a - 2 (aから2を減らす)
=3代入a = 2 ( aに2を記憶する)
[演算子]=3演算後代入a += 2 ( a = a + 2)

上記の表は四則演算などでよく使うものを、優先順位の高い順に並べたものです。順位の高いものから計算され、同じ順位のものは式の左から順に計算される。

a = 2 * 3 + 7 / 2
> 2 * 3 (=> 6)
> 7 / 2 (=> 3)
> 6 + 3 (=> 9)
> a = 9

上記のように優先順位の高いものから、計算される。整数と整数の除算は整数になる(小数点以下切り捨て)になるので注意。

また、優先順位を制御するために括弧 '(',')'を利用する。

a = 2 * (3 + 7) / 2
> 3 + 7 (=> 10)
> 2 * 10 (=> 20)
> 20 / 2 (=> 10)
> a = 10

比較演算

プログラミングでは条件によって、処理を分岐することが多々あります。条件とは真(0以外)偽(0)の二つです。そこで柔軟に条件を作るためにいくつかの比較演算子が用意されています。

演算子意味
==同値1 == 1 (真), 1 == 2 (偽)
!=異なる値1 != 1 (偽), 1 != 2 (真)
>左辺が大きい2 > 1 (真), 2 > 2 (偽)
<右辺が大きい1 < 2 (真), 2 < 2 (偽)
>=左辺が同じ値か大きい2 >= 2 (真), 1 >= 2 (偽)
<=右辺が同じ値か大きい2 <= 2 (真), 2 <= 1 (偽)

Warning!比較演算は左辺比較演算子右辺として利用するため、例えば

 1 < 2

上記のように利用することができまうが、

 1 < 2 < 3

上記のように利用はできません。エラーは起きませんが、期待通りの結果は得られないでしょう。このように複数の比較をしたい場合は、下記にある論理演算を使って、実現します。

 1 < 2  && 2 < 3 

このように右辺と左辺の形に直さなければ利用できません。

真(0以外)偽(0)は実はprintf関数を利用して、数値として表示することができます。

printf("真:%d\n",1==1);
printf("偽:%d\n",1>2);

実行結果

真:1
偽:0

このように真と偽も数値であるので、表示することができる。例えば繰り返しのwhileで条件式を使わず、真と偽の値を直接使うと

while(1){ 処理 }

このように条件式が必要な箇所に、直接0(偽)か0以外(真)の値を入れることもできます。ちなみに、このプログラムは無限ループとも呼ばれる、永遠に処理を繰り返すプログラムです。

論理演算

比較演算が複数になったりする場合など、複数の条件を取り扱う場合、論理演算を使います。

演算子意味
&&右辺と左辺両方が真ならば真a && b
||右辺と左辺のどちらかが真ならば真a || b
!真(偽)が反転し、偽(真)になる!a
printf("%d && %d は %d\n",1,1, 1&&1);
printf("%d && %d は %d\n",1,0, 1&&0);
printf("%d && %d は %d\n",0,1, 0&&1);
printf("%d && %d は %d\n",0,0, 0&&0);

printf("%d || %d は %d\n",1,1, 1||1);
printf("%d || %d は %d\n",1,0, 1||0);
printf("%d || %d は %d\n",0,1, 0||1);
printf("%d || %d は %d\n",0,0, 0||0);

printf("!%d は %d\n",1,!1);
printf("!%d %d は %d\n",0,!0);
1 && 1 は 1
1 && 0 は 0
0 && 1 は 0
0 && 0 は 0
1 || 1 は 1
1 || 0 は 1
0 || 1 は 1
0 || 0 は 0
!1 は 0
!0 は 1

ビット演算

未編集

局所スコープ

C言語でソースをコーディングしていると、{}をセットで取り扱っているのに気づくと思います(配列の初期化を除く)。これは可読性を高めるためだけでなく、きちんと効果をもっています。それは、変数などが有効に使える範囲を限定することです。限定ときくと不便なように感じるかもしれませんが、実はとても必要なことなのです。

{
	親
	{
		子
	}
}

例えば、上記のように、一つ目の{}の中を親、その中に{}を置いて、これを子とします。親の中で宣言された変数などは親子両方で利用できますが、子で宣言された変数などは子の中でしか利用できません。これをプログラムにして確かめてみると

int a = 10;
{
	int b = 5;
	printf("a=%d b=%d\n",a,b);
}
printf("a=%d b=%d\n",a,b);

実はこれ、コンパイル(もちろんビルドも)できません。何故ならば、最後の行のprintf関数で変数bを表示しようにも、変数bが宣言されていないからです。最後の行は親の領域なので、子の領域の変数宣言は無効です。逆に、4行目のprintf関数に問題はなく、親で宣言された変数aは子でも利用できるためです。

int a = 10; // (A)
int b = 50; // (B)
{
	int b = 5; // (C)
	a = 20; // (D)
	printf("a=%d b=%d\n",a,b); //(E)
}
printf("a=%d b=%d\n",a,b);// (F)
a=20 b=5
a=20 b=50

このプログラムは先程のプログラムと違い、親で変数bを宣言しているので、問題なくコンパイルできます。実行結果をみると、値の変化に気づくと思います。これは、

ステップ(親)変数a(親)変数b(子)変数b表示結果
(A)10
(B)1050
(C)10505
(D)20505
(E)20505a=10 b=5
(D)2050a=20 b=50

このように振る舞っているからです。親で宣言され、子でも同じ名前で宣言された場合、子で宣言された変数が優先されます。多重定義扱いではないというのが大切なポイントです。新しい変数として宣言されるので、親の同名の変数は影響をうけません。また、親で宣言された変数はそののまま子でも利用できます。この仕組みのおかげで、同じ名前が複数あっても、構文範囲を使えばいくらでも利用することができます。これは長文のプログラム複数人での共同作業に適しており、また名前は案外すぐに枯渇するので、その対策にもなります。※プログラミングで大変なものの一つに命名があげられます

また、複数行のプログラムを一つの文としてまとめることができます。例えばif〜elseのように

if(条件式) 真文 else 偽文

このような場合で、構文範囲を使わないと

int a = 1;
if(a) 
	printf("真だった場合:%d",a);
else 
	printf("偽だった場合:%d",a);

このように真文、偽文に一つの文しか書けません。複数行を書く場合は、構文範囲{,}で指定することで、一つの文のような振る舞いとして扱うことができます。つまり以下の様なことができるわけです。

int a = 1;
if(a) { // 真文の領域
	a = a * 20;
	printf("真だった場合:%d",a);
}
else { // 偽文の領域
	a = a * 10;
	printf("偽だった場合:%d",a);
}						
真だった場合:20

様々な処理をする場合、ある程度決まった処理があります。こういった処理は関数にした方が便利です。利点としては

  1. 何度も書かなくて良い
  2. 誰かが作ってくれたものを利用できる

特に2.の恩恵はすでに受けています。printf関数やscanf関数がそうですね。このように関数は中身は知らなくてよいという利点があります。そのため処理が明確でなければならないです。なので、自分で関数を作る場合、キチンと考えて作らなければなりません。

説明を簡単にするため、関数名を全てfuncとしています。

関数宣言

宣言

関数を作成するときは、関数を以下のように宣言します。

戻り値 関数名(引数...){ 処理 }
戻り値
関数で処理した結果帰ってくる値の型。型の詳しくは変数を参照
関数名
変数名と同じ要領で任意の名前で問題ありません。すでに宣言した関数などと同名の関数名をつけることはできません。
引数
必要な数だけ指定してください。この時引数は型 引数名で指定してください。複数指定する場合はカンマ(,)で区切って複数宣言してください。引数が不要な場合は何も書きません(void型を指定して、何もしないと明示したほうがいいかもしれません)
処理
与えられた引数などを使い、必要な処理をし、返す

return

処理をしたら、最後に必ず値を返します。この時、返す値の型は戻り値と同じ型でなくてはならないという条件があります。

return 値
int func(){
	return 1;
}

上記の関数は、戻り値は整数、returnで返される型は整数なので、正しいです。この関数を呼び出すと、数値の1が返されます。

引数に与えられた整数に1を加算し、返す関数を作成しなさい

さて、上記の問題から戻り値引数がわかりますね。引数は整数で、戻り値は整数に+1するので、整数が帰ります。つまりこうなります。

int func(int a){
	return a+1;
}

説明を簡単にするため、関数名はfuncとしましたが、本当はこの関数が何をするかが明瞭な関数名が望ましいです。例えばplusOneとかどうでしょう。

引数に二つの整数を与え、平均を求め返す関数を作成しなさい

引数は、二つの整数。戻り値は平均値なので実数ですね。

float func(int a, int b){
	return (a+b)/2.0;
}

引数が配列の場合

引数に5つ要素の整数配列を与え、平均を求め返す関数を作成しなさい
float func(int a[5]){
	int loop,sum; // 使い捨ての変数は現地調達
	sum = 0;
	for(loop = 0; loop < 5; loop++){
		sum += a[loop];
	}
	return sum/5.0;
}

関数の利用

関数を利用するのは簡単です。宣言された関数名引数を指定するだけです。とりあえず、例を示します。

#include <stdio.h>
int func(int a){ // 関数宣言
	return a+1; // 引数に1を加えて返す
}
int main(){
	int b = 2;
	int c;
	c = func(b); // 関数の利用
	printf("%d",c);
	return 0;
}
3

続いて配列を引数にした場合の例を示します

#include <stdio.h>
float func(int a[5]){ // 関数宣言
	int loop,sum; // 使い捨ての変数は現地調達
	sum = 0;
	for(loop = 0; loop < 5; loop++){
		sum += a[loop];
	}
	return sum/5.0;
}
int main(){
	int input[5];
	float ave;
	input[0] = 1; input[1] = 2; input[2] = 3;
	input[3] = 4; input[4] = 5;
	ave = func(input); // 関数の利用
	printf("%f",ave);
	return 0;
}
3.000000

このように、関数を利用する場合、決まった関数名で、必要な引数決まった型の決まった個数引き渡す。戻り値を利用したい場合、変数を用意して代入する

プロトタイプ宣言

コンパイラはソースコードの一行目から解釈するというのは覚えているでしょうか。ちなみにプログラムはmain関数の中身から実行しましたね。この時、変数宣言より前で変数を使おうとしたときに、エラーになります。このことをちょっと頭に置いて以下のソースコードを実行してみてください。

#include <stdio.h>
int main(){
	int b = 2;
	int c;
	c = func(b); // 関数の利用
	printf("%d",c);
	return 0;
}
int func(int a){ // 関数宣言
	return a+1; // 引数に1を加えて返す
}

コンパイル(ビルド)出来ないできないはずです(コンパイラによっては動くようです)。何故ならば、コンパイラが1行目から順に解釈した時に、5行目で関数が出てきていますが、func関数なんて今までの解釈に出てきてないから知らないぞ!とエラーを出します。でも、複数の関数を定義した場合、関数の前後関係を考えていたらとても大変ですよね。プログラムを書きたいのに、出てきた順番をいちいち考慮しなくちゃいけないなんて大変です。結婚式の席順みたいなものです。結婚式の席順ならどこかで妥協できますが、プログラムは妥協なんて言葉はありません。そこでプロトタイプ宣言を利用します。

#include <stdio.h>
int func(int a); // func関数のプロトタイプ宣言
int main(){
	int b = 2;
	int c;
	c = func(b); // 関数の利用
	printf("%d",c);
	return 0;
}
int func(int a){ // 関数宣言
	return a+1; // 引数に1を加えて返す
}

二行目に追加したのが、プロトタイプ宣言です。使い方は非常に簡単で、関数宣言の中身の無いものを利用する前に宣言するだけです。これは、コンパイラに対してこれからfunc関数が宣言される予定だからとりあえずあるものだと思ってください。と、とりあえず約束しておくものです。だから、関数の本体が呼び出す下にあっても問題はないので、このプログラムはコンパイルも実行もできます。

値を得る

関数で値を得る方法はいくつかあります。

  1. returnを使って値を返す方法
  2. 配列を利用する方法
  3. アドレスを渡す方法

returnを利用した方法は、以前書いたので省略します。

2. 配列を利用する方法

引数に配列を渡し、その配列に対して値を代入すると関数を呼び出した側の配列の中身も変更されます。以下に例を示します。

#include <stdio.h>
// 配列の値を0,1,2,3,4倍にする
void func(int a[5]){
	int i;
	for(i = 0; i < 5;i++){
		a[i] = a[i] * i;
	}
}

int main(){
	int i;
	int b[5]={2,2,2,2,2};
	
	func(b);// 配列の値を0,1,2,3,4倍にする
	for(i = 0; i < 5; i++){
		printf("%d, ",b[i]);
	}
	
	return 0;
}
0, 2, 4, 6, 8, 

上記のプログラムの関数は引数に配列を渡し、その中身を0,1,2,3,4倍し、配列の値を更新します。main関数でfunc関数を実行すると配列の値が0,1,2,3,4倍になることが確認できます。

3. アドレスを渡す方法

関数の引数に値を変更したい変数の場所(アドレス)を渡し、その場所に値を記憶させる方法です。関数を呼び出す際に引数に入れた値は、関数宣言で宣言された引数の変数へとコピーされます。そのため、変数に値を渡しただけでは、関数側の引数の変数を変更しただけでは、関数呼び出し側の変数の値は変更されないのです。そのため、記憶の領域の場所(アドレス)を渡してアドレスに直接値を記憶させます。

#include <stdio.h>

// 渡したアドレスの先に10を記憶させる
void func(int *a){
	// アドレスの先に何かをするときは、変数の前に'*'をつける
	*a = 10;
}

int main(){
	int b = 0;
	// 変数のアドレスを渡すときは変数の前に'&'をつける
	func(&b);
	printf("%d",b);
	
	return 0;
}
10

上記のプログラムのアドレス渡しの部分をイメージした図にしてみました。ちなみに、アドレスは実行する環境、その他のソフトウェアの実行状況によって変わりますので、今回は適当な値にしてあります。なので実行する際には同じアドレスにはなりません。

ちなみに、関数宣言部の引数はポインタと呼ばれるアドレス格納用変数です。ただ位置を記憶させるためのもので、特別なものではありません。

関数の再帰呼び出し

関数から同じ関数を呼び出す(自身を呼び出す関数)処理を再帰呼び出しといいます。

メリット
  • 簡潔に記述できる
デメリット
  • 繰り返しの度に関数呼び出しをするため遅い
  • 関数の深度に限界があるので止まることがある
お約束
  • 必ず終了条件を書く

例として階乗(n!)を求める再帰的関数を挙げます。

5!は5から1までを全て乗算した値です。つまり5 * 4 * 3 * 2 * 1となります。

上の図のように、5!は対象となる数 * (対象となる数-1 * .... * 1)となります。つまり対象となる数 * 対象となる数-1!となるわけです。すると、また階乗が現れます。ですので、その部分をまた階乗を求める関数(自身)で求めてやれば良いわけです。これはつまり関数の再帰呼び出しとなるわけです。これをプログラム書きなおすと以下のようになります。

#include <stdio.h>
// 階乗を求める関数
int func(int v){
	// 終了条件をお忘れなく
	if(v <= 1){ // 引数が1未満だったら
		return 1; // 1を返す
	}
	return v * func(v-1); // 対象となる数(引数) * 対象となる数-1!(関数呼び出し)
}

int main(){
	int loop;
	for(loop = 0; loop <= 5; loop++){
		printf("%d!=%d\n",loop,func(loop));
	}
	return 0;
}
0!=1
1!=1
2!=2
3!=6
4!=24
5!=120

このようにプログラムで表すことができます。例えば5!ならば、 5 * 4!となるわけですから、 5 * 24 = 120となるわけです。

関数作成のコツ

関数を作成する際のコツは非常に簡単です。

  1. 関数化を意識しないでプログラムを作成する
  2. 繰り返しよく使う部分を見つけ出し、関数予定処理にする(2回以上同じプログラムを書くなら関数にしてもよいかも)
  3. 関数予定処理で求めるに必要な値得られる値を見つけ出す
  4. 3.から関数の戻り値,引数を決定し、プロトタイプ宣言を作る
  5. 作っておいたプログラムを参考に関数宣言を完成させる

以下の問題を上記のリスト1〜5に当てはめて作成してみます。

入力した5つの値の平均を求めてください。

1.関数を使わずに作る

この項目は非常に大事です関数を使わずに作れないものは、関数にできません。この項目をクリアできないようでは関数化など不可能です。

int input[5];
float output;
int loop,sum;
// 入力
scanf("%d%d%d%d%d",input[0],input[1],input[2],input[3],input[4]);

// 処理
sum = 0;
for(loop = 0; loop < 5; loop++){
	sum = sum + input[loop];
}
output = sum / 5.0;

// 出力
printf("%f\n",output);
>1 2 3 4 5 6
3.000000

関数予定地を決める

int input[5];
float output;
int loop,sum;
// 入力
scanf("%d%d%d%d%d",input[0],input[1],input[2],input[3],input[4]);

// 処理
sum = 0; // 平均を求める部分(ここを関数にしたい)
for(loop = 0; loop < 5; loop++){
	sum = sum + input[loop];
}
output = sum / 5.0;

// 出力
printf("%f\n",output);

8行目あたりの平均化処理を関数にしたい。

3.求めるのに必要な値&得られる値

求めるのに必要な値
5つの整数
得られる値
平均値(実数)

4.プロトタイプ宣言を作る

3.から

引数
長さが5の整数配列
戻り値
実数を1つ

ということがわかりましたので、プロトタイプ宣言を作ります

float func(int a[5])

5.完成させる

処理をそのまま持ってきても使えません。main関数の中にある変数は構文範囲から一切使えません。使える値は、引数だけです。そこで、main関数を参考に作ってみましょう。

処理の中で使われている、変数や配列は以下の通りです

  • input
  • sum
  • loop

inputは引数の配列aに入っているいるので、配列aで置き換えればよいです。sumloopは一時的に必要なだけなので、関数内で新しく変数として宣言すれば事が足りそうです。

以下はとりあえず、main関数にあった処理部分をコピーしたものです。これを上記の通り書き換えてみましょう。

float func(int a[5]){
	// 処理
	sum = 0; // 平均を求める部分(ここを関数にしたい)
	for(loop = 0; loop < 5; loop++){
		sum = sum + input[loop];
	}
	output = sum / 5.0;
}

以下のプログラムは書き換えを行ったものです

float func(int a[5]){
	int sum; // 宣言しないと使えないので、宣言
	int loop; // 宣言しないと使えないので、宣言
	// 処理
	sum = 0; 
	for(loop = 0; loop < 5; loop++){
		sum = sum + a[loop]; // 引数の配列aに、main関数の配列inputが入っているので、置き換える
	}
	return sum / 5.0; // 結果を返す
}

元のmain関数からこの関数を利用して結果が同じになるか確認しましょう。

#include <stdio.h>
float func(int a[5]);
int main(){
	int input[5];
	float output;
	int loop,sum;
	// 入力
	scanf("%d%d%d%d%d",&input[0],&input[1],&input[2],&input[3],&input[4]);
	
	// 処理
	output = func(input); // 関数に置き換えてみました
	
	// 出力
	printf("%f\n",output);
	
	return 0;
}

float func(int a[5]){
	int sum; // 宣言しないと使えないので、宣言
	int loop; // 宣言しないと使えないので、宣言
	// 処理
	sum = 0; 
	for(loop = 0; loop < 5; loop++){
		sum = sum + a[loop]; // 引数の配列aに、main関数の配列inputが入っているので、置き換える
	}
	return sum / 5.0; // 結果を返す
}
>1 2 3 4 5 6
3.000000

このように同じ結果が得られました。このようにすれば、間違えることなく完成させることができます。ここで大事なのがmain関数の段階で作れないものは関数にできない!ということです。

条件分岐は、条件によって処理を切り替えたり、追加・除外したりするなど、コンピュータを利用する上での大きな利点の一つです。C言語で代表的な条件分岐は

  • if
  • switch

が挙げられます。以下ではこれらの説明を書きます。

if

if分は、の値が与えられた時に特定の処理(真文)を行わせることができます。

if(条件式) 真文
引用元:wikipedia - if文 http://ja.wikipedia.org/wiki/If%E6%96%87#C.E3.81.AE.E5.A0.B4.E5.90.88

主な使い方は以下のとおり

int a = 0;
if(a){
	printf("aは%dである\n",a);
}

a = 1;
if(a){
	printf("aは%dである\n",a);
}
aが1である

上記のように、整数変数aが0の時、ifの条件式には偽(0)が入っているので、真文にあたるprintf関数は実行されません。aに1が代入され、ifの条件式が真(0以外)なので真文のprintf関数が実行される。

一般的にはこのように、真偽を直接扱うことは少なく、比較演算子を利用して、柔軟に対応する。

int a = 5;
int b = 10;
if(a < b){
	printf("%dは%dより小さい",a,b);
}

a = 15;
if(a < b){
	printf("%dは%dより小さい",a,b);
}
5は10より小さい

上記のように条件式に比較演算子で真偽を指定することで、柔軟に対応することができる。

else

ifだけでも、条件式に真の反対の条件となる(偽)となる比較演算子を用いれば、真の場合と、偽の場合で処理を切り替えることができます。ですが、さすがに二度手間ですので、elseが用意されています。

if(条件式) 真文 else 偽文
引用元:wikipedia - if文 http://ja.wikipedia.org/wiki/If%E6%96%87#C.E3.81.AE.E5.A0.B4.E5.90.88

elseは条件式がの場合に、偽文が実行されます。

int a = 5;
int b = 10;
if(a < b){
	printf("%dは%dより小さい\n",a,b);
} else {
	printf("%dは%dより大きい\n",a,b);
}

a = 15;
if(a < b){
	printf("%dは%dより小さい\n",a,b);
} else {
	printf("%dは%dより大きい\n",a,b);
}
5は10より小さい
15は10より大きい

このように、真の場合と偽の場合で、異なった処理を実行することができます。

また、構文をみると明らかですが、いくつも条件をつなげて複雑な条件分岐も可能です

int a = 5;
int b = 10;
int c = 20;
if(a < b){
	if(a < c){
		printf("%dは%dより小さいく、%dよりも小さい\n",a,b,c);
	} else {
		printf("%dは%dより小さいが、%dよりは大きい\n",a,b,c);
	}
} else {
	printf("%dは%dより大きい\n",a,b);
}						
5は10より小さいく、20よりも小さい

このように真文や偽文は文なので、もちろんifなどを利用することが出来る。このように処理の中に処理が書かれる(深くなる)構造を>入れ子といいます。

switch

前述のifだけでも条件分岐は可能ですが、あまり多くなると可読性が落ちてしまう。

if(a == 1){
} else if(a == 2){
} else if(a == 3){
......
} else {
}

このようにifやelseに続けて書けば実現はできそうだけど、大変読みづらい。そこでswitchを使う。

switch (制御式) {
case 値1:
    文
    文
    ………
    
case 値2:
    文
    文
    ………
    
default:
    文
}
引用元:wikipedia - switch文 http://ja.wikipedia.org/wiki/Switch%E6%96%87#C

制御式には整数が入らなければならない。制御式に入った数値と同値のcase以降の文が実行される。また、caseに対応しない数値が入った場合、default以降の文が実行される。もし、defaultの記述がない場合、なにも実行せずに終わる。

int a = 2;
switch(a){
	case 1:
		printf("A\n");
	case 2:
		printf("B\n");
	case 3:
		printf("C\n");
	case 4:
		printf("D\n");
	case 5:
		printf("E\n");
	default:
		printf("default\n");
}
printf("switch文がおわりました\n");
B
C
D
E
default
switch文がおわりました

このような結果になる。意図的にこれを望んでいるならばよいが、例えば、aに2が記憶されていたら、Bだけを表示したい!となった場合は困る。そこでbreakを利用すると、switchの処理を抜け出すことが出来る。それを利用して実現すると、以下の様なプログラムになります。

int a = 2;
switch(a){
	case 1:
		printf("A\n");
		break;
	case 2:
		printf("B\n");
		break;
	case 3:
		printf("C\n");
		break;
	case 4:
		printf("D\n");
		break;
	case 5:
		printf("E\n");
		break;
	default:
		printf("default\n");
}
printf("switch文が終わりました");
B
switch文が終わりました

このようにbreakを使って制御すれば、目的のcaseだけを実行することができる。

コンピュータを使って、なにかやる利点といえば、繰り返し処理です。単純な処理も、人間だったら途方も無い時間を要することを文句も言わず、淡々と処理してくれます。コンピュータを使う最大の旨みではないでしょうか。その繰り返し処理もいくつか特徴をもった方法が提供されています。

  • while
  • do〜while
  • for

while

whileは条件が真の間、真文を実行しつづけます。

while(条件)
	真文

多くの場合は条件にあたる部分が、whileの真文で変化するか、真文の中でifなどを使って、繰り返しを脱しなければ、繰り返し続けます。

簡単な実例を下に示します

while(1){
	printf("繰り返しています\n");
}
繰り返しています
繰り返しています
繰り返しています
繰り返しています
....					

これは繰り返し続ける例で、「繰り返しています」が表示続けて終わりません。終わらないプログラムは欠陥品です。そこで条件をうまく利用してみましょう。

int i = 0;
while( i < 3){
	printf("繰り返してます:%d\n",i);
	i = i + 1;
}
繰り返してます:0
繰り返してます:1
繰り返してます:2

このように、真文の中で条件に関係する値を直接操作すれば繰り返しをコントロールできます。

for

whileは例に上げたように、繰り返す前に条件を制御するための変数を用意して、繰り返す度に何かを更新して条件に変化を与える方法がよく利用されます。そこで明示的に初期化、条件、更新を使うことができる繰り返し文が用意されています。

forはwhileと同じ流れですので、流れ図はwhileと同じです。

for(初期化; 条件; 更新) 真文

forは少々複雑な構文なので、実行順を以下に示します

for(1 ; 2 ;4) 3
1 2 3 4 2 3 4 2 3 .... 4 2

上記のように、初期化の部分は一度しか行われません。この処理をwhileで表すと

初期化
while(条件){
	処理
	更新
}

このようにすれば同じ処理をすることができます。また、逆にforの初期化と更新部分は処理しなくても問題ないので

for(;条件;) 真文

とすればwhileと同じ処理ができます。

以下のようにすると、動きが読み取りやすいのではないでしょうか。

int i;
for(printf("初期化\n"),i = 0; printf("条件\n"),i < 3; printf("更新\n"),i = i + 1)
    printf("繰り返しています:%d\n",i);
初期化
条件
繰り返しています:0
更新
条件
繰り返しています:1
更新
条件
繰り返しています:2
更新
条件

実は処理はカンマ(,)で区切ると続けることができます。このように処理を実行する場所にprintf関数を仕込んで置くと、動きが可視化できミスが発見しやすいです。

do〜while

do〜whilewhileの違いは、真文の位置が条件を判断する前にあることです。whileは条件を判断してから真文を実行していました。

do{
	真文
}while(条件)

以下に例を示します

int i;
do{
    scanf("%d",&i);
}while(i != 0);
1
5
4
6
0

上の例は、iに0が入力されるまで入力を繰り返す例です。このように、do〜whileは条件判断する前に処理ができるので、条件を事前に加工することができる利点があります。今回はキーボードからの入力をさせました。もしこれを、whileで実現すると

int i;
scanf("%d",&i);
while(i != 0){
	scanf("%d",&i);
}

このように余計な処理が増え、プログラムがすっきりとしません。

breakとcontinue

break

breakはfor,while,do〜whileの繰り返しを脱出する。また、switchでも抜け出します。

以下にサンプルプログラムを提示します

int i; // (A)
for(i = 0; i < 3; i = i + 1){ // (B)
    if(i == 1) // (C)
        break; //(D)
    printf("%d\n",i); (E)
}
printf("おわり\n"); // (F)
0
おわり
ステップi説明画面
(A)未定変数宣言
(B)0for文で初期化、比較
(C)0iが1ではないので偽
(E)0画面出力0
(B)1iに1を足す、比較
(C)1iが1なので真
(D)1breakでforを脱出
(F)1"おわり"を出力おわり

このように、処理を途中で打ち切ることができます。

continue

continueはfor,while,do〜whileの繰り返し処理の中で、continue以下の文をスキップして、次の繰り返しを行います。

int i; // (A)
for(i = 0; i < 3; i = i + 1){ //(B)
    if(i == 1) // (C)
        continue; // (D)
    printf("%d\n",i); // (E)
}
printf("おわり\n"); // (F)
0
2
おわり
ステップi説明画面
(A)未定変数宣言
(B)0for文で初期化、比較
(C)0iが1ではないので偽
(E)0画面出力0
(B)1iに1を足す、比較
(C)1iが1なので真
(D)1continueでforへ戻る
(B)2iに1を足す、比較
(C)2iが1ではないので偽
(E)2画面出力2
(B)3iに1を足す、比較すると条件を満たさないのでfor終了
(F)3"おわり"を出力おわり

このように、特定の条件になったら、それ以下の処理をスキップして次の繰り返しに移ることができます。

繰り返しプログラム作成のコツ

繰り返しは実生活でも多々あることなので、難しくはないですが、改めて繰り返しを考えると最初はとまどうものです。そこでちょっとしたコツをメモしておきます。ですが、これは最初の一歩なので、沢山の問題を繰り返し化し、日々の生活の中での繰り返しも発見してみてください。

これから以下の問題を前提に話を進めます

5要素の配列にキーボードから入力した整数値を、全て倍にし、表示する。

1.繰り返したい処理を一度だけ作る

いきなり繰り返そうだなんて、そんな欲張りな事を考えてはいけません。とりあえず、一つできるようにプログラムを作ってみましょう。

int a[5];
int tmp ; // キーボードからの入力をとりあえず入れておく変数

// 入力
// 5回入力したいのでここは繰り返したい
scanf("%d",&tmp);
a[0] = tmp;

// 2倍処理
// 5個倍にしたいので繰り返したい
a[0] = a[0] * 2;

// 出力
// 5個出力したいので繰り返したい
printf("%d ",a[0]);
>1
2 

以上のように、一回なら繰り返しなんて考えなくて良いですね。

2.繰り返し化

前述したプログラムのソースのコメントに書いた通り、繰り返したい部分をあらかじめメモしておくと繰り返し化するときに楽です。このコメントに書いたメモの部分を繰り返し化してみると

int a[5];
int tmp ; // キーボードからの入力をとりあえず入れておく変数
int loop ; // 繰り返し回数を管理する変数

// 入力
// 5回入力したいのでここは繰り返したい
for(loop = 0; loop < 5; loop++){
	scanf("%d",&tmp);
	a[loop] = tmp;
}

// 2倍処理
// 5個倍にしたいので繰り返したい
for(loop = 0; loop < 5; loop++){
	a[loop] = a[loop] * 2;
}

// 出力
// 5個出力したいので繰り返したい
for(loop = 0; loop < 5; loop++){
	printf("%d ",a[loop]);
}
>1   
>2
>3
>4
>5
2 4 6 8 10

このように繰り返したい場所を{,}で囲んで、繰り返しのfor, while, do-whileを使って制御するだけです。とっても簡単ですね。ここでも、入力処理出力に分けて繰り返しを作ると楽です。入力、処理、出力がわからない方はこちらのリンクから。

3.同じ繰り返しをまとめる(オプション)

2.は同じ繰り返しが3回行われています。つまり、3 ☓ 5 = 15回繰り返えさているわけです。確かに見やすいのですが、繰り返しが増えすぎるとコンピュータへ負荷が多くなって、結果を得るまで時間がかかります。そこで、余計な繰り返しはひとまとめにしましょう。ここで注意なのですが、状況によってはまとめられない事が多々あります。例えば、今回のプログラムでもまとめてしまうと問題が起こります。

以下は、同じ繰り返しをすべてまとめてしまった例です(ダメな例)

int a[5];
int tmp ; // キーボードからの入力をとりあえず入れておく変数
int loop ; // 繰り返し回数を管理する変数

for(loop = 0; loop < 5; loop++){
	// 入力
	// 5回入力したいのでここは繰り返したい
	scanf("%d",&tmp);
	a[loop] = tmp;

	// 2倍処理
	// 5個倍にしたいので繰り返したい
	a[loop] = a[loop] * 2;

	// 出力
	// 5個出力したいので繰り返したい
	printf("%d ",a[loop]);
}
1
2 
>2
4 
>3
6 
>4
8 
>5
10

このように、以前の結果と異ってしまいました。このプログラムではとりあえず動きますが、画面に表示される順番が違っただけで、もしプログラムの中で順番の違いが起こったら大変なことになることもあります。そこで、以前の結果と全く同じ結果にするには以下のようにプログラムを書きます。

以下のプログラムは入力と処理をまとて修正しました。

int a[5];
int tmp ; // キーボードからの入力をとりあえず入れておく変数
int loop ; // 繰り返し回数を管理する変数

for(loop = 0; loop < 5; loop++){
	// 入力
	// 5回入力したいのでここは繰り返したい
	scanf("%d",&tmp);
	a[loop] = tmp;

	// 2倍処理
	// 5個倍にしたいので繰り返したい
	a[loop] = a[loop] * 2;
}

// 出力
// 5個出力したいので繰り返したい
for(loop = 0; loop < 5; loop++){
	printf("%d ",a[loop]);
}
>1   
>2
>3
>4
>5
2 4 6 8 10

このようにすれば全く同じ結果になりました。このプログラムの繰り返しの回数は、5☓2=10回繰り返しとなり、2/3の計算量になりました。今回はデータが少ないですが、もし膨大なデータで、3日かかる計算だったら、2日で済んでしまうかもしれません。このように余計な繰り返しをまとめると高速に処理できたりします。ですが前述したように、この作業には注意を払ってください。

文字列とは

文字列とは文字が連続し、終端にヌル文字('\0')で終わっている配列です。文字列はchar型の配列に記憶します。また、文字列をダブルクオート(")囲むことで文字列として扱うことができます。例えば

char str1[81] = "abcdefg";
char str2[81] = {'a','b','c','d','e','f','g','\0'};
char str3[81];
str3[0]='a';str3[1]='b';str3[2]='c';str3[3]='d';str3[4]='e';str3[5]='f';str3[6]='g';str3[7]='\0';

printf("str1:%s str2:%s str3:%s no_str:%s\n",str1,str2,str3,"abcdefg");

上のプログラムを実行すると

str1:abcdefg str2:abcdefg str3:abcdefg no_str:abcdefg

となり、str1,str2,str3,no_strとも入力方法は違いますが、結果が同じになります。つまり、配列の先頭(0番目)から順に文字がひとつずつ入り、最後にヌル文字('\0')で終わるのが文字列だと確認できます。また、no_strの場合は直接文字列を扱っていますが、内部で一度配列として認識してから表示しています。

ここで注意が必要なのが、char型一つでは、半角(8bit)の文字しか扱えないため、もし日本語などの全角(16bit)を利用する場合は、倍の配列の長さが必要になります。また、本ノートでは全て半角文字を扱うこととします。

文字列とはただこれだけです。よく使われるため、別に取り上げましたが、ただの配列です。特別なことはありません

代表的な標準文字列関数

文字列の処理はある程度用途が限定されるので、標準である程度の機能をもった関数が提供されています。文字列処理に関する標準関数を利用するときは必ず#include <string.h>を入れるのを忘れずに。

この項では、以下のようにstring.hをインクルードしたことを前提に記述します。記述する際は /* (A) */の部分を削除してから、プログラムを記述してください。

#include <stdio.h>
#include <string.h> // 追加
int main(){
	/* (A) */
	return 0;
}						

strlen関数

この関数は文字列の長さを返す関数です。

strlen(文字列)
int len;
len = strlen("abcdefg");
printf("%d\n",len);
printf("%d\n",strlen("abcdefg"));
7
7						

このように、結果を整数型の変数や配列に格納してもいいですし、直接なにかに利用することもできます。(値を返すすべての関数でも言えることですが)

文字の長さを数えるという意味では、この関数を利用せずとも、以下のように調べることができます。

char str[81] = "abcdefg";
int cnt = 0; // 文字の数をカウントする変数
int loop;
for(loop = 0; str[loop]!='\0'; loop++){
	cnt++;
}
printf("%sは%d文字です",str,cnt);
abcdefgは7文字です

ポイントは文字列の最後にはヌル文字('\0')が入っているという条件を利用することです。

strcmp関数

文字列を比較するとき、 strcmp関数を利用します。strcmp関数は引数に二つの文字列を与えると、大小関係により整数値を返します。

strcmp(文字列1, 文字列2)

返す値は、文字列が同値なら0それ以外なら、正負の値を返す。正確には、文字列の先頭から一文字ずつ比較し、違う値が見つかった初めの文字を比較し、文字列1>文字列2の場合はを、文字列1<文字列2の場合はを返す。

printf("%sと%sは:%d\n","ABC","ABC",strcmp("ABC","ABC"));
printf("%sと%sは:%d\n","ABC","AB",strcmp("ABC","AB"));
printf("%sと%sは:%d\n","ABC","AAC",strcmp("ABC","AAC"));
printf("%sと%sは:%d\n","AAC","ABC",strcmp("AAC","ABC"));
printf("%sと%sは:%d\n","ABC","abc",strcmp("ABC","abc"));
ABCとABCは:0
ABCとABは:1
ABCとAACは:1
AACとABCは:-1
ABCとabcは:-1

これを利用すれば、例えば文字列の検査ができる。

char str[81];
scanf("%s",str);
if(strcmp("twitter",str)==0){ // 入力した文字が twitter ならば真、でなければ偽
	printf("twitterと入力されました");
}else
	printf("twitterと入力されませんでした");
}

上記のプログラムはキーボードから"twitter"と入力と入力されるかを判断したプログラムです。

日本語からプログラムへ

学校の課題や、自分の問題を解決するためにプログラムを作成する場合、作成するプログラムの概要が日本語(などの自然言語)で書かれていたり、書いたりします。例えば以下のように

キーボードから入力された値(整数)を画面に表示してください

このような場合もあれば、

値を画面に表示してください。ただし、値はキーボードから入力された値(整数)とします。

このような書き方をされている場合があります。もちろん、この二つは同じ意味なので、同一のプログラムで実現しなければなりません。つまり、文に出てきた順にプログラムにするのではないというのは、すぐに理解できますね。ではどの様にプログラムに直したら良いのでしょう

1.仕事を分類してみる

プログラムは以下の3つに大別することができます。

  • 入力
  • 処理
  • 出力

この3つをどのように分別したら良いでしょうか。

 
入力
キーボードから値を入力
処理
特になし
出力
値を画面に出力

このように分別できます。どのようにこれを分けたかと言うと

入力
キーボードやファイルからの入力
処理
演算、分岐、繰り返しなど
出力
画面やファイルに出力

このように分けました。ここで、誤解がないようにいうと、大別しただけなので、入力で繰り返しや分岐なども行ったりします。あくまで、イメージでとりあえず分けます。

入力・処理・出力は大きく分けると以下のように順に実行されます

例えば、ゲームなどは処理をしつつ、ゲームパッドからの入力と、画面への出力を行なっていますが、中をよく覗くと、入力パートと処理パートと出力パートに分かれていて、結局上の図のように構成がどこかに沢山隠されています。

2.キーワードにマッピング

仕事と、プログラムの要素をキーワードでマッピングします。

入力する
プログラムに埋め込み
キーボードから入力
scanf関数・gets関数・getchar関数
ファイルから入力
fscanf関数・fgets関数
◯回繰り返す・◯になるまで繰り返す
while・for・do while
◯◯の時は△する・◯◯の時は△、そうでないときは□
if・switch・if else
画面に出力
printf関数・puts関数
ファイルに出力
fprintf関数・fputs関数

上記の問題をこの一覧にマッピングすると

このようにマッピングできます。さて、いくつも候補がありますが、ここで条件を条件をみてみます。今回は値(整数)を入力しなければなりません。そうなると、入力はscanf関数、出力はprintf関数が適切です。つまり以下のようになります

この時点で、処理する順番や、おおまかに使う関数などの目星が付きます。いよいよコーディングです。

3.コーディング

とりあえず、2.の段階の流れずをプログラムにしてみましょう。

scanf("%d",);
printf("%d",);

もちろんこのプログラムでは動きません。何故ならば、それぞれ(ここではscanf関数とprintf関数)の要件をみたしていません。ここで足らないものは

scanf関数の不足分
値を入れるための変数が不足
printf関数の不足分
出力する値を指定していない

この要件を満たすために、scanf関数の不足分から修正します。修正は処理の頭から直していかなければなりません。どうしてかというと、例えば、料理を作ろうとした場合、材料が間違っているのに、作り方をいくら見なおしても結果は絶対に間違ったままだからです。

では、scanf関数に必要な変数を追加し、併せてprintf関数の不足分も満たせそうなので修正します。

int a;
scanf("%d",&a);
printf("%d",a);

一見すると問題が解決できたように思えます。ただ、まだ終わりではありません

4.確認

最後の最後に確認です。まず、問題を見直しましょう。

キーボードから入力された値(整数)を画面に表示してください

このような問題でした。大事なのはキーボードから入る値は必ず整数であるという点。つまり、確認のためのテストでは整数だけで良いという点。多くの場合は整数以外の値が入った時の扱いも問題に記載されています。

ある限りの条件をリストアップし実行の確認をしたら、完成です。

余談ですが、入力する形式には必ずチェックを入れるべきです。下記の問題のように入力を間違えた後の処理まで書いてあるのが普通なのではないでしょうか。

キーボードから入力された値(整数)を画面に表示してください。もし、値が整数以外の場合は「整数ではありません」と表示し、エラーとしてプログラムを終了してください。

このように入力のチェックなどのも問題に書いてあります。このような場合、入力直後にチェックするべきケースが多いです。チェックはもし☓☓だったらと解釈できますので、条件分岐を用います

#include 

int main(){
	int a;
	int cnt;
	
	cnt = scanf("%d",&a);
	if(cnt == 0){
		printf("整数ではありません");
		return 1;
	}
	
	printf("%d",a);
	
	return 0;
}
> 5
5
> a
整数ではありません

このように入力をチェックして、処理したりします。入力チェックはそれぞれ条件がことなるので、色々な方法がありますが、2.キーボードのマッピングでさらに細かくマッピングしてみてください。

例題

キーボードから入力した値(整数)回分、「HelloWorld!」と表示する

キーワード:変数scanfforprintf比較演算

int a; // キーボードからの値を記憶する変数
int loop; // 繰り返し回数を管理するための変数
scanf("%d",&a); // キーボードから繰り返す回数を入力
for(loop=0; loop < a; loop++){ // loop を 0 から a-1 まで a回繰り返す
	printf("HelloWorld!\n"); // HelloWorld!と表示
}
>3
HelloWorld!
HelloWorld!
HelloWorld!
日本語、英語、中国語の得点を3人分入力し、表示する

安直に実装する例 キーワード:配列変数printf比較演算

int ja[3]; // 3人分の日本語の点数を記憶する配列
int en[3]; // 3人分の英語の点数を記憶する配列
int cn[3]; // 3人分の中国語の点数を記憶する配列

// 入力
ja[0] = 39; en[0] = 82; cn[0] = 60; // 0番目の人の得点
ja[1] = 79; en[1] = 68; cn[1] = 29; // 1番目の人の得点
ja[2] = 22; en[2] = 58; cn[2] = 75; // 2番目の人の得点

// 処理

// 出力
printf("日本語:%d 英語:%d 中国語:%d\n",ja[0],en[0],cn[0]);
printf("日本語:%d 英語:%d 中国語:%d\n",ja[1],en[1],cn[1]);
printf("日本語:%d 英語:%d 中国語:%d\n",ja[2],en[2],cn[2]);
日本語:39 英語:82 中国語:60
日本語:79 英語:68 中国語:29
日本語:22 英語:58 中国語:75

少しだけ賢くした例

int loop ; // 繰り返し回数を管理する変数
int ja[3]; // 3人分の日本語の点数を記憶する配列
int en[3]; // 3人分の英語の点数を記憶する配列
int cn[3]; // 3人分の中国語の点数を記憶する配列

// 入力
ja[0] = 39; en[0] = 82; cn[0] = 60; // 0番目の人の得点
ja[1] = 79; en[1] = 68; cn[1] = 29; // 1番目の人の得点
ja[2] = 22; en[2] = 58; cn[2] = 75; // 2番目の人の得点

// 出力
for(loop = 0 ; loop < 3; loop++){ // 3人分繰り返して表示
	printf("日本語:%d 英語:%d 中国語:%d\n",ja[loop],en[loop],cn[loop]);
}

多重配列を使ってもっと賢くした例

int loop_member ; // 人数分の繰り返し回数を管理する変数
int loop_subject ; // 科目分の繰り返し回数を管理する変数
int point[3][3]; // 点数を記憶する配列 [人数][科目]
char message[3][20] = {"日本語:","英語:","中国語:"}; // メッセージ用の文字列配列

// 入力
point[0][0] = 39; point[0][1] = 82; point[0][2] = 60; // 0番目の人の得点
point[1][0] = 79; point[1][1] = 68; point[1][2] = 29; // 1番目の人の得点
point[2][0] = 22; point[2][1] = 58; point[2][2] = 75; // 2番目の人の得点

// 出力
for(loop_member = 0; loop_member < 3; loop_member++){ // 0人目、1人目、2人目の繰り返し
	for(loop_subject = 0 ; loop_subject < 3; loop_subject++){ // 日本語、英語、中国語の繰り返し
		printf("%s%d",message[loop_subject],point[loop_member][loop_subject]); // 得点を表示
	}
	printf("\n"); // 一人分表示が終わったら改行
}

プログラムの段落が深くなって見辛い場合は、ソースをコピーして、テキストエディタに貼り付けてから読んでください。

複数の正の整数が空白と改行で区切られたファイルから、偶数と奇数の数をそれぞれ表示する。
1 5 12
7 93
2

キーワード:変数fopenfclosewhilefscanfprintf

int cnt[2]; // 偶数(0)・奇数(1)のカウント用配列
int val; // 読み込んだ整数の一時保管用変数
FILE *FP; // 読み込みファイル用ファイルポインタ

// 入力
FP = fopen("numbers.txt","r"); // 読み込みモードで開く
if(FP ==NULL){ // オープンのチェック
	printf("ファイルが開けませんでした");
	return 1;
}
cnt[0] = cnt[1] = 0; // カウンタの初期化
while(fscanf(FP,"%d",&val)!=EOF){ // ファイルの終端まで繰り返す
	// valに読み込んだ整数が記憶されている
	cnt[val%2]++; // val%2=0なら偶数, =1なら奇数
}
fclose(FP);

// 出力
printf("偶数:%d\n",cnt[0]);
printf("奇数:%d\n",cnt[1]);
偶数:2
奇数:4
ファイルから日本語、英語、中国語の得点を3人分入力し、それぞれ表示する。ファイルには以下の内容を記述しておく。
39 82 60
79 68 29
22 58 75

キーワード:変数配列fopenfcloseforfscanfprintf

int loop_member ; // 人数分の繰り返し回数を管理する変数
int loop_subject ; // 科目分の繰り返し回数を管理する変数
int point[3][3]; // 点数を記憶する配列 [人数][科目]
int tpoint; // 読み込んだ点数を一時的に保管する
int check_eof; // ファイルが終端かどうかチェックするための変数 
char message[3][20] = {"日本語:","英語:","中国語:"}; // メッセージ用の文字列配列
FILE *FP; // 入力用ファイルのためのファイルポインタ

// 入力
FP = fopen("points.txt","r"); // 読み込みモードでファイルを開く
if(FP ==NULL){
	printf("ファイルが開けません");
	return 1;
}
for(loop_member = 0; loop_member < 3; loop_member++){ // 0人目、1人目、2人目の繰り返し
	for(loop_subject = 0 ; loop_subject < 3; loop_subject++){ // 日本語、英語、中国語の繰り返し
		check_eof=fscanf(FP, "%d", &tpoint); // ファイルから整数を一つ読み込む
		if(check_eof == EOF){ // ファイルが終端まで達しているか
			printf("終端まで読み込みました\n"); // メッセージを出す
			break; // とりあえずループを脱出
		}
		point[loop_member][loop_subject] = tpoint; // 得点を記憶
	}
}
fclose(FP);

// 出力
for(loop_member = 0; loop_member < 3; loop_member++){ // 0人目、1人目、2人目の繰り返し
	for(loop_subject = 0 ; loop_subject < 3; loop_subject++){ // 日本語、英語、中国語の繰り返し
		printf("%s%d",message[loop_subject],point[loop_member][loop_subject]); // 得点を表示
	}
	printf("\n"); // 一人分表示が終わったら改行
}
日本語:39 英語:82 中国語:60
日本語:79 英語:68 中国語:29
日本語:22 英語:58 中国語:75
ファイルから日本語、英語、中国語の得点を3人分入力し、一人ずつ合計を求め、各点数と合計を表示する。ファイルには以下の内容を記述しておく。
39 82 60
79 68 29
22 58 75

キーワード:変数配列fopenfcloseforfscanfprintf

int loop_member ; // 人数分の繰り返し回数を管理する変数
int loop_subject ; // 科目分の繰り返し回数を管理する変数
int point[3][3]; // 点数を記憶する配列 [人数][科目]
int sum[3]; // 合計を記憶する配列
int tpoint; // 読み込んだ点数を一時的に保管する
int check_eof; // ファイルが終端かどうかチェックするための変数 
char message[3][20] = {"日本語:","英語:","中国語:"}; // メッセージ用の文字列配列
FILE *FP; // 入力用ファイルのためのファイルポインタ

// 入力
FP = fopen("points.txt","r"); // 読み込みモードでファイルを開く
if(FP ==NULL){
	printf("ファイルが開けません");
	return 1;
}
for(loop_member = 0; loop_member < 3; loop_member++){ // 0人目、1人目、2人目の繰り返し
	for(loop_subject = 0 ; loop_subject < 3; loop_subject++){ // 日本語、英語、中国語の繰り返し
		check_eof=fscanf(FP, "%d", &tpoint); // ファイルから整数を一つ読み込む
		if(check_eof == EOF){ // ファイルが終端まで達しているか
			printf("終端まで読み込みました\n"); // メッセージを出す
			break; // とりあえずループを脱出
		}
		point[loop_member][loop_subject] = tpoint; // 得点を記憶
	}
}
fclose(FP);

// 処理 (New)
// 合計を求める
for(loop_member = 0; loop_member < 3; loop_member++){ // 0人目、1人目、2人目の繰り返し
	int sum_person = 0; // 3科目の合計点を一時的に記憶する変数
	for(loop_subject = 0 ; loop_subject < 3; loop_subject++){ // 日本語、英語、中国語の繰り返し
		sum_person = sum_person + point[loop_member][loop_subject];
	}
	sum[loop_member] = sum_person; // 3科目の合計を配列に記憶
}

// 出力
for(loop_member = 0; loop_member < 3; loop_member++){ // 0人目、1人目、2人目の繰り返し
	for(loop_subject = 0 ; loop_subject < 3; loop_subject++){ // 日本語、英語、中国語の繰り返し
		printf("%s%d ",message[loop_subject],point[loop_member][loop_subject]); // 得点を表示
	}
	printf("合計:%d",sum[loop_member]); // 合計点の表示(New)
	printf("\n"); // 一人分表示が終わったら改行
}
日本語:39 英語:82 中国語:60 合計:181
日本語:79 英語:68 中国語:29 合計:176
日本語:22 英語:58 中国語:75 合計:155

予約語


標準関数






逆引き