Code Fix

中級

sizeofが配列の要素数ではなくポインタのサイズを返す

配列に対するsizeofは配列全体のバイト数を返しますが、関数の引数として渡された配列はポインタに変わっているため、同じsizeofが8(ポインタのサイズ)を返してしまいます。

なぜエラーが出ないのか

出力: 2
(エラーなし)
Cは何も報告しません。コンパイルも実行も正常に完了するためです
出力: 2
実際の挙動 — 期待した結果と食い違っている箇所
見つけ方
エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます

このエラーが出る典型パターン

パターン1

 1  #include <stdio.h>
 2  
 3  int main(void) {
 4      int nums[5] = {1, 2, 3, 4, 5};
 5      int *p = nums;
 6      int count = sizeof(p    ) / sizeof(int);
                             ^
 7      printf("%d\n", count);
 8      return 0;
 9  }
出力: 2期待: 5

pはnumsの先頭要素を指すポインタで、sizeof(p)はポインタ自体のサイズ(8バイト)を返します。配列全体のバイト数が欲しいときは、配列そのものにsizeofを使う必要があります。

直し方: p を nums にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  #include <stdio.h>
 2  
 3  int main(void) {
 4      double prices[4] = {9.5, 3.2, 7.1, 4.4};
 5      double *p = prices;
 6      int count = sizeof(p      ) / sizeof(double);
                              ^
 7      printf("%d\n", count);
 8      return 0;
 9  }
出力: 1期待: 4

ポインタ変数のsizeofは指している型によらず常にポインタ自身のサイズです。要素数を得るには配列の変数名に直接sizeofを使います。

直し方: p を prices にします。

この問題を解いてみる →

パターン3

 1  #include <stdio.h>
 2  
 3  int main(void) {
 4      char letters[6] = {'a', 'b', 'c', 'd', 'e', 'f'};
 5      char *p = letters;
 6      int count = sizeof(p       ) / sizeof(char);
                               ^
 7      printf("%d\n", count);
 8      return 0;
 9  }
出力: 8期待: 6

char型の配列でも同じ罠があります。ポインタpのsizeofは要素の型と無関係に8(ポインタのサイズ)を返すため、要素数の計算には使えません。

直し方: p を letters にします。

この問題を解いてみる →

パターン4

 1  #include <stdio.h>
 2  
 3  int main(void) {
 4      long ids[3] = {100, 200, 300};
 5      long *p = ids;
 6      int count = sizeof(p   ) / sizeof(long);
                             ^
 7      printf("%d\n", count);
 8      return 0;
 9  }
出力: 1期待: 3

long型の配列でも同じです。ポインタのsizeofは要素の型に関係なく常にポインタ自身のサイズになります。

直し方: p を ids にします。

この問題を解いてみる →

パターン5

 1  #include <stdio.h>
 2  
 3  int main(void) {
 4      float weights[7] = {1, 2, 3, 4, 5, 6, 7};
 5      float *p = weights;
 6      int count = sizeof(p       ) / sizeof(float);
                               ^
 7      printf("%d\n", count);
 8      return 0;
 9  }
出力: 2期待: 7

要素数が増えても規則は同じです。要素数を得るには配列の変数名に直接sizeofを使う必要があります。

直し方: p を weights にします。

この問題を解いてみる →

よくある誤解

配列を関数に渡しても「配列そのもの」が渡されると思いがちですが、Cでは配列は関数の引数になった瞬間に先頭要素へのポインタに変換されます(配列のdecay)。要素数の情報は失われるため、関数内でsizeofから要素数を復元することはできません。

実務での勘所

配列の要素数を求める定石はsizeof(arr) / sizeof(arr[0])ですが、これが正しく働くのは配列がまだポインタに変換されていない、宣言されたスコープの中だけです。関数に渡した後の引数としてのarrに対して同じ式を書くと、ポインタのサイズを要素のサイズで割った無意味な値になります。この制約のため、Cでは配列を関数に渡すときは、要素数を表す引数を別途一緒に渡すのが標準的な作法です(void f(int *arr, size_t n)のように)。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)