【C言語入門】sizeof演算子の使い方(配列の要素数、構造体のサイズ)

みなさんは、sizeof演算子をご存知でしょうか?

他の言語ではほとんどの場合、配列の要素数を求めるためのマクロやメソッドが用意されています。

ですが、残念ながらC言語にはありません。C言語ではsizeof演算子を使って、配列の要素数を求めます。sizeof演算子はその他にも、構造体のサイズやポインタのサイズを取得するために使われます。

この記事では、sizeof演算子について

  • sizeof演算子とは
  • 配列の要素数を取得する
  • ポインタのサイズを取得する

という基本的な内容から、関数に配列を渡す時の注意点や、文字列長と配列サイズの使い分けといった応用的な内容についても解説していきます。

目次

sizeof演算子とは

sizeof演算子とは、変数や型のメモリサイズを調べるための演算子です。sizeof演算子は、変数や型のメモリサイズをバイト単位で返してくれます。メモリサイズとはコンピュータが使用するメモリの大きさのことです。

配列の要素数を取得する

配列の要素数は

  • sizeof演算子を用いて配列全体のメモリサイズを求める
  • 全体のメモリサイズを配列の要素一つ分のメモリサイズで割る

という2ステップで簡単に求められます。サンプルコードは以下の通りです。

#include <stdio.h>
 
