目的
C言語での型変換にはキャストを使うが、実際にどのような機械語へ変換されるかを確認する。
gcc -O0 -S test.c -o test.s -masm=intel
[20251016追記]
同じ検証をARMでも行いました。
C言語の型変換をARM32アセンブリで確認する(int, short, float)
int -> short
intは4バイト
shortは2バイト
値100をメモリ(a変数)へ格納したあと、EAXレジスタ(32ビット)へ取り出し、EAXの下位16ビットAXをメモリ(b変数)へ格納している。
こうすることでINT型の下位16ビットのみを取り出し、SHORT型へ変換している。
int a=100;
short b=(short)a;
mov DWORD PTR -4[rbp], 100
mov eax, DWORD PTR -4[rbp]
mov WORD PTR -6[rbp], ax
元のコード
int main(){
int a=100;
short b=(short)a;
return 0;
}
test@test-Th
.file "test.c"
.intel_syntax noprefix
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
mov DWORD PTR -4[rbp], 100
mov eax, DWORD PTR -4[rbp]
mov WORD PTR -6[rbp], ax
mov eax, 0
pop rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04.2) 11.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
short -> int
shortは2バイト
intは4バイト
movsxは16ビットの符号を保ったままビットに広げる命令
short b=100;
int a=(int)b;
mov WORD PTR -6[rbp], 100
movsx eax, WORD PTR -6[rbp]
mov DWORD PTR -4[rbp], eax
元のコード
int main(){
short b=100;
int a=(int)b;
return 0;
}
.file "test.c"
.intel_syntax noprefix
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
mov WORD PTR -6[rbp], 100
movsx eax, WORD PTR -6[rbp]
mov DWORD PTR -4[rbp], eax
mov eax, 0
pop rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04.2) 11.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
int -> float
cvtsi2ssは符号付き整数を整数 → 単精度浮動小数点に変換する命令
movssは単精度浮動小数点(float、32bit)をレジスタ⇔メモリ間 で移動する命令
int a = 42;
float f;
f = (float)a;
mov DWORD PTR -8[rbp], 42
pxor xmm0, xmm0 ; xmm0レジスタを0で初期化
cvtsi2ss xmm0, DWORD PTR -8[rbp] ; 42を浮動小数点 42.0fとして読みこむ
movss DWORD PTR -4[rbp], xmm0 ; xmm0 の下位32bit(単精度 float)をスタック上の f の領域に格納
元のコード
int main() {
int a = 42;
float f;
f = (float)a;
return 0;
}
.file "test.c"
.intel_syntax noprefix
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
mov DWORD PTR -8[rbp], 42
pxor xmm0, xmm0
cvtsi2ss xmm0, DWORD PTR -8[rbp]
movss DWORD PTR -4[rbp], xmm0
mov eax, 0
pop rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04.2) 11.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
float -> int
float f = 12.3;
int a;
a = (int)f;
movss xmm0, DWORD PTR .LC0[rip] ; 単精度浮動小数点をxmm0に読み込む
movss DWORD PTR -8[rbp], xmm0
movss xmm0, DWORD PTR -8[rbp]
cvttss2si eax, xmm0 ; float → int に変換する命令 小数点切り捨て
mov DWORD PTR -4[rbp], eax
.LC0:
.long 1095027917
元のコード
int main() {
float f = 12.3;
int a;
a = (int)f;
return 0;
}
.file "test.c"
.intel_syntax noprefix
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
movss xmm0, DWORD PTR .LC0[rip]
movss DWORD PTR -8[rbp], xmm0
movss xmm0, DWORD PTR -8[rbp]
cvttss2si eax, xmm0
mov DWORD PTR -4[rbp], eax
mov eax, 0
pop rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.section .rodata
.align 4
.LC0:
.long 1095027917
.ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04.2) 11.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
まとめ
int -> short axレジスタに入れることで上位16ビットを切り捨てている。
short -> int movsx命令を使って拡張している。
int -> float cvtsi2ss命令を使って変換している。
float -> int cvttss2si命令を使って変換している。
キャストの書き方は同じでも、内部での変換方法は型によって全く異なることが分かる。
環境
端末 : ThinkPad-X280
OS : Ubuntu 22.04.5 LTS
gcc : (Ubuntu 11.4.0-1ubuntu1~22.04.2) 11.4.0