問題① 要素数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
を符号無し整数型にしなければならない事情があったとしたら(size_t型を使うなど)、i >= 0
という終了条件ではうまくいきません。ループの最後の段階で、0
からデクリメントすることになるため、巨大な正の数に戻ってきてしまうからですが、そもそも符号無し整数型の値が
0未満になることは絶対にないのですから、i >= 0
という条件がおかしいのは明らかです。
このような場合には、いったん int型にキャストすることも1つの方法ではありますが、int型で表現できる上限値以下の値にしかならないことを保証しなければならず、結局は危険性があります。
あるいは、次のように変形する方法もあります。
// 逆順で出力
for (unsigned 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” の直後に文字列の末尾が表れたことになります。文字列の終端が ‘\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 is duplicated.\n", values[i]);
printfreturn 0;
}
}
}
("There is no duplicate value.");
puts}
実行結果:
27 is duplicated.
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 is included in both.\n", values1[i]);
printfreturn 0;
}
}
}
("There is no value included in both");
puts}
実行結果:
-5 is included in both.
今度は、変数i と変数j は、別の配列をアクセスする添字になっています。今回は、別の配列の要素と比較するので、外側のループの制御変数i も、配列の末尾の要素まで進めないといけません。
()
の前後の空白の空け方)(
の直後、)
の直前に空白を入れない)return 0;
を削除(C言語編全体でのコードの統一)第30章から練習問題⑤⑥を移動してきて、練習問題④⑤とした。
全面的に文章を見直し、修正を行った。
新規作成。
Programming Place Plus のトップページへ
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
RSS | 管理者情報 | プライバシーポリシー |