C言語基礎|size_t型とtypedef宣言

sizeof の正体、ちゃんと知ってる?―size_t と typedef を押さえると、型の不安がスッと消える。

size_t型とtypedef宣言って何のためにあるの?

C言語でサイズを調べるとき、みんな大好き sizeof が登場しますよね。
ところがここに、初学者がよくハマる“地味だけど重要”なポイントがあります。

  • sizeof が返す値は 符号無し整数
  • でも、その「具体的な型」が unsigned なのか unsigned long なのかなどは 処理系(環境)次第

つまり、A環境では unsigned、B環境では unsigned long…みたいに変わり得るんです。
これだと「sizeof の結果を受け取る変数は何型にすべき?」がブレますよね。

そこで登場するのが size_t です。
size_t は <stddef.h> などで、だいたい次のように typedef 宣言されています(例)

typedef unsigned long size_t;   /* 例:実際は環境によって変わる */

これにより、どの環境でも

  • sizeof が返す型は size_t

と言い切れるようになります。
この「環境依存の差を吸収して、共通の名前で呼べるようにする」役が typedef の大事なお仕事です。

まず押さえる:typedef は“新しい型”を作らない

typedef はよく「型を作る」と誤解されますが、やっているのは 別名(あだ名)を付けることだけです。

typedef の基本ルール

既存の型 A に、別名 B を付ける

typedef  A  B;

この結果、B は 型名として使えるようになります。
でも中身はあくまで A のままです。新種の型が増えたわけではありません。

size_t と typedef の役割

項目何をするもの?うれしいこと
sizeof型や変数の大きさ(バイト数)を得る何バイトかを安全に調べられる。
size_tsizeof の結果を表すための型名環境差を吸収して共通の書き方にできる。
typedef既存の型に別名を付ける宣言読みやすさUP/環境依存を隠せる。

この表のポイントはここです。
size_t は typedef で作られた“別名”で、sizeof の結果を受け取るための共通の呼び名。

size_t が必要になる理由を、もう一段わかりやすく

ありがちな悩み

「sizeof の結果って、unsigned で受け取ればよくない?」

→ それが よくない場合があるんです。
環境によって sizeof の型が違うので、受け取り側の型を固定するとズレる可能性があります。

例:環境が違うとこうなり得る

環境A:sizeof の型 = unsigned
環境B:sizeof の型 = unsigned long
環境C:sizeof の型 = unsigned long long

でも全部まとめて size_t という名前で呼べるようにする

つまり size_t は“橋渡し役”です。
「sizeof の型は環境によって違うけど、size_t って呼べばOK」にしてくれます。

書式(命令の書式)をきっちり:typedef の書き方

typedef 宣言の書式

  • 基本形:
    typedef 既存の型名 新しい別名;

例:

typedef unsigned long size_t;
typedef int Score;
typedef unsigned char u8;

何をする命令なの?

  • typedef は 型に別名を付ける宣言
  • 変数を作ったり、メモリ確保をしたりはしません
  • “読みやすさ”と“移植性(環境差への強さ)”を上げます

printf で size_t を出すときのルール(ここ大事)

size_t は unsigned 系のどれか、という立ち位置なので、printf で出すときは 専用の書式指定を使います。

  • size_t を表示する書式指定:%zu

(z は size_t 用の修飾子、u は unsigned 系、というイメージです)

サンプルプログラム

配列の要素数と全体サイズを確認するプログラムです。

プロジェクト名:chap7-9-1 ソースファイル名:chap7-9-1.c

#include <stdio.h>
#include <stddef.h>

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

    size_t total_bytes = sizeof(numbers);
    size_t one_bytes   = sizeof(numbers[0]);
    size_t count       = total_bytes / one_bytes;

    puts("配列のサイズを確認してみよう!");
    printf("配列全体のバイト数 = %zu\n", total_bytes);
    printf("要素1個のバイト数   = %zu\n", one_bytes);
    printf("要素数(計算結果)  = %zu\n", count);

    return 0;
}

このプログラムで使っている要素の説明

書き方意味結果の型
sizeof(numbers)配列全体のバイト数size_t
sizeof(numbers[0])要素1個のバイト数size_t
total_bytes / one_bytes要素数(割り算で求める)size_t(計算結果として)

図のイメージ

この図が言っているのは、

  • 配列は「同じ型の要素が並ぶ箱の列」
  • 全体サイズを1要素サイズで割ると要素数が出る
    という超定番テクです。

まとめ:今日のゴール

  • sizeof の結果は size_t 型
  • size_t は 環境依存の unsigned 系型に typedef で別名を付けたもの
  • typedef は 新しい型を作らず、別名を与える
  • size_t を printf するなら %zu