問題① 要素数10 の int型配列に、2 から始まる 2 のべき乗を順番に格納し、それを逆の順番で表示するプログラムを作成してください。
たとえば次のようになります。
#include <stdio.h>
#define ARRAY_SIZE 10
int main(void)
{
int array[ARRAY_SIZE];
int num = 2;
// 2 から始まる 2 のべき乗を格納
for( int i = 0; i < ARRAY_SIZE; ++i ){
[i] = num;
array*= 2;
num }
// 逆順で出力
for( int i = ARRAY_SIZE - 1; i >= 0; --i ){
( "%d\n", array[i] );
printf}
}
実行結果:
1024
512
256
128
64
32
16
8
4
2
配列の先頭から末尾へ向かう for文と、末尾から先頭に向かう for文は、確実に書けるようになっておく必要があります。特に後者の終了条件式には注意が必要です。
たまに、ループ回数の制御を、符号無し整数型の変数で行う必要に迫られることがありますが、そういう場合だと「i >= 0」という終了条件ではダメです。 0 から 1 を引くと、突然巨大な正の数に戻ってきてしまいます。そもそも、符号無し整数型の値が 0未満になることは絶対にないですから、このような条件式は明らかに間違っています。
この場合は、いったん int型にキャストすることも1つの方法ですが、その場合は、int型で表現できる程度の数しか扱わないことを確認する必要があります。あるいは、次のように変形する必要があります。
// 逆順で出力
for( int i = ARRAY_SIZE; i > 0; --i ){
( "%d\n", array[i-1] );
printf}
これは、常に変数i には 1 大きい値を入れるようにして、ループの内部で -1 して使っています。
問題② 次のように文字型の配列を定義します。
char str[] = "abcdef";
この文字列を 1文字ずつ改行しながら出力するプログラムを作成してください。
たとえば次のようになります。
#include <stdio.h>
int main(void)
{
char str[] = "abcdef";
for( int i = 0; str[i] != '\0'; ++i ){
( "%c\n", str[i] );
printf}
}
実行結果:
a
b
c
d
e
f
文字列の終端には ‘\0’ があるので、これを使って for文を終了させるようにします。
‘\0’ は整数でいえば、ただの 0 です。 0 は偽であることから、次のように書くこともできます。
for( int i = 0; str[i]; ++i ){
}
このように短く簡潔に書くことを好む人も多いので、こういうプログラムも読めた方が良いでしょう。
なお、文字列が文字型の「配列」であるという観点からすれば、配列の要素数を使う終了判定を行うこともできます。
= sizeof(str) / sizeof(str[0]);
size for( size_t i = 0; i < size; ++i ){
}
問題③ 問題②と同じ内容で、文字型の配列の初期値を次のように変えた場合、どうなるでしょう?
char str[] = "abc\0def";
問題②と同じプログラムを使って試してみます。
#include <stdio.h>
int main(void)
{
char str[] = "abc\0def";
for( int i = 0; str[i] != '\0'; ++i ){
( "%c\n", str[i] );
printf}
}
実行結果:
a
b
c
‘\0’ という文字は、文字列の終端を表します。 意図したにせよ、意図していないにせよ、“abc\0def” のように書いた場合、“abc” の時点で文字列の末尾が表れたことになります。
この文字列は、全体としてみれば “abc\0def\0” と書くのと同じ結果ですが、‘\0’ のところで終端であると期待したプログラムは、 途中の ‘\0’ が終端であると誤認します。
この現象は、puts関数などに渡して試すとすぐに確認できます。
#include <stdio.h>
int main(void)
{
char str1[] = "abcdef";
char str2[] = "abc\0def";
( str1 );
puts( str2 );
puts}
実行結果:
abcdef
abc
問題④ 次のような配列があります。
#define ARRAY_SIZE 5
int values[ARRAY_SIZE] = { 13, 27, 75, 27, 48 };
この配列の中に、同じ値が重複して含まれているかどうかを調べるプログラムを作成してください。
配列内の2つの値を比較する必要があります。手元のトランプの束から、同じ数字のカードを探すときのように、まず1つの値に注目し、他のカードから同じ値を探し出すように考えます。
#include <stdio.h>
int main(void)
{
#define ARRAY_SIZE 5
int values[ARRAY_SIZE] = { 13, 27, 75, 27, 48 };
for( int i = 0; i < ARRAY_SIZE-1; ++i ){
for( int j = i + 1; j < ARRAY_SIZE; ++j ){
if( values[i] == values[j] ){
( "%d が重複して含まれています。\n", values[i] );
printfreturn 0;
}
}
}
( "重複している値はありません。" );
puts}
実行結果:
27 が重複して含まれています。
for文で二重ループを作っています。内側のループの方が速く回り、外側のループが遅く回るので、前述したように、1つの値に注目した状態のまま、他の値を順番に調べるようなプログラムになっています。
外側のループの終了条件が少し不思議かもしれませんが、常に 1つ先の要素とセットで処理を行うので、変数i の方は、末尾の要素の1つ手前で止めます。そうしないと、変数j の方は i + 1 で始まるため、配列の末尾を超えたところをアクセスしてしまいます。
問題⑤ 次のような配列があります。
#define ARRAY_SIZE 5
int values1[ARRAY_SIZE] = { -17, 8, 29, -5, 13 };
int values2[ARRAY_SIZE] = { 64, -5, 17, -22, -38 };
2つの配列の両方に同じ値が含まれているかどうかを調べるプログラムを作成してください。
問題⑤ができれば、こちらはその変形でできます。
#include <stdio.h>
int main(void)
{
#define ARRAY_SIZE 5
int values1[ARRAY_SIZE] = { -17, 8, 29, -5, 13 };
int values2[ARRAY_SIZE] = { 64, -5, 17, -22, -38 };
for( int i = 0; i < ARRAY_SIZE; ++i ){
for( int j = 0; j < ARRAY_SIZE; ++j ){
if( values1[i] == values2[j] ){
( "%d が両方に含まれています。\n", values1[i] );
printfreturn 0;
}
}
}
( "両方に含まれている値はありません。" );
puts}
実行結果:
-5 が両方に含まれています。
今度は、変数i と変数j は、別の配列をアクセスする添字になっています。 今回は、別の配列の要素と比較するので、外側のループの制御変数i も、配列の末尾の要素まで進めないといけません。
return 0;
を削除(C言語編全体でのコードの統一)’2018/6/4 第30章から練習問題⑤⑥を移動してきて、練習問題④⑤とした。
’2018/2/28 全面的に文章を見直し、修正を行った。
’2009/8/21 新規作成。
Programming Place Plus のトップページへ