C言語編 第8章 理解の定着・小休止@

先頭へ戻る

このエントリーをはてなブックマークに追加

この章の概要

この章の概要です。

理解の定着・小休止@

さて、この章ではこれまでに見てきた内容の理解を再確認しましょう。 また、1章丸ごとを割くほどでも無い細かい部分について、少し触れていきます。

今回は、以下の範囲が対象です。 基本的な文法と、標準入出力がテーマとなります。

C言語での処理の単位はです。 文は基本的に、セミコロン(;)が現れるまで続きます。

文は、1行に複数記述することもできるし、セミコロンしかない文(空文)を作ることもできます。 ただし、できる限り、1行に複数の文を記述することは避けた方が良いでしょう。

文1;  文2;    /* 1行に2つの文 */
;               /* 空文 */

/* と */ で囲まれた範囲は、コメントと呼ばれます。 コメントの中では、日本語も含めて自由に記述できます。この部分は、プログラムの実行に影響を与えません。

処理の流れ

C言語のプログラムは、main関数から開始されます。そのため、main関数を書くことは必須です。 main関数の終わりには、return文を書きます。

int main(void)
{
	return 0;
}

main関数から開始した処理は、そのまま、上から下へ向かって流れていきます。 もし、1行に複数の文がある場合、左から右へ向かって流れていきます

今後、C言語の学習を進めていくと、より複雑な処理の流れを行うことがありますが、基本は上に書いた通りです。 プログラムが思った通りに動作してくれないとき、処理の流れを追いかけてみる必要が出てきます(この作業をトレースといいます)。 このとき、処理の流れがどういう法則で行われているかを知っておくことは重要です。

ソースコード上の文字

ソースコード上では、コメントの部分を除くと、半角のアルファベット、数字、アンダーバーが使えます。 コメントの部分は、プログラムの実行に一切の影響を与えないので、他の文字も使用できます。

また、半角空白とタブ文字、改行文字が使用でき、これらにより、ソースコードを見やすい形に整形できます。 全角文字は使用できません。謎めいたコンパイルエラーの原因になることもあるので、注意して下さい。

アルファベットに関しては、大文字と小文字は区別されます。 ですから、例えば「return」と「RETURN」は別物になります。

型と変数

「100」や「5.5」「'X'」「"abcde"」といったデータが、どんな種類であるかをという考え方で表現します。

データ 型の分類 変数の型
100 整数型 intなど
5.5 浮動小数点型 double、float
'X' 文字型 char
"abc" 文字列型 char[]

変数は、プログラムの実行中に変化し得る値で、型を持ちます。 上の表にあるように、型の種類に合わせて「int」や「double」といった適切な指示を与え、更に変数名を指示します。 この過程を宣言と呼び、次のように記述します。

int main(void)
{
	int num;         /* 宣言 */
	int num2 = 10;   /* 宣言+初期化 */

	return 0;
}

このサンプルで、変数num の値は不定であり、プログラムを実行するたびに変化する可能性があります。 一方、変数num2 は初期値として 10 が与えられており、必ず 10 で開始されます。 初期化は、後から行うこともできます。 初期値を与えることを、初期化と呼びます。

int main(void)
{
	int num;    /* 宣言 */
	
	num = 100;  /* 初期化(代入) */

	return 0;
}

変数に値を格納することを代入と呼びます。 宣言と同時に初期値を与える場合でも、後から代入によって行う場合でも、初めて値を与えるとき、初期化と呼びます。

文字は「''」で囲んで表現し、文字列は「""」で囲んで表現します。 変数としては、文字は char型で、文字列は char str[10] のように、必要な文字数とともに宣言します。 [10] のような記述を伴う変数は、配列と呼ばれます。

文字列の末尾には、'\0' という見えない文字が存在している点に注意が必要で、 char str[10] の場合、'\0' を除く9文字分しか格納できません。

変数の名前には、ある程度の制約があります。 以下に従う必要があります。

なお、先頭にアンダーバーが来る名前は、コンパイラの実装のために予約されているので、避けた方が無難です。

標準出力

実行環境にもよりますが、実行の結果や経過は画面に表示することが一般的です。 このとき、画面は標準的な出力先と考えることができ、標準出力と呼ばれます。 必ずしも、「標準出力=画面」とは限りませんが、ほとんどの環境ではそうなっているはずです。

標準出力へ文字列を出力するには、puts関数(⇒リファレンス)を使用します。

#include <stdio.h>

int main(void)
{
	puts( "Hello, World" );

	return 0;
}

実行結果:

Hello, World

puts関数を使用するには、「#include <stdio.h>」という記述が必要です。 なお、puts関数は自動的に改行を行います

現実的には、数字なども出力する必要があるでしょう。 その場合、printf関数(⇒リファレンス)を使います。 printf関数も「#include <stdio.h>」が必要です。

