C言語でsizeof演算子の使い方を教えて!
こういった悩みにお答えします.
本記事の信頼性
- リアルタイムシステムの研究歴12年.
- 東大教員の時に,英語でOS(Linuxカーネル)の授業.
- 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校(UNC)コンピュータサイエンス学部で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
- プログラミング歴15年以上,習得している言語: C/C++,Python,Solidity/Vyper,Java,Ruby,Go,Rust,D,HTML/CSS/JS/PHP,MATLAB,Assembler (x64,ARM).
- 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」をGitHubにオープンソースとして公開.
- 2020年1月~現在はアメリカのノースカロライナ州チャペルヒルにあるGuarantee Happiness LLCのCTOとしてECサイト開発やWeb/SNSマーケティングの業務.2022年6月~現在はアメリカのノースカロライナ州チャペルヒルにあるJapanese Tar Heel, Inc.のCEO兼CTO.
- 最近は自然言語処理AIとイーサリアムに関する有益な情報発信に従事.
- (AI全般を含む)自然言語処理AIの論文の日本語訳や,AIチャットボット(ChatGPT,Auto-GPT,Gemini(旧Bard)など)の記事を50本以上執筆.アメリカのサンフランシスコ(広義のシリコンバレー)の会社でプロンプトエンジニア・マネージャー・Quality Assurance(QA)の業務委託の経験あり.
- (スマートコントラクトのプログラミングを含む)イーサリアムや仮想通貨全般の記事を200本以上執筆.イギリスのロンドンの会社で仮想通貨の英語の記事を日本語に翻訳する業務委託の経験あり.
こういった私から学べます.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!
目次
sizeof演算子とは
sizeof演算子とは,データ型,変数,ポインタ,構造体,配列等のサイズ(大きさ)をバイト単位で計算する演算子です.
ANSI規格ではchar型のサイズが1バイトと定義されています.
ほとんどの処理系では1バイトは8ビットですが,正確にはchar型のビット幅はlimits.h内の定数マクロCHAR_BITで定義されています.
Stack OverflowにあるWhat platforms have something other than 8-bit char?の記事によると,以下のDigital Signal Processor(DSP)のchar型のサイズは16ビット(2バイト)とのことです.
それでは,sizeof演算子を利用するコードを見ていきましょう.
sizeof演算子でデータ型のサイズの計算
sizeof演算子でデータ型のサイズを計算するコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> int main(void) { printf("sizeof(char) = %zu\n", sizeof(char)); printf("sizeof(short) = %zu\n", sizeof(short)); printf("sizeof(int) = %zu\n", sizeof(int)); printf("sizeof(long) = %zu\n", sizeof(long)); printf("sizeof(long long) = %zu\n", sizeof(long long)); printf("sizeof(float) = %zu\n", sizeof(float)); printf("sizeof(double) = %zu\n", sizeof(double)); printf("sizeof(long double) = %zu\n", sizeof(long double)); return 0; } |
私の環境での実行結果は以下になります.
あなたの環境でも確認してみて下さい.
1 2 3 4 5 6 7 8 9 10 |
$ gcc sizeof.c $ a.out sizeof(char) = 1 sizeof(short) = 2 sizeof(int) = 4 sizeof(long) = 8 sizeof(long long) = 8 sizeof(float) = 4 sizeof(double) = 8 sizeof(long double) = 16 |
sizeof演算子で変数のサイズの計算
sizeof演算子でデータ型の変数のサイズを計算するコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> int main(void) { char c; short s; int i; long l; long long ll; float f; double d; long double ld; printf("sizeof(c) = %zu\n", sizeof(c)); printf("sizeof(s) = %zu\n", sizeof(s)); printf("sizeof(i) = %zu\n", sizeof(i)); printf("sizeof(l) = %zu\n", sizeof(l)); printf("sizeof(ll) = %zu\n", sizeof(ll)); printf("sizeof(f) = %zu\n", sizeof(f)); printf("sizeof(d) = %zu\n", sizeof(d)); printf("sizeof(ld) = %zu\n", sizeof(ld)); return 0; } |
実行結果は以下になります.
データ型を指定した場合と同じ結果になることがわかります.
1 2 3 4 5 6 7 8 9 10 |
$ gcc sizeof_variables.c $ a.out sizeof(c) = 1 sizeof(s) = 2 sizeof(i) = 4 sizeof(l) = 8 sizeof(ll) = 8 sizeof(f) = 4 sizeof(d) = 8 sizeof(ld) = 16 |
sizeof演算子でポインタのサイズの計算
sizeof演算子でデータ型のポインタのサイズの計算するコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> int main(void) { printf("sizeof(char *) = %zu\n", sizeof(char *)); printf("sizeof(short *) = %zu\n", sizeof(short *)); printf("sizeof(int *) = %zu\n", sizeof(int *)); printf("sizeof(long *) = %zu\n", sizeof(long *)); printf("sizeof(long long *) = %zu\n", sizeof(long long *)); printf("sizeof(float *) = %zu\n", sizeof(float *)); printf("sizeof(double *) = %zu\n", sizeof(double *)); printf("sizeof(long double *) = %zu\n", sizeof(long double *)); return 0; } |
実行結果は以下になります.
64ビットOS(Linux)で実行しているので,データ型のサイズとは異なり,全て8バイト(64ビット)になることに注意して下さい.
1 2 3 4 5 6 7 8 9 10 |
$ gcc sizeof_pointer.c $ a.out sizeof(char *) = 8 sizeof(short *) = 8 sizeof(int *) = 8 sizeof(long *) = 8 sizeof(long long *) = 8 sizeof(float *) = 8 sizeof(double *) = 8 sizeof(long double *) = 8 |
sizeof演算子で配列のサイズの計算
sizeof演算子で配列のサイズを計算するコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> int main(void) { int a[3]; double d[5]; printf("sizeof(a) = %zu\n", sizeof(a)); printf("sizeof(a[0]) = %zu\n", sizeof(a[0])); printf("sizeof(d) = %zu\n", sizeof(d)); printf("sizeof(d[0]) = %zu\n", sizeof(d[0])); return 0; } |
実行結果は以下になります.
配列aはint型(4バイト)で要素数が3なので4 * 3 = 12バイト,配列dはdouble型(8バイト)で要素数が5なので8 * 5 = 40バイトになります.
1 2 3 4 5 6 |
$ gcc sizeof_array.c $ a.out sizeof(a) = 12 sizeof(a[0]) = 4 sizeof(d) = 40 sizeof(d[0]) = 8 |
sizeof演算子で配列の要素数の計算
sizeof演算子は,配列の要素数の計算する時に有用です.
sizeof演算子で配列の要素数を計算するコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> int array[] = {11, -100, 33, 22, 567, -44, 77}; #define NR_ELEMENTS (sizeof(array) / sizeof(array[0])) int main(void) { printf("NR_ELEMENTS = %lu\n", NR_ELEMENTS); return 0; } |
実行結果は以下になります.
9行目のNR_ELEMENTSが,7行目のint型の配列arrayのサイズを7と正しく計算しています.
1 2 3 |
$ gcc sizeof_array_nr_elements.c $ a.out NR_ELEMENTS = 7 |
sizeof演算子で構造体のサイズの計算
sizeof演算子で構造体のサイズを計算するコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> struct data { char c; int i; double d; }; struct data2 { int i; double d; char c; }; struct aligned_data { int i; double d; char c; } __attribute__((aligned(64))); int main(void) { struct data d; struct data2 d2; struct aligned_data ad; printf("sizeof(struct data) = %zu\n", sizeof(struct data)); printf("sizeof(d) = %zu\n", sizeof(d)); printf("&d.c = %p: sizeof(d.c) = %zu\n", &d.c, sizeof(d.c)); printf("&d.i = %p: sizeof(d.i) = %zu\n", &d.i, sizeof(d.i)); printf("&d.d = %p: sizeof(d.d) = %zu\n", &d.d, sizeof(d.d)); printf("\n"); printf("sizeof(struct data2) = %zu\n", sizeof(struct data2)); printf("sizeof(d2) = %zu\n", sizeof(d2)); printf("&d2.i = %p: sizeof(d2.i) = %zu\n", &d2.i, sizeof(d2.i)); printf("&d2.d = %p: sizeof(d2.d) = %zu\n", &d2.d, sizeof(d2.d)); printf("&d2.c = %p: sizeof(d2.c) = %zu\n", &d2.c, sizeof(d2.c)); printf("\n"); printf("sizeof(struct aligned_data) = %zu\n", sizeof(struct aligned_data)); printf("sizeof(ad) = %zu\n", sizeof(ad)); printf("&ad.i = %p: sizeof(ad.i) = %zu\n", &ad.i, sizeof(ad.i)); printf("&ad.d = %p: sizeof(ad.d) = %zu\n", &ad.d, sizeof(ad.d)); printf("&ad.c = %p: sizeof(ad.c) = %zu\n", &ad.c, sizeof(ad.c)); return 0; } |
struct data構造体はchar型,int型,double型のメンバを持ちます.
struct data2構造体は順番は違いますが,int型,double型,char型のメンバを持ちます.
struct aligned_data構造体はstruct data2構造体と同じ順番で定義の最後に「__attribute__((aligned(64)))」と記載します(GCC/Clangの拡張機能で64バイトでアラインメント).
ここで,アラインメントで指定できる値は,2の乗数(2,4,8,16,32,64,...)のみであることに注意して下さい.
main関数では,構造体や構造体の変数,構造体のメンバのアドレスやサイズを表示します.
実行結果は以下になります.
アドレスは実行毎に変わるので,相対アドレスに注目して下さい.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ gcc sizeof_struct.c $ a.out sizeof(struct data) = 16 sizeof(d) = 16 &d.c = 0x7ffca02ada10: sizeof(d.c) = 1 &d.i = 0x7ffca02ada14: sizeof(d.i) = 4 &d.d = 0x7ffca02ada18: sizeof(d.d) = 8 sizeof(struct data2) = 24 sizeof(d2) = 24 &d2.i = 0x7ffca02ada20: sizeof(d2.i) = 4 &d2.d = 0x7ffca02ada28: sizeof(d2.d) = 8 &d2.c = 0x7ffca02ada30: sizeof(d2.c) = 1 sizeof(struct aligned_data) = 64 sizeof(ad) = 64 &ad.i = 0x7ffca02ada40: sizeof(ad.i) = 4 &ad.d = 0x7ffca02ada48: sizeof(ad.d) = 8 &ad.c = 0x7ffca02ada50: sizeof(ad.c) = 1 |
最初に,struct data構造体の実行結果を見てましょう(3~7行目).
struct data構造体と構造体の変数のサイズは16バイトになります(3~4行目).
しかし,各々のメンバのサイズは1バイト(char型),4バイト(int型),8バイト(double型)で合計すると13バイトなので計算が合わないですよね(5~7行目).
この理由は,メモリ上のアドレスを調整するデータ構造アラインメントが行われるからです.
int型で4バイトのアラインメント,double型で(x64の場合は)8バイトのアラインメントが行われます.
つまり,d.iのアドレスはd.cから4バイトのオフセット,d.dのアドレスはd.cから8バイトのオフセットがあるので,struct data構造体のサイズは合計16バイトになります.
次に,struct data2構造体の実行結果を見てみましょう(9~13行目).
struct data2構造体のメンバの順番は,d.i,d.d,d.cになっています.
d.dのアドレスはd.iから8バイトのオフセット,d.cのアドレスはd.iから16バイトのオフセット,64ビットOSは8バイトのアラインメントなので,struct data2構造体のサイズは合計24バイトになります.
メンバの順番が違うだけで構造体のサイズが変わるのは驚きですよね.
最後に,struct aligned_data構造体の実行結果を見てみましょう(15~19行目).
struct aligned_data構造体は,struct data2構造体とメンバの順番が同じで,オフセットやアラインメントももちろん同じです.
しかし,struct data2構造体とは異なり,struct aligined_data構造体と構造体の変数のサイズは合計64バイトになっています(15~16行目).
この理由は,「__attribute__((aligned(64)))」でアラインメントを64バイトに設定したからです.
「__attribute__((aligned(64)))」は,特定のキャッシュラインに構造体のみを載せたい場合に使うGCC拡張の機能です(Visual Studioでは利用できません.).
※Intel CPUのキャッシュラインのサイズは64バイトが多いです.
他の変数が同じキャッシュラインに入ってしまうと,キャッシュのヒット率に影響してしまい,性能が落ちる原因になるからです.
しかし,アラインメントの分だけメモリサイズが大きくなります.
なので,性能とメモリサイズのトレードオフを考慮してアラインメントを利用しましょう.
sizeof演算子で構造体のビットフィールドのサイズの計算
sizeof演算子で構造体のビットフィールドのサイズを計算するコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> struct data { char c: 2; }; struct data2 { int i: 3; }; struct data3 { char c: 2; int i: 3; }; struct data4 { char c: 2; int i: 30; }; struct data5 { char c: 2; int i: 31; }; struct data6 { char c; int i; }; int main(void) { printf("sizeof(data) = %zu\n", sizeof(struct data)); printf("sizeof(data2) = %zu\n", sizeof(struct data2)); printf("sizeof(data3) = %zu\n", sizeof(struct data3)); printf("sizeof(data4) = %zu\n", sizeof(struct data4)); printf("sizeof(data5) = %zu\n", sizeof(struct data5)); printf("sizeof(data6) = %zu\n", sizeof(struct data6)); return 0; } |
実行結果は以下になります.
構造体のビットフィールドで指定した場合,ビットフィールドのビット数ではなく型サイズ分の大きさ(char型は1バイト,int型は4バイト)があることがわかります(3~4行目).
ただし,struct data3/struct data4構造体のように,複数のビットフィールドが大きい方の型サイズ(この場合はint型)に入る場合,4バイトになることがわかります(5~6行目).
1ビットでも型サイズを超えた場合,struct data5構造体のように型サイズ分の大きさが加算されて8バイトになります(7行目).
また,ビットフィールドを指定しない場合,struct data6構造体のようにアラインメントにより8バイトになります(8行目).
1 2 3 4 5 6 7 8 |
$ gcc sizeof_struct_bit_field.c $ a.out sizeof(data) = 1 sizeof(data2) = 4 sizeof(data3) = 4 sizeof(data4) = 4 sizeof(data5) = 8 sizeof(data6) = 8 |
まとめ
C言語でsizeof演算子の使い方を紹介しました.
具体的には,sizeof演算子で,データ型,変数,ポインタ,配列のサイズや要素数,構造体のサイズを計算しました.
あなたにとって予想外の実行結果になったものがありましたか.
sizeof演算子を正しく理解していないとバグの原因になりますので,使う時は注意して下さい.
sizeof演算子と似ている_Alignof演算子の使い方を知りたいあなたはこちらからどうぞ.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!