ここまでのプログラムでは for や if の後ろは、実行されるべき文を
必ず '{' と '}' で括りブロックを形成していた。
実は、C では、この '{' と '}' は書かなくてもよい場合が存在する。
実はfor文は、繰り返し処理を行う文を1つだけ取る。
同様に if文で、条件が成立した際に実行されるのも単一の文である。
ところが、実際に行いたい処理が単一文で書くことができるのは滅多にない。
そのような場合には、 '{' と '}' で文を括ることにより、それらの複数の文は
一つの文と同じように扱われる。
これを「複文(compound statement)」と呼ぶ。
単一の文であっても、'{' と '}' でくくって複文としても良いので、これまでは
必ず複文にしていたのである。
C で利用できる繰り返し処理の方法には、これまでに出てきた for文、
while文に加えて、do〜while文が存在する。
for文
for (初期化; 条件式; 再初期化)
繰り返し文
forに突入した段階で、「初期化」文を実行し、繰り返し文を実行する。
次に再初期化を行い、条件式が真か偽かの判断を行う。
条件が成立していれば再び繰り返し文を実行する。
以下、条件が成立している限り「繰り返し文」→「再初期化」を繰り返す。
初期化、条件式、再初期化の各文については不要な部分を省略することができる。
while文
while (条件式)
繰り返し文
条件式が成立している限り、実行文を繰り返す。
while文に突入した時点で、条件が成立していなければ、繰り返し文は一度も実行されない
あらかじめ、繰り返し回数が判っていない場合などに、よく用いられる。
do〜while文
do
繰り返し文
while (条件式)
while文に良く似ている。異なる点は、条件判定が、繰り返し文を実行したあとに行われる点である。
したがって、繰り返し文は、必ず一度は実行される点がwhile文とは大きく異なる。
上記 3種の構造は次のような図で表すことができる。
C で利用できる条件分岐としては、今まで学習した if(-else)に加えて
(先週フライングでちょっとだけ出てきた)switch文がある。
if文
if (条件)
条件が成立した場合の文;
else
条件が成立しなかった場合の文;
条件式が成立しているかどうかで、実行文を切り替える。
不要であれば、else以下は省略することができる。
各場合で行う処理が複数ある場合は複文にするのを忘れないように。
「条件が成立しなかった場合の文」にも if文を書くことができるので、複数の条件で分岐したい場合には次のような形となる。
if (条件1)
条件1が成立した場合の文;
else if (条件2)
条件1が成立せず、条件2が成立した場合の文;
else
条件1も条件2も成立しなかった場合の文;
switch文
switch (式) {
case 定数式1:
実行文1;
break;
case 定数式2:
実行文2;
break;
default:
実行文n;
}
switch文ではある式の結果によって三つ以上の場合分けを行う事ができる。
式の結果を caseラベルで示される定数式と比較して、一致するものがあれば、
対応する実行文が実行される。
caseラベルは
と書き、定数式の後ろはコロン「:」である事に注意。
また、各実行文の後ろに break;をつけ忘れると、
プログラムの続きに書かれている文も実行されてしまう事にも注意が必要。
どの定数式にも一致しなかった場合は、defaultラベルがあれば
対応する実行文が実行される。
次のプログラムを実行し、switch 文の振る舞いについて確認せよ。
/* switch.c */
/* switch 文の確認 */
#include <stdio.h>
int main(int argc, char *argv[])
{
switch (argc) {
case 1:
printf("引数がありません\n");
break;
case 4:
printf(".");
/* 下に抜ける */
case 3:
printf(".");
/* 下に抜ける */
case 2:
printf(".");
printf("引数は %dつです\n", argc - 1);
break;
default:
printf("引数はたくさんです。\n");
break;
}
return 0;
}
ある変数に入っている値を「文字」と考えたときに、それが何であるかを 調べるために、いちいち ASCII 表をみるのは、煩雑であり面倒である。 そのため、文字種を判定するための関数が C 言語には用意されている。
たとえば、isdigit という関数は、引数が(10進数で言うところの)数字であれば真を、そうでなければ偽を返す。
したがって次の2つのコードは同じ意味となる。( int 型の変数 i に確かめたい文字が代入されているものとする)
if (i >= 0x30 && i <= 0x39) {
printf("'%c' is digit\n", i);
}
if (isdigit(i)) {
printf("'%c' is digit\n", i);
}
同じ結果になるとは言え、プログラムの可読性には大きく差があることが判る。
このような文字種判定関数は次のものが用意されている。使用する場合は ctype.h を #include しておくこと。
なお引数の型はすべて int であるが、値としては EOF もしくは符合無 char 型で表現できるものでなければならない。
| 関数名 | 真となる引数 | 備考 |
|---|---|---|
| isalnum | 英字もしくは数字 | (isalpha(c) || isdigit(c)) と等価 |
| isalpha | アルファベット | 標準の C ロケールでは (isupper(c)||islower(c))と等価。それ以外では、大文字でも小文字でもない他の文字でも真になる場合がある |
| isascii | ASCII 文字 | |
| isblank | 空白文字(スペースもしくはタブ) | |
| iscntrl | 制御文字 | |
| isdigit | 数字 | 10進数での数字、すなわち (0〜9) |
| isgraph | 表示可能文字 | スペースは含まない |
| islower | 小文字 | 標準 C ロケールであれば (a〜z) |
| isprint | 表示可能文字 | スペースを含む |
| ispunct | 記号 | 表示可能文字から英数字とスペースを除いた物 |
| isspace | 空白文字 | 標準 C ロケールならば、スペース、フォームフィード('\f')、改行('\n')、復帰('\r')、 水平タブ('\t')、垂直タブ('\v') |
| isupper | 大文字 | 標準 C ロケールであれば (A〜Z) |
| isxdigit | 数字 | 16進数での数字、すなわち (0〜9, A〜F, a〜f) |
次の関数が何をしているかを調べ、mycat2.cにこの関数を組み合わせたプログラムを作成せよ。
/* rot.c */
int rot(char *ch, int shift)
{
char base = 0;
if (isupper((int)*ch)) {
base = 'A';
} else if (islower((int)*ch)) {
base = 'a';
}
if (base > 0) {
*ch = base + ((*ch - base) + shift) % 26;
return 0;
}
return 1;
}
出来上がったプログラムに次の内容のファイル(input.txt)を引数に与えて、何を意味するものか調べよ。
Pbatenghyngvba, lbh tbg vg! V jvfu lbh n zreel puevfgznf naq n unccl arj lrne
他にも5日目の問題5 (calendar の仕上げ)、 9日目の問題10 についても、解答プログラムが 完成している事も確認する事。
パーツについてかなり見てきたので、初日に動かした chat4.c が徐々に読めるようになってきているはずである...
| 9日目 | 表紙 | 11日目 |