#include <stdio.h>

int main(void)
{
	int num = 100;
	
	printf( "%d\n", num );

	return 0;
}

実行結果:

100

printf関数に渡す1つ目の情報には、2つ目以降の情報をどのようなフォーマットとして扱うかの指示が含まれます。 以下のように、データの種類ごとに使い分ける必要があります。

フォーマット指定 意味
%d 10進数の整数
%f 浮動小数点数
%c 文字
%s 文字列

また、以下のような特別なものも覚えておきましょう。

指定 意味
\n 改行
%% % そのものを出力
\\ \ そのものを出力
\" " そのものを出力

また、printf関数には複数のデータを一気に出力する能力があります。

#include <stdio.h>

int main(void)
{
	int num  = 100;
	double f = 7.2;
	
	printf( "%d, %f\n", num, f );

	return 0;
}

実行結果:

100, 7.200000

標準入力

標準出力に対して、データを入力する側を標準入力と呼び、通常はキーボードが割り当てられています。 標準出力と同様、必ずしも「標準入力=キーボード」とは限りません。

fgets関数(⇒リファレンス)を使うと、文字列の入力を受け取ることができます。 以下のサンプルプログラムでは、標準入力から、最大で 80文字の文字列を受け取ります。

#include <stdio.h>

int main(void)
{
	char str[80];  /* 入力された文字列を格納する場所 */


	puts( "何か文字列を入力してください。" );
	fgets( str, sizeof(str), stdin );

	/* 入力された文字列をそのまま出力する */
	printf( "%s", str );

	return 0;
}

実行結果:

何か文字列を入力してください。
abcde
abcde

fgets関数は、末尾の改行文字(最後に押した Enterキーの分)も受け取ります

結果を受け取る変数は、文字列を受け取るため char str[80] のような配列を宣言します。 fgets関数の2つ目の情報には、この大きさを渡しますが、このとき sizeof演算子を使うと簡単です。

fgets関数の他、scanf関数(⇒リファレンス)を使う方法もあります。 scanf関数の場合は、整数や実数を受け取ることもできますが、幾つかの問題を避けるためにも、 fgets関数と sscanf関数(⇒リファレンス)を組み合わせる手法を使う方が良いでしょう。

scanf関数で、整数を受け取る例は次のようになります。

#include <stdio.h>

int main(void)
{
	int num;

	/* 数値を受け取って、2倍した値を出力する */
	puts( "数値を入力してください。" );
	scanf( "%d", &num );
	printf( "%d\n", num * 2 );

	return 0;
}

実行結果:

数値を入力してください。
10
20

fgets関数と sscanf関数を組み合わせる場合は、次のようになります。

#include <stdio.h>

int main(void)
{
	char str[10];
	int num;

	/* 数値を受け取って、2倍した値を出力する */
	puts( "数値を入力してください。" );
	fgets( str, sizeof(str), stdin );
	sscanf( str, "%d", &num );
	printf( "%d\n", num * 2 );

	return 0;
}

実行結果:

数値を入力してください。
10
20

scanf関数は、標準入力の文字列をフォーマット指定に従って、変数に取り出す関数です。 一方、sscanf関数は、どこかにある char型の配列が対象になるだけで、その他の点では scanf関数と同じです。

フォーマット指定の方法は、printf関数と似ています。

フォーマット指定 意味
%d 10進数の整数。int型変数で受け取る
%f 浮動小数点数。float型変数で受け取る
%lf 浮動小数点数。double型変数で受け取る
%c 文字。char型変数で受け取る
%s 文字列。char型配列で受け取る

これらの関数で、文字列以外("%s"以外の指定)を使う場合には、 受け取り側の変数の名前の先頭に「&」を付ける必要があります

printf関数とは異なり、「%lf」という指定がある点に注意が必要です。 printf関数では、float型でも double型でも「%f」で指示を与えますが、scanf関数や sscanf関数では、 float型と double型とで、指示の仕方を変えなくてはなりません

fgets関数の場合は、受け取る文字数を指定することが必須ですが、 scanf関数の場合は、"%20s" のような感じで、忘れずに最大文字数を指示する必要があります。 この点においても、fgets関数の方が安全性が高いと言えます。

この最大文字数の指定を誤ると、受け取り側の配列に収まりきらず、バッファオーバーフローという現象を起こしてしまいます。 この現象は必ず避ける必要があるので、最大文字数の指定は重要です。

演算子

四則演算に代表される計算を行うには、専用の演算子を使います。

意味 演算子
加算 +
減算 -
乗算 *
除算 /
剰余 %

これらの演算子を使ったサンプルプログラムは、以下のようになります。

#include <stdio.h>