int main(int argc, char *argv[])
{
  //この配列(array)の要素数(10)を求める
  int array[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
 
  //配列の要素数  = 配列の要素全体の大きさ /  配列の要素一つ分の大きさ
  int arrayNumber = sizeof array / sizeof array[0];
 
  printf("配列全体のメモリサイズ: %ld\n", sizeof array); //配列全体のメモリサイズ
  printf("配列の要素一つ分のメモリサイズ: %ld\n", sizeof array[0]); //配列の要素一つ分の大きさ
  printf("配列の要素数: %d\n", arrayNumber); //配列の要素数 (10)
 
  return 0;
}

実行結果:

配列全体のメモリサイズ: 40
配列の要素一つ分のメモリサイズ: 4
配列の要素数: 10

sizeof演算子がバイト単位でメモリサイズを求めてくれるので、

配列の要素数(arrayNumber) =
配列の要素全体の大きさ(sizeof array) / 配列の要素一つ分の大きさ(sizeof array[0])

という簡単な割り算の式で配列の要素数を求めることができます。

ポインタのサイズを取得する

sizeof演算子を使ってポインタのバイト数を取得する場合は注意が必要です。サンプルコードでみていきましょう。

#include <stdio.h>
 
int main(void) {
    char *str = "Hello World!";
    
    printf("ポインタstrのバイト数: %ld", sizeof(str));
    
    return 0;
}

実行結果:

ポインタstrのバイト数: 8

このサンプルコードでは、文字列のポインタstrのバイト数をsizeof演算子を使って取得しています。

ポインタstrには文字列リテラル”Hello World!”が格納されていますので、文字列のバイト数の12が出力されると勘違いしがちですが、ポインタのバイト数は8と出力表示されています。

sizeof演算子で取得した値はあくまでポインタのバイト数で、格納されているデータのバイト数ではないので注意しましょう!

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

ここまでで、sizeof演算子を使って、配列の要素数を求める方法はわかりました。ですが、ここで一つ注意して欲しいことがあります。それは配列がポインタの値に置き換えられていた場合です。

C言語では、配列を別の関数に引数として渡す場合、処理を軽くするために配列の中身をそのまま渡すのではなくて、配列の先頭のポインタの値を渡します。

引数として別の関数で用いられた場合、sizeof演算子で計算できるのはポインタのメモリサイズであって、元の配列のメモリサイズではありません。つまり、別の関数内では要素数を求めることができなくなってしまうのです。

なので、配列の値を別の関数に渡す場合は上記の方法を使って、あらかじめ配列の要素の数を求めておき、配列の値と同時に配列の要素数も引数として渡します。では、比べる方法をサンプルコードで確認してみましょう。

#include <stdio.h>
 
 
//別の関数を定義する
void anotherFunction (int *array2)
{
  //配列の要素数  = 配列の要素全体の大きさ /  配列の要素一つ分の大きさ
  int arrayNumber2 = sizeof array2 / sizeof array2[0];
 
  //別関数内での配列全体のメモリサイズ
  printf("別関数内での配列全体のメモリサイズ: %ld\n", sizeof array2);
  //別関数内での配列の要素一つ分の大きさ
  printf("別関数内での配列の要素一つ分のメモリサイズ: %ld\n", sizeof array2[0]);
  //別関数内での配列の要素数
  printf("別関数内での配列の要素数: %d\n",arrayNumber2);
 
  return;
}
 
int main()
{
  //この配列(array)の要素数(10)を求める
  int array[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
 
  //配列の要素数  = 配列の要素全体の大きさ /  配列の要素一つ分の大きさ
  int arrayNumber = sizeof array / sizeof array[0];
 
  //配列全体のメモリサイズ
  printf("配列全体のメモリサイズ: %ld\n", sizeof array);
  //配列の要素一つ分の大きさ
  printf("配列の要素一つ分のメモリサイズ: %ld\n", sizeof array[0]);
  //配列の要素数
  printf("配列の要素数: %d\n\n",arrayNumber);
 
  //別の関数に配列の値を渡して、ボインタに変換された配列の値を出力する
  anotherFunction(array);
 
  return 0;
}

実行結果:

配列全体のメモリサイズ: 40
配列の要素一つ分のメモリサイズ: 4
配列の要素数: 10
 
別関数内での配列全体のメモリサイズ: 8
別関数内での配列の要素一つ分のメモリサイズ: 4
別関数内での配列の要素数: 2

このように、本来配列の要素数は10ですが、同様の方法で配列の要素数を求めると別の関数内では2になってしまいます。なので、別の関数で正しく配列の要素数を求めるためには、あらかじめ求めた配列の要素数を引数として渡してあげる必要があります。

配列の要素数を引数として渡す方法をサンプルコードで確認してみましょう。

#include <stdio.h>
 
 
//引数に配列の要素数を含んだ別の関数を定義する
void anotherFunction (int *array2, int arrayNumber)
{
  //要素の説明を出力する
  printf("%s\n", "別関数での配列の要素数");
  
  //別関数内での配列の要素数
  printf("%d\n", arrayNumber);  
 
  return;
}
 
int main()
{
  //この配列(array)の要素数(10)を求める
  int array[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
 
  //配列の要素数  = 配列の要素全体の大きさ /  配列の要素一つ分の大きさ
  int arrayNumber = sizeof array / sizeof array[0];
 
  //要素の説明を出力する
  printf("%s\n", "配列の要素数");
  //配列の要素数
  printf("%d\n",arrayNumber);  
 
  //別の関数に配列の値を渡して、ボインタに変換された配列の値を出力する
  anotherFunction(array,arrayNumber);
 
  return 0;
}

実行結果:

配列の要素数
10
別関数での配列の要素数
10

引数に配列の要素数の値をそのまま渡したので、同じ値になります。これで、別の関数に渡した後でも配列の要素数を問題なく扱えます。

文字列長と配列サイズの使い分け

文字配列を宣言・定義し、その文字列長を取得する場合はsizeof演算子を使って配列サイズを求める場合と使い分ける必要があります。

文字列長を取得する方法として、strlen関数を使う方法があります。strlen関数を使うには「string.h」をインクルードする必要があります。strlen関数とsizeof演算子を使った場合とサンプルコードで比較してみましょう。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <locale.h>

int main(void) {
    char str[] = "Hello";
    
    printf("strの文字列長: %ld\n", strlen(str));
    printf("strのバイト数: %ld\n", sizeof(str));
    
    // マルチバイト文字列のバイト数
    char *str2 = "こんにちは";
    printf("str2の文字列長: %ld\n", strlen(str2));
    
    // 参考:マルチバイト文字列の文字数を取得する方法
    int i = 0, len = 0, num = 0;
    setlocale(LC_CTYPE, "");
    while(str2[i] != '\0') {
        len = mblen(&str2[i], MB_CUR_MAX);
        i += len;
        num++;
    }
    printf("str2の文字数: %d\n", num);
    
    return 0;
}
strの文字列長: 5
strのバイト数: 6
str2の文字列長: 15
str2の文字数: 5

このサンプルコードでは、文字列strの文字列長をstrlen関数で、バイト数をsizeof演算子で取得しています。sizeof演算子で取得した結果は1文字分多くなっています。

C言語では文字列の終端に”\0″が付けられていて、それまで含めたバイト数をsizeof演算子では取得するためです。なお、日本語のようなマルチバイトの文字列は文字列長と文字数が異なりますので、注意しましょう。

参考までにmblen関数を使ったマルチバイト文字列の文字数を取得する方法も記述しています。mblen関数を使うには、setlocale関数でロケールを設定する必要があります。

mblen関数を使うには「stdlib.h」をインクルードする必要があり、setlocale関数を使うには「locale.h」をインクルードする必要があります。

サンプルコードではLinux環境でコンパイル、実行しています。他のコンパイラーなど環境が異なる場合は、エラーが発生するかもしれませんので、ご注意ください。

配列の使い方総まとめ

この記事では紹介しきれなかった配列のいろいろな使い方を次の記事にまとめているので、ぜひ確認してください!

まとめ

いかがでしたか。この記事では、sizeof演算子を使って配列の要素数を求める方法を解説しました。実際のプログラミングでは、たとえばfor文の繰り返し回数を決めるのに配列の要素数は使われます。

また、配列を扱う上では、必ず配列の要素数を必要とする場面に遭遇します。C言語はC++やJavaなど、数多くの言語に影響を与えた言語です。C言語を学べば、他の言語を学ぶ基礎が身につくこと間違いなしでしょう。

この記事を書いた人

Unityを使ったiOSアプリのリリース、フリマサイト運営の経験があります。

経験した言語はC、C#、Javascript、R、Python、Ruby、PHPなど

言語が好きで、英語や中国、ドイツ語を勉強しました。
将来的には海外で生活したいです。

現在はRuby on Rails5やCocos2dxの勉強を主にしています。

ライターとしては
できるだけ初心者にわかりやすい文章になるように心がけています。

趣味は語学、読書です。

目次