C LANGUAGE TECHNOLOGY

【C言語】sizeof演算子の使い方

悩んでいる人

C言語でsizeof演算子の使い方を教えて!

こういった悩みにお答えします.

本記事の信頼性

  • リアルタイムシステムの研究歴12年.
  • 東大教員の時に,英語でOSの授業.
  • 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校コンピュータサイエンス学部2021年の世界大学学術ランキングで20位)で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発
  • プログラミング歴15年以上,習得している言語: C/C++,Java,Python,Ruby,HTML/CSS/JS/PHP,MATLAB,Assembler (x64,ARM).
  • 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」GitHubにオープンソースとして公開

こういった私から学べます.

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演算子でデータ型のサイズを計算するコードは以下になります.

私の環境での実行結果は以下になります.

あなたの環境でも確認してみて下さい.

sizeof演算子で変数のサイズの計算

sizeof演算子でデータ型の変数のサイズを計算するコードは以下になります.

実行結果は以下になります.

データ型を指定した場合と同じ結果になることがわかります.

sizeof演算子でポインタのサイズの計算

sizeof演算子でデータ型のポインタのサイズの計算するコードは以下になります.

実行結果は以下になります.

64ビットOS(Linux)で実行しているので,データ型のサイズとは異なり,全て8バイト(64ビット)になることに注意して下さい.

sizeof演算子で配列のサイズの計算

sizeof演算子で配列のサイズを計算するコードは以下になります.

実行結果は以下になります.

配列aはint型(4バイト)で要素数が3なので4 * 3 = 12バイト,配列dはdouble型(8バイト)で要素数が5なので8 * 5 = 40バイトになります.

sizeof演算子で配列の要素数の計算

sizeof演算子は,配列の要素数の計算する時に有用です.

sizeof演算子で配列の要素数を計算するコードは以下になります.

実行結果は以下になります.

9行目のNR_ELEMENTSが,7行目のint型の配列arrayのサイズを7と正しく計算しています.

sizeof演算子で構造体のサイズの計算

sizeof演算子で構造体のサイズを計算するコードは以下になります.

struct data構造体はchar型,int型,double型のメンバを持ちます.

struct data2構造体は順番は違いますが,int型,double型,char型のメンバを持ちます.

struct aligned_data構造体はstruct data2構造体と同じ順番で定義の最後に「__attribute__((aligned(64)))」と記載します(64バイトでアラインメント).

ここで,アラインメントで指定できる値は,2の乗数(2,4,8,16,32,64,...)のみであることに注意して下さい.

main関数では,構造体や構造体の変数,構造体のメンバのアドレスやサイズを表示します.

実行結果は以下になります.

アドレスは実行毎に変わるので,相対アドレスに注目して下さい.

最初に,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演算子で構造体のビットフィールドのサイズを計算するコードは以下になります.

実行結果は以下になります.

構造体のビットフィールドで指定した場合,ビットフィールドのビット数ではなく型サイズ分の大きさ(char型は1バイト,int型は4バイト)があることがわかります(3~4行目).

ただし,struct data3/struct data4構造体のように,複数のビットフィールドが大きい方の型サイズ(この場合はint型)に入る場合,4バイトになることがわかります(5~6行目).

1ビットでも型サイズを超えた場合,struct data5構造体のように型サイズ分の大きさが加算されて8バイトになります(7行目).

また,ビットフィールドを指定しない場合,struct data6構造体のようにアラインメントにより8バイトになります(8行目).

まとめ

C言語でsizeof演算子の使い方を紹介しました.

具体的には,sizeof演算子で,データ型,変数,ポインタ,配列のサイズや要素数,構造体のサイズを計算しました.

あなたにとって予想外の実行結果になったものがありましたか.

sizeof演算子を正しく理解していないとバグの原因になりますので,使う時は注意して下さい.

C言語を独学で習得することは難しいです.

私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.

友だち追加

独学が難しいあなたは,C言語を学べるおすすめのオンラインプログラミングスクール3社で自分に合うスクールを見つけましょう.

-C LANGUAGE, TECHNOLOGY
-, , , , , , , , , , ,