MENU

C++ 配列のサイズを求めるときに sizeof 演算子をポインタに適用しない

C++プログラミングにおいて、配列のサイズを正確に求めることは非常に重要です。しかし、初心者プログラマーがよく陥る落とし穴の一つに、配列のサイズを求める際にsizeof演算子をポインタに適用してしまうことがあります。この記事では、なぜそれが問題なのか、そして正しい方法は何かを詳しく解説します。

目次

なぜ sizeof 演算子をポインタに適用してはいけないのか

配列のサイズを求める際にsizeof演算子をポインタに適用すると、予期せぬ結果が得られます。これは、配列がポインタにデグレードする(降格する)という C++ の特性に関連しています。

以下の例を見てみましょう:

#include <iostream>

void printArraySize(int* arr) {
    // 警告: これは配列のサイズを正しく計算しません
    std::cout << "配列のサイズ(間違った方法): " << sizeof(arr) / sizeof(arr[0]) << std::endl;
}

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    
    std::cout << "配列のサイズ(正しい方法): " << sizeof(numbers) / sizeof(numbers[0]) << std::endl;
    
    printArraySize(numbers);
    
    return 0;
}

このコードを実行すると、以下のような出力が得られます(64ビットシステムの場合):

配列のサイズ(正しい方法): 5
配列のサイズ(間違った方法): 2

なぜこのような結果になるのでしょうか?

  1. main()関数内でsizeof(numbers)を使用した場合、正しく配列全体のサイズ(5 * sizeof(int))が返されます。
  2. しかし、printArraySize()関数内では、arrは単なるポインタです。配列が関数に渡されると、ポインタにデグレードしてしまうのです。
  3. ポインタのsizeofは、ポインタ自体のサイズを返します(通常、32ビットシステムで4バイト、64ビットシステムで8バイト)。
  4. そのため、sizeof(arr) / sizeof(arr[0])は、ポインタのサイズをintのサイズで割った結果となり、配列の実際の要素数とは無関係な値になってしまいます。

正しい方法:配列のサイズを求める

配列のサイズを正確に求めるには、以下の方法があります:

  1. 配列が局所変数や静的配列の場合:
int numbers[] = {1, 2, 3, 4, 5};
size_t size = sizeof(numbers) / sizeof(numbers[0]);

2.テンプレートを使用する方法:

template<typename T, size_t N>
size_t arraySize(T (&)[N]) {
    return N;
}

// 使用例
int numbers[] = {1, 2, 3, 4, 5};
size_t size = arraySize(numbers);

3.C++17以降では、std::size()関数を使用できます:

#include <array>

int numbers[] = {1, 2, 3, 4, 5};
size_t size = std::size(numbers);

4.std::arrayを使用する(C++11以降):

#include <array>

std::array<int, 5> numbers = {1, 2, 3, 4, 5};
size_t size = numbers.size();

関数に配列を渡す際の注意点

配列を関数に渡す際に、サイズ情報が失われないようにする方法がいくつかあります:

  1. 配列のサイズを別の引数として渡す:
void processArray(int* arr, size_t size) {
    for (size_t i = 0; i < size; ++i) {
        // 処理
    }
}

// 使用例
int numbers[] = {1, 2, 3, 4, 5};
processArray(numbers, sizeof(numbers) / sizeof(numbers[0]));

2.参照を使用する:

template<size_t N>
void processArray(int (&arr)[N]) {
    for (size_t i = 0; i < N; ++i) {
        // 処理
    }
}

// 使用例
int numbers[] = {1, 2, 3, 4, 5};
processArray(numbers);

3.std::arrayまたはstd::vectorを使用する:

#include <array>
#include <vector>

void processArray(const std::array<int, 5>& arr) {
    for (size_t i = 0; i < arr.size(); ++i) {
        // 処理
    }
}

void processVector(const std::vector<int>& vec) {
    for (size_t i = 0; i < vec.size(); ++i) {
        // 処理
    }
}

// 使用例
std::array<int, 5> numbers = {1, 2, 3, 4, 5};
processArray(numbers);

std::vector<int> numberVector = {1, 2, 3, 4, 5};
processVector(numberVector);

まとめ

配列のサイズを求める際にsizeof演算子をポインタに適用しないことは、C++プログラミングにおいて重要な注意点です。これを避けることで、多くのバグや予期せぬ動作を防ぐことができます。

代わりに、適切な方法(sizeof演算子を配列自体に使用する、テンプレート関数を使用する、std::size()を使用する、std::arrayを使用するなど)を選択することで、正確に配列のサイズを求めることができます。

また、関数に配列を渡す際には、サイズ情報が失われないような設計を心がけることが重要です。これにより、より安全で保守性の高いコードを書くことができます。

C++の配列とポインタの関係を深く理解することで、このような落とし穴を避け、より効果的なプログラミングが可能になります。初心者の方は、これらの概念をしっかりと把握し、実践を通じて理解を深めていくことをお勧めします。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次