おさえておきたいプログラミングの基本
コンピュータのプログラムの処理には、順次処理(じゅんじしょり)と分岐処理(ぶんきしょり)があることはすでに述べました。ここでは、もうひとつの処理、繰り返し処理について説明します。 コンピュータのプログラムのプログラムの処理のことを、一般にアルゴリズムと言いますが、アルゴリズムは、この3つの処理によって記述されています。
C言語では、繰り返し処理を記述するためにfor(フォー)文、while(ホワイル)文、do~while(ドゥ・ホワイル文)という3つの文が用意されています。ここではそれらについて説明します。
では、まず手始めに繰り返し処理の最も基本的な処理である、for(フォー)文について学んでいくことにしましょう。
for文は、{}で囲まれた処理を、指定した条件が満たされるまで繰り返す処理です。 繰り返し処理をループ処理ということから、for文による繰り返しを、forループとも呼びます。forループは、C言語のプログラムの中で最もよく使われる処理の一つです。しっかりと覚えておきましょう。まずは、以下のサンプルを実行してみてください。
list4-1:main.c#include <stdio.h>
int main(int argc, char** argv) {
int i;
for(i = 1;i <= 5;i++){
printf("%d ",i);
}
printf("\n");
return 0;
}
実行結果
プログラムの結果をみると、for()文の{}に囲まれた部分が5回実行されたことが分かります。しかもiが1から5に1つづつ増加していたことはわかります。ではなぜこのような結果になるのでしょう。
それを知る前に、まずはfor文の書式をみてみましょう。
for文の書式これをlist4-1にあてはめてみましょう。
まず「i = 1」としていますから、最初はiの値は1から始まるわけです。
条件式はif文で用いられるものと同じもので、この場合「i <= 5」ですから、iが5以下の場合この処理は継続されるわけです。
では、増分処理には「i++」と書いてありますが、これは一体なんでしょうか?これは、インクリメントと言って、iの値を1増加させる処理なのです。
以上より、このfor文は、i=1から始めて、iを一つずつ増分させていき、iが5以下ならば{}内の処理を実行することを繰り返し、iが5より大きくなれば、ループから抜けるという処理になるのです。(図4-1)
図4-1.forループの仕組みfor文では、このインクリメントおよび、デクリメントという処理をよく行います。デクリメントとは、インクリメントの反対で、変数の値を1減らす処理です。この処理を表にまとめると、以下のようになります。(表4-1)
表4-1:インクリメント・デクリメントの処理一覧演算子 | 呼び名 | 意味 | 該当する演算 |
---|---|---|---|
i++ | インクリメント(後置:こうち) | 変数の値を1増加させる。 | i=i+1; i+=1; |
++i | インクリメント(前置:ぜんち) | 変数の値を1増加させる。 | |
i-- | デクリメント(後置:こうち) | 変数の値を1減少させる。 | i=i-1; i-=1; |
--i | デクリメント(前置:ぜんち) | 変数の値を1減少させる。 |
これをみると、i++と++i、i--と--iはそれぞれまったく同じ意味であるように見えます。しかし、このように前置(ぜんち)と後置(こうち)があるのには、それぞれ意味があるのです。例えば、
後置と代入の処理を同時に行うといった処理を行うと、a,iの値は次のようになります。
後置と代入の処理を同時に行った結果なぜこのような処理になるかというと、この処理は以下のような処理と同等だからです。
後置と代入の処理を同時に行った場合の処理の内容つまり、aにiの値を代入した後にiに1を足すのです。
同様に前置でも同じ処理をしてみましょう。
前置と代入の処理を同時に行うといった処理を行うと、a,iの値は次のようになります。
前置と代入の処理を同時に行った結果なぜこのような処理になるかというと、この処理は以下のような処理と同等だからです。
前置と代入の処理を同時に行った場合の処理の内容これは後置が代入の後に加算を行うのに対し、前置が代入の後に加算を行うということによる違いに基づくものです。デクリメントに関しても同様です。
このような処理はかつてコンピュータの処理速度がさほど速くなかったり、メモリなどが十分になかったころ、リソースを少しでも節約するために用いられた手法です。しかし濫用するとプログラムがわかりにくくなるという欠点があります。さらに現在のコンピュータは処理スピードも早く、メモリも十分に搭載していることから、こういった処理をプログラム中で書くことはほとんどなくなりました。
これで、基本的なfor文の使い方がわかったことと思います。次に、これを用いて色々な記述方法を見てみましょう。list4-1の5行目を、表4-2のように、様々な値に変えてみましょう。
表4-2:for文の記述方法の例記述方法 | 実行結果 | 説明 | |
---|---|---|---|
① | for(i = 0 ; i < 5 ; i++) | 0 1 2 3 4 | 変数の値を1増加させる。5になるとループを抜ける |
② | for(i = -2 ; i <= 2 ; i++) | -2 -1 0 1 2 | -2から2まで、値を1つずつ増加させる |
③ | for(i = 0 ; i < 10 ; i+=2) | 0 2 4 8 | 変数の値を2づつ増加させる。10になるとループを抜ける |
③ | for(i = 5 ; i >= 1 ; i--) | 5 4 3 2 1 | 5から1まで、値を1つずつ減少させる |
⑤ | for(i = 2 ; i >= -2 ; i--) | 2 1 0 -1 -2 | 2から-2まで、値を1つずつ減少させる |
⑥ | for(i = 12 ; i > 0 ; i-=3) | 12 9 6 3 | 変数の値を3づつ減少させる。0になると、ループを抜ける |
この例では、intの場合をとりあげましたが、その他のデータ型でもforループを利用することは可能です。
多重ループとは、繰り返し処理(ループ)構造の中に別の繰り返し構造が含まれているものです。ループの入れ子(ネスト)構造とも呼ばれます。多重ループはネストの数に応じて、二重ループ、三重ループ、・・・と呼ばれます。
多重ループは、行列の処理や2次元配列の操作など、複雑なデータ処理に活用されます。そのため、一番使用頻度が高いのが2重ループです。
実際にforの二重ループ、もしくはforのネストと呼ばれる処理を作ってみましょう。この処理はforループの中に更にforループを記述する二重の処理で、よく用いられるテクニックです。
list4-2:main.c#include <stdio.h>
// forの二重ループ
int main(int argc, char** argv) {
int i,j;
for(i = 1; i<= 2; i++){
for(j = 1; j <= 3; j++){
printf("%d+%d=%d ",i,j,i+j); // 繰り返される処理
}
printf("\n");
return 0;
}
}
実行結果
このプログラムではその側の変数iのループ(2回)が、内側の変数jのループ(3回)を繰り返しています。そのため2回×3回で、6回の表示処理のループが実現しています。(表4-3)
表4-3:i,jの関係性と表示内容回数 | i | j | 表示内容 |
---|---|---|---|
① | 1 | 1 | 1+1=2 |
② | 2 | 1+2=3 | |
③ | 3 | 1+3=4 | |
④ | 2 | 1 | 2+1=3 |
⑤ | 2 | 2+2=4 | |
⑥ | 3 | 2+3=5 |
iが1の時、jのループで最初のループで①~②の内容が表示され、ループが終了すると改行(10行目)され、iが2になり、再びjのループが繰り返され、③~⑥の内容が表示されます。
ループ処理を行うのは、for文だけではありません。while(ホワイル)文を用いることによっても実現可能です。まずは、以下のサンプルを見てください。
list4-3:main.c#include <stdio.h>
// iを用いたループ
int main(int argc, char** argv) {
int i = 0;
while(i <= 5){
printf("%d ",i);
i++;
}
printf("\n");
return 0;
}
実行結果
プログラムについて解説する前に、まずはwhile文の書式を見てみましょう。
whileの書式while文は、()内の条件が成り立つ間は、{}内に記述されている処理を繰り返します。
つまりlist4-3では、i<=5という条件が成り立っている間は中の処理が繰り返されることを意味します。()内で記述する条件式の記述方法は、if文の場合と同様のものです。
5行目で、iを0で初期化していますから、この段階でwhile文の条件である、「i<=5」は偽ですから、ループ処理に入ります。ループ内で、iの値を表示すると共に、i++を行うことによって、iの値が増加しています。そのため、i=6となると、i<=5という命題は正しくない、つまり「偽」となります。ループから出ます。(図4-2)(表4-4)
図4-2.whileループの仕組みi | 条件式 | 真/偽 |
---|---|---|
1 | 1<=5 | 真 |
2 | 2<=5 | |
3 | 3<=5 | |
4 | 4<=5 | |
5 | 5<=5 | |
6 | 6<=5 | 偽 |
見てわかるとおり、このサンプルは、list4-1と同等の処理です。しかし、for()文と違うのは、インクリメント・デクリメントといった処理や、初期値を設定する処理がループ内に存在しないことです。
次に、ループ処理の3つ目である、do~while(ドゥ・ホワイル)文について説明しましょう。まずは、次のサンプルを入力してください。
list4-4:main.c#include <stdio.h>
// iを用いたループ
int main(int argc, char** argv) {
int i = 0;
do{
printf("%d ",i);
i++;
}while(i <= 5);
printf("\n");
return 0;
}
実行結果
実行結果は、list4-1,4-3と変わりません。
do~while文は一度{}内の処理を実行した後にwhileの後の条件を確認し、その後処理を繰り返すかを判断します。(図4-3)
図4-3.do~whileループの仕組みここで出てくる、do~while文の書式は次の通りです。
do~whileの書式基本的にはdo~while文は、条件式の判定が後ろについているだけで、while文と似た働きをします。ただし、こちらは、whileの後に;(セミコロン)がついていますので、注意してください。
ここまでくると、「いったい、whileとdo~whileはどう違うのか?」という疑問をもたれる方もいることでしょう。実は、この二つには大きな違いがあるのです。まずは、以下のサンプルを実行してみてください。
list4-5:main.c#include <stdio.h>
// iを用いたループ
int main(int argc, char** argv) {
int i,num;
printf("回数を入力:");
scanf("%d",&num); // キーボードからループの回数を入力
// whileループで実行
printf("whileで実行\n");
i = 1;
while(i <= num){
printf("*");
i++;
}
printf("\n");
// do~whileループで実行
printf("do~whileで実行\n");
i = 1;
do{
printf("*");
i++;
}while(i <= num);
printf("\n");
return 0;
}
このプログラムは、コンソールから数字を入力し、その数だけ*を表示するプログラムです。そのため、入力した値により、実行結果は異なります。まず、5を入力するとwhile、do~while共に星が5つ出ます。
実行結果1(5を入力した場合)こういった場合は、while,do~while共に同じ結果になります。しかしここで0を代入すると、以下のようになります。
実行結果2(0を入力した場合)whileの方は、何も表示されていません。しかし、do~whileの方は、1つだけ表示されています。
これは、whileの場合、iの値はもともと条件を満たしていないので、{}内の処理が実行されないのに対し、 do~whileの場合、まず{}の処理を実行してから条件判定をしているため、仮に条件式を満たしていなくても最低限1度は処理が実行されることによるものです。
最後に、ループの流れを変えるbreak(ブレイク)とcontinue(コンティニュー)の使い方を説明します。
まずは、ループを途中から抜けるbreakから説明しましょう。次のサンプルを実行してみてください。
list4-6:main.c#include <stdio.h>
// breakでループを抜ける
int main(int argc, char** argv) {
int num;
printf("負の値で、ループから抜けます。¥n");
while(1){
printf("数値を入力:");
scanf("%d",&num);
if(num < 0){
// ループから抜ける処理
break;
}
}
printf("終了\n");
return 0;
}
実行結果
プログラムを実行すると、「数値を入力:」と言う文字が出力され、コンソールからの数値の入力待ちになります。ここに様々な数値を入力していくことになります。ここに正の数を入れる限り、同じことが繰り返されます。しかし、負の数を入れると、「終了」と表示されて、プログラムが終了します。
while(1)とすると{}内の処理は無限に繰り返されます。これは、ifやwhileの条件式は、偽の場合は0、真の場合はそれ以外の値をとるからです。
while(1)とすることにより、()内の条件がつねに成り立っているのと同じ状態になり、処理が無限に繰り返されます。
ただこういったループでも、breakがあるとループから出ることができます。このサンプルでは、入力した値が負の値の場合、12行目のbreakでループから抜けることにより処理が終了します。
一般に、際限なく繰り返されるループの事を無限ループと言います。
無限ループは、forでも、do~whileでもできます。いずれも、breakで出ることができます。ただ、無限ループにはこのサンプルのように故意に行うものと、意図せずにできるものがあります。
故意に用いる場合は、このプログラムのように、必要な箇所にbreakを入れて、ループから出ることができるようになります。 しかし、そうでない場合は、強制的にプログラムを終了させる必要がある場合もあります。
continueはループの冒頭部分に戻る処理です。次のサンプルはcontinueを使ったサンプルです。
list4-7:main.c#include <stdio.h>
// continueでループの先頭に戻る
int main(int argc, char** argv) {
int i;
for(i = 1;i <= 5;i++){
// iが2の時にループの先頭に戻る
if(i == 2){
continue;
}
printf("%d ",i);
}
printf("\n");
return 0;
}
これはlist4-1のforループに8~10行目の処理を追加したものです。
実行結果からわかる通り、「2」が表示されていません。これはiが2のときに9行目のcontinueでループの先頭に処理が戻るからです。
以上で、順次処理、分岐処理、繰り返し処理という、アルゴリズムの三大要素を記述する方法について説明しました。しかし、これだけではより複雑な実用プログラムを作るにはまだ不十分です。そこで、次からは、より複雑なプログラムを作るために必要な知識を身につけていくことにしましょう。
練習問題 : 問題4.
一週間でわかるC言語・C++言語がオンライン講座になりました!動画と音声によってさらにわかりやすくなりました!! 1講座で2つの言語を学ぶことができる上に、練習問題の回答もダウンロードできます。
Read →本講座が「1週間でC言語の基礎が学べる本」として書籍化されました!サイトの内容プラスアルファでより学習しやすくなっています!Impressより発売中です!!
Read →