【C++】sizeofとは?|使い方・配列とポインタの違いを初心者向けに解説

サムネイル画像 C++

今回は、C++での「sizeofとは何か」について解説していきます。

「sizeofって何を返しているの?」
「配列に使うと便利って聞いたけど、どう便利なの?」
「ポインタに使うと結果が変わるのはなぜ?」

このような疑問を持ったことはありませんか?

この記事を読み終えると、あなたはsizeofの基本的な意味から、配列とポインタの違いまでしっかり理解できるようになると思いますので、ぜひ最後まで読んでいただけると嬉しいです。

sizeofとは?

sizeofとは、型や変数がメモリ上で何バイト使うかを調べるための演算子です。

sizeof(int)のように型に対して使うこともできますし、sizeof(value)のように変数に対して使うこともできます。

特にC++では、配列の要素数を求めたい時や、構造体のサイズを確認したい時によく使います。ゲーム制作やツール制作でも登場回数はかなり多いです。

なぜsizeofが必要?

sizeofが便利なのは、サイズを手で数えなくてよくなるからです。

  • 配列に何個要素が入っているか調べたい
  • 構造体がどれくらいメモリを使うか確認したい
  • バッファサイズを安全に扱いたい

こういった場面で数字をベタ書きすると、あとで要素数を変えた時にズレやすいです。sizeofを使えば、コードの変更にも強くなります。

使い方の基本

まずは一番基本的な使い方を見てみましょう。#include <> の書き方が気になる方は、#includeの””と<>の違いを解説した記事も参考になります。

▼main.cpp

#include <iostream>

int main() {
    char letter = 'A';
    char title[] = "abc";

    std::cout << "sizeof(char): " << sizeof(char) << "\n";
    std::cout << "sizeof(letter): " << sizeof(letter) << "\n";
    std::cout << "sizeof(title): " << sizeof(title) << "\n";

    return 0;
}

実行結果

sizeof(char): 1
sizeof(letter): 1
sizeof(title): 4

charは1バイトです。titleは”abc”なので3文字ですが、文字列の最後には終端文字 \0 が入るため、結果は4になります。

このようにsizeofは、見た目の文字数ではなく、実際に確保されているサイズを返します。

配列での使い方

sizeofが特に便利なのが配列です。配列の要素数は、次のように求められます。

sizeof(配列全体) / sizeof(配列の1要素)

配列の初期化が不安な方は、変数の初期化 =0 と {0} の違いもあわせて読むと理解しやすいです。

▼main.cpp

#include <iostream>
#include <cstddef>

int main() {
    int damageList[] = {10, 20, 30, 40, 50};

    std::size_t count = sizeof(damageList) / sizeof(damageList[0]);

    std::cout << "要素数: " << count << "\n";

    return 0;
}

実行結果

要素数: 5

要素を5個書いているので、結果も5になります。こうしておけば、あとで6個や7個に増やしてもコードを直す箇所が減るので便利です。

【重要】私が実際にsizeofで困った体験談

私も自主制作のゲームで、sizeofの使い方を勘違いしてハマったことがあります。

敵の出現データを配列で管理していて、「今いくつの敵データがあるか」を関数の中で調べようとしていました。そこで関数の引数に配列を渡し、その中でsizeof(data) / sizeof(data[0])と書いたのですが、想定した数ではなく、64bit環境で2のようなおかしな値になってしまったんです。

原因は、関数に渡した時点で配列がポインタとして扱われるからでした。つまり、配列全体のサイズではなく、ポインタ自身のサイズを取ってしまっていたわけです。

この時に、「sizeofは便利だけど、配列なのかポインタなのかを意識しないと危ない」とかなり実感しました。以降は、配列の要素数は呼び出し側で求めて渡すか、std::arraystd::vectorを使うようにしています。

sizeof使用時のよくあるエラーと対処法

  • ポインタにsizeofしてしまう
    配列のサイズを取りたいつもりでも、ポインタならポインタ自身のサイズしか取れません。配列のまま使うか、要素数を別で渡しましょう。
  • std::stringの文字数をsizeofで調べてしまう
    sizeof(std::string)は文字列の長さではありません。文字数を知りたいならname.size()を使います。
  • size_tとintを混ぜてしまう
    sizeofの結果は基本的にsize_tです。ループや比較で警告が出る時は、std::size_tでそろえると安全です。

実践例

では、先ほどの失敗を避けた書き方を見てみましょう。関数を分けて書く時は、プロトタイプ宣言の記事もあわせて読むと理解しやすいです。

▼main.cpp

#include <iostream>
#include <cstddef>

void PrintCount(const int* data, std::size_t count);

int main() {
    int enemyIds[] = {101, 102, 103, 104, 105};

    std::size_t count = sizeof(enemyIds) / sizeof(enemyIds[0]);
    PrintCount(enemyIds, count);

    return 0;
}

void PrintCount(const int* data, std::size_t count) {
    std::cout << "要素数: " << count;
}

実行結果

要素数: 5

このように、配列の要素数は配列であるうちに求めて、関数には値として渡すのが安全です。

注意点

  • intやポインタのサイズは、環境によって結果が変わることがあります
  • 文字列リテラルを配列で持つ場合、終端文字 \0 もサイズに含まれます
  • sizeofは便利ですが、「配列か、ポインタか、stringか」を見分けることが大切です

まとめ

  • sizeofは、型や変数のバイト数を調べる演算子
  • 配列の要素数を求める時に特に便利
  • 関数に渡した配列はポインタとして扱われるので注意
  • std::stringの文字数を知りたい時はsize()を使う
  • sizeofの戻り値は基本的にsize_t

sizeofを正しく使えるようになると、配列やメモリサイズまわりの理解がかなり深まります。

最初は「なんとなく使う」でも大丈夫ですが、配列とポインタの違いだけは意識しておくと、後でかなり助かります。

ここまで読んでくださり、ありがとうございました。

関連記事