sizeof演算子のまとめ
sizeof演算子について、いろいろ試してみました。アセンブリコードを見てみると、sizeofオペランドの式はコンパイル時に定数に置換されるか、sizeofの計算に影響を与えない数式の場合、式自体が翻訳時に捨てられているのが分かります。
00000000004004e3 <main>: int main(int argc, char *argv[]){ 4004e3: 55 push rbp 4004e4: 48 89 e5 mov rbp,rsp 4004e7: 48 83 ec 30 sub rsp,0x30 4004eb: 89 7d dc mov DWORD PTR [rbp-0x24],edi 4004ee: 48 89 75 d0 mov QWORD PTR [rbp-0x30],rsi char buf[]="ABCDE"; 4004f2: c7 45 f0 41 42 43 44 mov DWORD PTR [rbp-0x10],0x44434241 4004f9: 66 c7 45 f4 45 00 mov WORD PTR [rbp-0xc],0x45 int i=0; 4004ff: c7 45 f8 00 00 00 00 mov DWORD PTR [rbp-0x8],0x0 int n=0; 400506: c7 45 ec 00 00 00 00 mov DWORD PTR [rbp-0x14],0x0 char c; printf("sizeof(\"\") = %zu\n", sizeof("")); 40050d: b8 18 08 40 00 mov eax,0x400818 400512: be 01 00 00 00 mov esi,0x1 400517: 48 89 c7 mov rdi,rax 40051a: b8 00 00 00 00 mov eax,0x0 40051f: e8 94 fe ff ff call 4003b8 <printf@plt> printf("sizeof(\"ABCDE\") = %zu\n", sizeof("ABCDE")); 400524: b8 2a 08 40 00 mov eax,0x40082a 400529: be 06 00 00 00 mov esi,0x6 40052e: 48 89 c7 mov rdi,rax 400531: b8 00 00 00 00 mov eax,0x0 400536: e8 7d fe ff ff call 4003b8 <printf@plt> printf("sizeof(buf) = %zu\n", sizeof(buf)); 40053b: b8 41 08 40 00 mov eax,0x400841 400540: be 06 00 00 00 mov esi,0x6 400545: 48 89 c7 mov rdi,rax 400548: b8 00 00 00 00 mov eax,0x0 40054d: e8 66 fe ff ff call 4003b8 <printf@plt> printf("sizeof('A') = %zu\n", sizeof('A')); 400552: b8 54 08 40 00 mov eax,0x400854 400557: be 04 00 00 00 mov esi,0x4 40055c: 48 89 c7 mov rdi,rax 40055f: b8 00 00 00 00 mov eax,0x0 400564: e8 4f fe ff ff call 4003b8 <printf@plt> printf("sizeof(node) = %zu\n", sizeof(node)); 400569: b8 67 08 40 00 mov eax,0x400867 40056e: be 08 00 00 00 mov esi,0x8 400573: 48 89 c7 mov rdi,rax 400576: b8 00 00 00 00 mov eax,0x0 40057b: e8 38 fe ff ff call 4003b8 <printf@plt> printf("i = %d\n", i); 400580: b8 7b 08 40 00 mov eax,0x40087b 400585: 8b 55 f8 mov edx,DWORD PTR [rbp-0x8] 400588: 89 d6 mov esi,edx 40058a: 48 89 c7 mov rdi,rax 40058d: b8 00 00 00 00 mov eax,0x0 400592: e8 21 fe ff ff call 4003b8 <printf@plt> printf("sizeof(i++) = %zu\n", sizeof(i++)); 400597: b8 83 08 40 00 mov eax,0x400883 40059c: be 04 00 00 00 mov esi,0x4 4005a1: 48 89 c7 mov rdi,rax 4005a4: b8 00 00 00 00 mov eax,0x0 4005a9: e8 0a fe ff ff call 4003b8 <printf@plt> printf("i = %d\n", i); 4005ae: b8 7b 08 40 00 mov eax,0x40087b 4005b3: 8b 55 f8 mov edx,DWORD PTR [rbp-0x8] 4005b6: 89 d6 mov esi,edx 4005b8: 48 89 c7 mov rdi,rax 4005bb: b8 00 00 00 00 mov eax,0x0 4005c0: e8 f3 fd ff ff call 4003b8 <printf@plt> printf("n = %d\n", n); 4005c5: 8b 55 ec mov edx,DWORD PTR [rbp-0x14] 4005c8: b8 96 08 40 00 mov eax,0x400896 4005cd: 89 d6 mov esi,edx 4005cf: 48 89 c7 mov rdi,rax 4005d2: b8 00 00 00 00 mov eax,0x0 4005d7: e8 dc fd ff ff call 4003b8 <printf@plt> printf("sizeof(int [n++]) = %zu\n", sizeof(int [n++])); 4005dc: 8b 45 ec mov eax,DWORD PTR [rbp-0x14] 4005df: 89 c2 mov edx,eax 4005e1: 48 63 d2 movsxd rdx,edx 4005e4: 48 c1 e2 02 shl rdx,0x2 4005e8: 83 c0 01 add eax,0x1 4005eb: 89 45 ec mov DWORD PTR [rbp-0x14],eax 4005ee: b8 9e 08 40 00 mov eax,0x40089e 4005f3: 48 89 d6 mov rsi,rdx 4005f6: 48 89 c7 mov rdi,rax 4005f9: b8 00 00 00 00 mov eax,0x0 4005fe: e8 b5 fd ff ff call 4003b8 <printf@plt> printf("n = %d\n", n); 400603: 8b 55 ec mov edx,DWORD PTR [rbp-0x14] 400606: b8 96 08 40 00 mov eax,0x400896 40060b: 89 d6 mov esi,edx 40060d: 48 89 c7 mov rdi,rax 400610: b8 00 00 00 00 mov eax,0x0 400615: e8 9e fd ff ff call 4003b8 <printf@plt> printf("n = %d\n", n); 40061a: 8b 55 ec mov edx,DWORD PTR [rbp-0x14] 40061d: b8 96 08 40 00 mov eax,0x400896 400622: 89 d6 mov esi,edx 400624: 48 89 c7 mov rdi,rax 400627: b8 00 00 00 00 mov eax,0x0 40062c: e8 87 fd ff ff call 4003b8 <printf@plt> printf("sizeof(int [++n]) = %zu\n", sizeof(int [++n])); 400631: 8b 45 ec mov eax,DWORD PTR [rbp-0x14] 400634: 83 c0 01 add eax,0x1 400637: 89 45 ec mov DWORD PTR [rbp-0x14],eax 40063a: 8b 45 ec mov eax,DWORD PTR [rbp-0x14] 40063d: 48 98 cdqe 40063f: 48 8d 14 85 00 00 00 lea rdx,[rax*4+0x0] 400646: 00 400647: b8 b7 08 40 00 mov eax,0x4008b7 40064c: 48 89 d6 mov rsi,rdx 40064f: 48 89 c7 mov rdi,rax 400652: b8 00 00 00 00 mov eax,0x0 400657: e8 5c fd ff ff call 4003b8 <printf@plt> printf("n = %d\n", n); 40065c: 8b 55 ec mov edx,DWORD PTR [rbp-0x14] 40065f: b8 96 08 40 00 mov eax,0x400896 400664: 89 d6 mov esi,edx 400666: 48 89 c7 mov rdi,rax 400669: b8 00 00 00 00 mov eax,0x0 40066e: e8 45 fd ff ff call 4003b8 <printf@plt> printf("sizeof(c) = %zu\n", sizeof(c)); 400673: b8 d0 08 40 00 mov eax,0x4008d0 400678: be 01 00 00 00 mov esi,0x1 40067d: 48 89 c7 mov rdi,rax 400680: b8 00 00 00 00 mov eax,0x0 400685: e8 2e fd ff ff call 4003b8 <printf@plt> printf("sizeof(c+0) = %zu\n", sizeof(c+0)); 40068a: b8 e1 08 40 00 mov eax,0x4008e1 40068f: be 04 00 00 00 mov esi,0x4 400694: 48 89 c7 mov rdi,rax 400697: b8 00 00 00 00 mov eax,0x0 40069c: e8 17 fd ff ff call 4003b8 <printf@plt> /* printf("sizeof(ia) = %zu\n", sizeof(ia)); */ printf("sizeof(main) = %zu\n", sizeof(main)); 4006a1: b8 f4 08 40 00 mov eax,0x4008f4 4006a6: be 01 00 00 00 mov esi,0x1 4006ab: 48 89 c7 mov rdi,rax 4006ae: b8 00 00 00 00 mov eax,0x0 4006b3: e8 00 fd ff ff call 4003b8 <printf@plt> printf("sizeof(&main) = %zu\n", sizeof(&main)); 4006b8: b8 08 09 40 00 mov eax,0x400908 4006bd: be 08 00 00 00 mov esi,0x8 4006c2: 48 89 c7 mov rdi,rax 4006c5: b8 00 00 00 00 mov eax,0x0 4006ca: e8 e9 fc ff ff call 4003b8 <printf@plt> printf("n = %d\n", n); 4006cf: 8b 55 ec mov edx,DWORD PTR [rbp-0x14] 4006d2: b8 96 08 40 00 mov eax,0x400896 4006d7: 89 d6 mov esi,edx 4006d9: 48 89 c7 mov rdi,rax 4006dc: b8 00 00 00 00 mov eax,0x0 4006e1: e8 d2 fc ff ff call 4003b8 <printf@plt> printf("sizeof(func(&n)) = %zu\n", sizeof(func(&n))); 4006e6: b8 1d 09 40 00 mov eax,0x40091d 4006eb: be 04 00 00 00 mov esi,0x4 4006f0: 48 89 c7 mov rdi,rax 4006f3: b8 00 00 00 00 mov eax,0x0 4006f8: e8 bb fc ff ff call 4003b8 <printf@plt> printf("n = %d\n", n); 4006fd: 8b 55 ec mov edx,DWORD PTR [rbp-0x14] 400700: b8 96 08 40 00 mov eax,0x400896 400705: 89 d6 mov esi,edx 400707: 48 89 c7 mov rdi,rax 40070a: b8 00 00 00 00 mov eax,0x0 40070f: e8 a4 fc ff ff call 4003b8 <printf@plt> return 0; 400714: b8 00 00 00 00 mov eax,0x0 } 400719: c9 leave 40071a: c3 ret 40071b: 90 nop 40071c: 90 nop 40071d: 90 nop 40071e: 90 nop 40071f: 90 nop
結果
sizeof("") = 1 sizeof("ABCDE") = 6 sizeof(buf) = 6 sizeof('A') = 4 sizeof(node) = 8 i = 0 sizeof(i++) = 4 i = 0 n = 0 sizeof(int [n++]) = 0 n = 1 n = 1 sizeof(int [++n]) = 8 n = 2 sizeof(c) = 1 sizeof(c+0) = 4 sizeof(main) = 1 sizeof(&main) = 8 n = 2 sizeof(func(&n)) = 4 n = 2
文字列リテラル・char型配列
sizeof("") = 1 sizeof("ABCDE") = 6 sizeof(buf) = 6
文字列リテラルやchar型配列変数というと、式中ではchar型配列における先頭要素へのポインタに展開されますが、sizeof演算子のオペランドにおいては、char型配列として評価されます。末尾のナル文字を含めたサイズが返されます。なので空文字列は「1」。
文字定数
sizeof('A') = 4
sizeof(char)は「1」ですが、文字定数はint型なので「4」。
構造体
4 typedef struct node_t{ 5 unsigned char f; 6 int id; 7 }node_t, node;
sizeof(node) = 8
上記の場合、境界調整でパディングが入るため、5でなくて「8」です。
sizeofオペランドの式
i = 0 sizeof(i++) = 4 …(1) i = 0 n = 0 sizeof(int [n++]) = 0 …(2) n = 1 n = 1 sizeof(int [++n]) = 8 …(3) n = 2
C99以前の規格Cではsizeof演算子のオペランドには整数定数式を要求しますが、可変長配列が導入されたC99では、sizeofのオペランドに可変長配列が指定された場合、以下の作用があるようです。
sizeofの式に可変長配列型の名前が現れ、その配列の要素数がsizeof式の値に影響する場合、その配列の 要素数式は(副作用も含めて)完全に計算される。配列の要素数がsizeofの結果に影響しない場合、その 要素数式が計算されるかどうかは未定義である。 (「Cリファレンスマニュアル 第5版」p244より)
つまり、オペランドに可変長配列の要素数でない式が指定された場合(1)、その式は評価されません。オペランドが可変長配列の要素数であり、かつsizeof式の値に影響する場合(2)(3)、その式は評価されます。(2)のn++は評価されますが、後置インクリメントであるため、sizeof式の評価後にインクリメントしています。そのため、sizeof式はインクリメント前の値で配列要素数が計算されています。(3)の++nは前置インクリメントであるため、インクリメント後の値で配列要素数が計算されています。
暗黙の型変換
sizeof(c) = 1 …(1) sizeof(c+0) = 4 …(2)
(1)char型変数のsizeofは「1」です。
(2)c+0の場合、リテラル数0がint型であるため、式c+0はint型としてサイズ計算されます。
関数
sizeof(main) = 1 (1) sizeof(&main) = 8 (2) n = 2 sizeof(func(&n)) = 4 (3) n = 2
(1)関数名を指定した場合、「1」を返しますが、これはmeaninglessな値だそうです。sizeofで関数へのポインタ値のサイズを取得する場合、「&」をつけます。
(2)検証機は64bit OSであるため、関数へのポインタ値は「8」となります。
(3)関数funcは、ポインタで渡されたnの格納値をインクリメントする定義としていますが、nの値が更新されていないため、関数内の処理は実行されていないようです。