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

先頭へ戻る

この章の概要

この章の概要です。

理解の定着・小休止①

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

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

C言語では、は、基本的に、セミコロン(;)が現れるまで続きます。 セミコロンまで含めて、1つの文です。

文は、1行に複数記述することもできるし、 セミコロンしかない文(空文)を作ることもできます。 基本的には1行1文を守った方が見やすいでしょう。

次のコードの1行目には、複数の文があります。 2行目は、空文です。

a = 0; b = 0;
;

コメント

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

a = 0; b = 0;    /* 1行に2つの文 */
;                /* 空文 */

C99 では、//形式のコメントが使用できます(第5章

処理の流れ

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

C99 では、return文を省略できることがあります(第1章)。

int main(void)
{
    return 0;
}

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

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

ソースコード上の文字

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

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

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

型と変数

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

型の分類 型の名前
100 整数型 int
5.5 実数型 double、float
'X' 文字型 char
"abc" 文字列型 char[]

実数型とありますが、C言語では実数は、整数以外の数値のことをいいます。 要するに、小数点を含むような数のことです。

変数は、型を持っており、その型に見合った値を保持することができます。 この値は、プログラムの実行中に変化させることができます。

変数を使うには、型と名前の指示を行う、宣言という行為が必要です。 具体的には、次のように記述します。

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

    return 0;
}

このサンプルで、変数num には値を与えていないため、不定値になっています。 不定値が入っている変数の値を使おうとしてはいけません。

一方、変数num2 には、初期値として 10 が与えられています。 変数に最初の値を与えることを、初期化と呼びます。 初期化は、後から行うこともできます。

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

	return 0;
}

変数に値を格納することを代入と呼びます。


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

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

文字と文字列

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

文字列の末尾には、'\0' という見えない文字(ヌル文字)が存在しています。 そのため、「char str[10]」の場合、'\0' が1文字分の場所を使うので、残り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関数を組み合わせる手法を使う方が良いでしょう。

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関数と同じです。

scanf関数や sscanf関数の変換指定子は、printf関数と似ています。

変換指定子 意味
%d 10進数の整数。int型変数で受け取る
%f 浮動小数点数。float型変数で受け取る
%c 文字。char型変数で受け取る
%s 文字列。char型配列で受け取る

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

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

"%lf" は、"%f" という変換指定子に、"l" という追加情報(変換修飾子)を与えた形です。

fgets関数の場合は、受け取る文字数を指定することが必須ですが、 scanf関数の場合は、"%19s" のような感じで、忘れずに最大文字数を指示する必要があります。 20文字分の char型配列に対して、"%19s" のように指定しなければならないですし、間違いやすくもあります。 この点においても、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;
}

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

#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;
}

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

#include <stdio.h>

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

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

    return 0;
}

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

#include <stdio.h>

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

    return 0;
}

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

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

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

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

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

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

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

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

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

#include <stdio.h>

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

    return 0;
}

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

int main(void)
{
    3 + 7;

    return 0;
}

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

#include <stdio.h>

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

    return 0;
}

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

#include <stdio.h>

int main(void)
{
    int num = 50;

    printf( "%d\n", num / 100 * 80 );	/* num の 80% を出力 */

    return 0;
}


解答ページはこちら

参考リンク

更新履歴

'2018/2/2 全面的に文章を見直し、修正を行った。

'2018/2/1 C言語編全体で表記を統一するため、「フォーマット指定」を「変換指定子」に改めた。

'2018/1/23 現在の第1章の内容に合わせて見直し、修正。

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

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

▼更に古い更新履歴を展開


このエントリーをはてなブックマークに追加
rss1.0 取得ボタン RSS

前の章へ(第7章 標準入力②)

次の章へ(第9章 関数)

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

Programming Place Plus のトップページへ