![見出し画像](https://assets.st-note.com/production/uploads/images/97855712/rectangle_large_type_2_9a9a501f325b7e9850ccfa211e0dd0d9.png?width=1200)
何故 ARM だと char が unsigned になるのか
タイトル通りです。
signed char ではなく
unsigned char でもなく
プレーンで char 型を使ったときに
何故 ARM だと unsigned で展開されるのか
従来、C言語では、
『unsigned も signed も指定しない場合、 signed である』
というのが一般的でした。
unsigned も signed も指定しないプレーンな char を使用した場合、多くのC言語プログラマーは「signed char」と考える人が多いでしょう。しかしながら、C言語の言語仕様としては、「char」が「signed char」であると明確に規定されているわけではないそうです。ですが、規定されていないとは言え「符号を指定しない場合は有符号(signed)」に慣れているプログラマーが多い中で、何故わざわざ「無符号(unsigned)」にする必要があるのか。それが甚だ疑問でした。
それを調べてみました。
そもそもの発端は、前回記事のこちらにあります。
結論から申しますと・・・。
正確にはわからなかったorz。
で終わるのもアレなので、とにかくスッタモンダした経緯を書いておこうかと思います。
とりあえず、
char の場合と
signed char の場合で
それぞれどのようにアセンブラに展開されるのか見てみました。
ソースコードはこちら。
void clear(char data[])
{
char c;
for (c = 0; c < 0x80; c++)
{
data[c] = 0;
}
}
void clear_s(signed char data[])
{
signed char c;
for (c = 0; c < 0x80; c++)
{
data[c] = 0;
}
}
そして、アセンブラです。
関数「clear_s」のアセンブラは、
関数「clear」との差異のみ記載します。
clear: // %bb.0:
sub sp, sp, #16
.cfi_def_cfa_offset 16
str x0, [sp, #8]
strb wzr, [sp, #7]
b .LBB0_1
.LBB0_1: // =>This Inner Loop Header: Depth=1
ldrb w8, [sp, #7]
subs w8, w8, #128
cset w8, ge
tbnz w8, #0, .LBB0_4
b .LBB0_2
.LBB0_2: // in Loop: Header=BB0_1 Depth=1
ldr x8, [sp, #8]
ldrb w9, [sp, #7]
// kill: def $x9 killed $w9
add x8, x8, x9
strb wzr, [x8]
b .LBB0_3
.LBB0_3: // in Loop: Header=BB0_1 Depth=1
ldrb w8, [sp, #7]
add w8, w8, #1
strb w8, [sp, #7]
b .LBB0_1
.LBB0_4:
add sp, sp, #16
.cfi_def_cfa_offset 0
ret
clear_s: .LBB1_1: // =>This Inner Loop Header: Depth=1
ldrsb w8, [sp, #7]
subs w8, w8, #128
cset w8, ge
tbnz w8, #0, .LBB1_4
b .LBB1_2
.LBB1_2: // in Loop: Header=BB1_1 Depth=1
ldr x8, [sp, #8]
ldrsb x9, [sp, #7]
add x8, x8, x9
strb wzr, [x8]
b .LBB1_3
それで、結局のところ、どこがどう違うのかというと、差違はここだけ。
ldrb w8, [sp, #7]
ldrsb w8, [sp, #7]
メモリ [sp, #7] から 1byte のデータをレジスタにロードする命令です。
ARM には 1byte のレジスタはありません。
レジスタ x8 は 64bit
レジスタ w8 は 32bit
w8 は x8 の半分だけを使います。
8bit から 32bit に展開する場合、残りの 24bit をどうするのかを考えなければなりません。
特に、 8bit 全てが 1 で、
1111 1111
の場合、
255 と解釈するのか、はたまた
-1 と解釈するのか
(1)255 の場合
0000 0000 0000 0000 0000 0000 1111 1111
に展開しなければなりません。
そして。
(2)-1 の場合
1111 1111 1111 1111 1111 1111 1111 1111
に展開しなければなりません。
先のニーモニックで
ldrb は (1) のように展開して、
ldrsb は (2) のように展開します。
それだけ?
それだけ。
と言っても、それぞれの命令の処理時間までは考慮していないのですが。
ですが、もう少しだけ。
次のサイトは、 ARM でよくお世話なっているサイトです。
中央あたりに表があるのですが、ロード命令がなんと、9種類もあるというのです。
符号付きは 7種類を使い分けなければならないのですが、
符号なしであれば 5種類で済む。
うーん。
このためかなぁ。
「符号付きの方が、アセンブラに展開するときに面倒」?
コンピューターにとってそんなことが問題になるだろうか。
イマイチしっくりこない。
全く違う理由かもしれない。
ご存知の方がいらっしゃれば教えていただけると幸いです。
という、中途半端なオチでした・・・