int main(void)
{
	int sum = 30 + 4;
	int sub = 30 - 4;
	int mul = 30 * 4;
	int div = 30 / 4;
	int mod = 30 % 4;

	printf( "sum: %d\n", sum );
	printf( "sub: %d\n", sub );
	printf( "mul: %d\n", mul );
	printf( "div: %d\n", div );
	printf( "mod: %d\n", mod );

	return 0;
}

実行結果:

sum: 34
sub: 26
mul: 120
div: 7
mod: 2

除算と剰余に関して、0で割るという行為が行えないことに注意して下さい。 これはコンパイルはできますが、実行するとエラーになってしまう不正な処理です。 また、剰余は余りを求めるので、実数で割ることはできません。

演算子には、一般的な数学と同様に、乗算・除算・剰余を先に計算するという優先順位の性質があります。 この優先順位は、( ) を使うことで変更できます。 ただし、1つの式の中に、同じ優先順位が複数登場する場合、どちらから計算されるかは決まっていません。 この点に関しては、左から右のような規則は存在しません。

また、整数同士の計算の結果が、実数になってしまった場合(例えば、10 / 3 のような計算)、小数点以下は切り捨てられます。 切り捨てられないようにするには、double型や float型を使う必要があります。


練習問題

まとめとして、多めに練習問題を用意しました。★の数は難易度を表します。

問題@ 次のプログラムは間違っています。間違いを指摘して下さい。[★]

#include <stdio.h>

int main(void)
{
	int    a = 15;
	double f = 4.2;
	
	printf( '%d %f\n', a, f );

	return 0;
}

問題A 次のプログラムには問題があります。指摘して下さい。[★]

#include <stdio.h>

int main(void)
{
	int a = 0;
	int b = 10;
	int c = 20;
	
	printf( "%d * %d = %d\n", a, b, a * b );
	printf( "%d / %d = %d\n", c, a, c / a );
	printf( "%d + %d = %d\n", b, c, b + c );

	return 0;
}

問題B 次のプログラムの間違いを指摘して下さい。[★]

#include <stdio.h>

int main(void)
{
	int num = 10;
	char c = 'C';

	puts( "%d, %c\n", num, c );

	return 0;
}

問題C 次のプログラムと同じ結果になるように、puts関数を使ったプログラムを書いて下さい。[★]

#include <stdio.h>

int main(void)
{
	printf( "abc" );
	printf( "def\n" );
	printf( "ghi\n" );

	return 0;
}

問題D 「\300 の 20%引きは \240 です」という文字列を、puts関数と printf関数で出力するプログラムを書いて下さい。[★★]

問題E 標準入力から秒数を受け取り、それを時・分・秒に変換して出力するプログラムを書いて下さい。[★★]

問題F 標準入力から3つの整数を受け取り、その合計と平均値を出力するプログラムを書いて下さい。[★★]

問題G 標準入力から受け取った整数を、次の表の規則性に従うように変換し、出力するプログラムを書いて下さい。[★★★]

入力値 出力値
0 0
1 2
2 2
3 4
4 4
5 6
6 6
7 8
8以上も同様に続く ???

問題H 標準入力から受け取った整数の符号を反転して出力するプログラムを書いて下さい。「10」なら「-10」に、「-10」なら「10」にします。[★★]

問題I 標準入力から受け取った文字列を、"" で挟んで出力するプログラムを書いて下さい。[★★★]

問題J 標準入力から半径の長さを受け取り、円の面積と、円周の長さを出力するプログラムを書いて下さい。 円周率にどの程度の精度を与えるかは各自の自由としますが、最低でも 3.14 として下さい。[★★★]

問題K 次のプログラムは正しいでしょうか。[★]

#include <stdio.h>

int main(void)
{
	/*  puts関数に変えてみた
	printf( "コメントは /* と */ で挟む\n" );
	*/
	puts( "コメントは /* と */ で挟む" );

	return 0;
}

問題L 次のプログラムは正しいでしょうか。[★]

int main(void)
{
	3 + 7;

	return 0;
}

問題M 次のプログラムは正しいでしょうか。[★]

#include <stdio.h>

int main(void)
{
	puts( "Hello!!" );;;;;;;;;;;;;;;;;;;

	return 0;
}

問題N 次のプログラムが出力する値は幾つになるでしょう。[★]

#include <stdio.h>

int main(void)
{
	int num = 50;
	
	printf( "%d\n", num / 100 * 80 );	/* num の 80% を出力 */

	return 0;
}


解答ページはこちら

参考リンク

更新履歴

'2012/2/14 変数名のルールを修正。

'2011/2/5 単に「小数」と表記していた箇所を、「実数」や「浮動小数点数」に改めた。

'2010/2/27 main() を main(void) に修正。

'2009/3/22 「この章の概要」を追加。

'2009/1/31 新規作成。



前の章へ

次の章へ

C言語編のトップページへ

Programming Place Plus のトップページへ