このページは、練習問題の解答例や解説のページです。
次のように変数が宣言されているとき、以下の 1~5 の式の値はそれぞれどうなりますか?
int iarray[] = {3, 5, 10};
char carray[] = "Hello";
まず、iarray と carray
の要素数を確認しておきましょう。要素数を明示的に指定していないので、初期化子から判断されます(本編解説)。iarray は初期化子が
3
、5
、10
の3つなので、要素数は 3
です。carray のほうは "Hello"
ですが、これは要素数 6
の文字列リテラルなので 6
になります。文字列リテラルには隠された終端文字があることを忘れてはなりません(本編解説)。
1番の iarray[0]
では、int型の配列の要素1つの大きさが得られます。つまり、sizeof(int)
と同じことであり、int型が 4バイトの処理系であれば 4 になります。
2番では、配列そのものを指定しており、この場合、配列全体の大きさが得られます。配列の要素は隙間なく連続的に並びますから、iarray
は int型
3個分の大きさだけを持ち、それより大きくなることはありません。したがって、sizeof(int) * 3
と同等なので、int型が 4バイトなら、12
になります。
3番は、2番の結果を1番の結果で割っているだけなので、それぞれが 12
と 4 なのであれば、3 になります。この
sizeof(配列) / sizeof(配列[0])
は、配列に含まれている要素数を調べる式です(本編解説)。
4番は carray の大きさなので、sizeof(char) * 6
です。sizeof(char)
は必ず 1 ですから(「整数型」のページを参照)、結果は
6 になります。文字列の末尾には終端文字があることを意識しましょう。
5番の std::string関数は、文字列の長さを取得します(本編解説)。これには終端文字の分が含まれないため、結果は 5 になります。この例では見た目どおりの文字数が返ってきたようにみえますが、1文字が 2バイト以上になる文字が含まれている場合は、文字数と同じにはならないことに注意してください。
生の配列の要素がメモリ上でどのように配置されているか確認してください。
&演算子を使って、各要素のメモリアドレスを出力してみましょう。
#include <iostream>
#define SIZE_OF_ARRAY(array) (sizeof(array) / sizeof(array[0]))
int main()
{
int array[5] {};
for (size_t i = 0; i < SIZE_OF_ARRAY(array); ++i) {
std::cout << &array[i] << "\n";
}
}
実行結果:
00AFFB54
00AFFB58
00AFFB5C
00AFFB60
00AFFB64
再三繰り返しているように、配列の要素は、メモリ上で隙間なく連続的に並びます。実行結果はその様子を表しています。隙間が
4 ずつなのは、sizeof(int)
が、この処理系では 4
だからです。
標準入力から整数を5つ入力させ、生の配列に格納したあと、入力された順番とは逆の順番で出力するプログラムを作成してください。
たとえば次のようになります。
#include <iostream>
#include <iterator>
int main()
{
int array[5] {};
for (int& e : array) {
std::cout << "Please enter the integer.\n";
std::cin >> e;
}
for (auto it = std::crbegin(array); it != std::crend(array); ++it) {
std::cout << *it << "\n";
}
}
実行結果:
Please enter the integer.
2 <-- 入力した整数
Please enter the integer.
3 <-- 入力した整数
Please enter the integer.
5 <-- 入力した整数
Please enter the integer.
8 <-- 入力した整数
Please enter the integer.
12 <-- 入力した整数
12
8
5
3
2
範囲外アクセスによる未定義動作はとても危険なので避けなければなりませんが、その可能性がないか考えることはとても頭を悩ませるものです(その挙句に失敗しやすくもあります)。ここまでのページのアドバイス通り、可能なかぎり、添字を使うよりもイテレータを、イテレータをそのまま使うよりも範囲for文を選びましょう。また、標準ライブラリ関数が使えるならそうするべきです。
ここでは、配列に要素を格納していく部分では範囲for文を、逆順にアクセスする部分では逆イテレータ(「イテレータ」のページを参照)を使いました。
int型の配列に、ある指定した値を持つ要素が含まれているかどうかを調べるプログラムを以下の3通りの方法で作成してください。
まず、添字演算子を使う方法です。
#include <iostream>
#define SIZE_OF_ARRAY(array) (sizeof(array) / sizeof(array[0]))
int main()
{
int array[] {1, 7, 2, 6, 4, 8, 1, 4};
constexpr int target {8}; // 探す値
bool found {false};
for (size_t i = 0; i < SIZE_OF_ARRAY(array); ++i) {
if (array[i] == target) {
= true;
found break;
}
}
if (found) {
std::cout << "found.\n";
}
else {
std::cout << "not found.\n";
}
}
実行結果:
found.
範囲for文は使う場合は、次のようになります。添字演算子よりも、こちらの方が簡潔ですし、要素数や添字に関するミスが起こり得なくなります。
#include <iostream>
int main()
{
int array[] {1, 7, 2, 6, 4, 8, 1, 4};
constexpr int target {8}; // 探す値
bool found {false};
for (int e : array) {
if (e == target) {
= true;
found break;
}
}
if (found) {
std::cout << "found.\n";
}
else {
std::cout << "not found.\n";
}
}
実行結果:
found.
<algorithm> にある標準ライブラリ関数を使う場合、std::find関数が適しています(「要素を探索する」のページを参照)。この方法は、コードの意味がより明確になる利点があります。
#include <algorithm>
#include <iostream>
int main()
{
int array[] {1, 7, 2, 6, 4, 8, 1, 4};
constexpr int target {8}; // 探す値
if (std::find(std::cbegin(array), std::cend(array), target) == std::cend(array)) {
std::cout << "not found.\n";
}
else {
std::cout << "found.\n";
}
}
実行結果:
found.
はてなブックマーク に保存 | Pocket に保存 | Facebook でシェア |
X で ポスト/フォロー | LINE で送る | noteで書く |
RSS | 管理者情報 | プライバシーポリシー |