アプリケーションばかり書いているとエンディアンが異なるマシンへの移植なんて考えないのだが、成る程と思う事有り。
次の二つは書き方の違いに過ぎないように見えるが(この例はシフトやOR演算は本質では無い)…
u_int32_t *src u_int32_t data; … u_int8_t *p = (u_int8_t)&data; … src[0] = 0x78100000 | (p[0] << 8); src[1] = 0x78110000 | (p[1] << 8); src[2] = 0x78120000 | (p[2] << 8); src[3] = 0x78130000 | (p[3] << 8);
u_int32_t *src u_int32_t data; … src[0] = 0x78100000 | ((data & 0x000000ff) << 8); src[1] = 0x78110000 | ( data & 0x0000ff00 ); src[2] = 0x78120000 | ((data & 0x00ff0000) >> 8); src[3] = 0x78130000 | ((data & 0xff000000) >> 16);
後者はそうではないが、前者はエンディアンが異なるマシンで異なる結果を生む。 data をバイト配列アクセスしている処が原因。
成る程…前者の方が簡潔に見えるので好まれるかも知れないが、そう言う事である。 昔書いた次の節のC++コードは失格 orz
先日、Windows プログラム(非ネットワークプログラム)でビッグエンディアンからリトルエンディアンへ変換する必要があった。然したる処理速度を要求されている訳でもなく、急ぎだったのでさっさと #include
VC6 Debug、混合モードで追い、ntohl/ntohs についてそれとなくコメントを付けてみた。
DWORD y = ntohl(0x12345678); /* 74F93304 mov ecx,dword ptr [esp+4] ; 引数 0x12345678 を cx へ 74F93308 mov eax,ecx ; ax へ 74F9330A mov edx,ecx ; 上位ワードの作成 74F9330C shl eax,10h ; 0x56780000 74F9330F and edx,0FF00h ; 0x00005600 74F93315 or eax,edx ; 0x56785600 74F93317 mov edx,ecx ; 下位ワードの作成 74F93319 shr edx,10h ; 0x00001234 74F9331C and ecx,0FF0000h ; 0x00340000 74F93322 or edx,ecx ; 0x00341234 74F93324 shl eax,8 ; 0x78560000 シフトして上位ワード 74F93327 shr edx,8 ; 0x00003412 シフトして下位ワード 74F9332A or eax,edx ; 0x78563412 合成して出来上がり 74F9332C ret 4 */ WORD a = ntohs(0x1234); /* 74F91512 movzx ecx,word ptr [esp+4] 74F91517 mov eax,ecx 74F91519 xor edx,edx 74F9151B shr eax,8 74F9151E mov dh,cl 74F91520 or eax,edx 74F91522 ret 4 */
ntohs はともかく、ntohl も流石にレジスタのみで得るようになっており、 word 単位(MSでは16bitの意味)でシフトして最後に byte シフトして合成している。奇をてらわない素直な流れと思う。
意義は無いが、これをそっくり真似てインラインアセンブリ関数にするならば、こんな感じか…↓
DWORD n2hl(DWORD v) { _asm { mov ecx, v mov eax, ecx mov edx, ecx shl eax, 10h and edx, 0FF00h or eax, edx mov edx, ecx shr edx, 10h and ecx, 0FF0000h or edx, ecx shl eax, 8 shr edx, 8 or eax, edx mov v, eax } return v; }
さて、同じ事を C++で書くと効率悪いコードになるか...
70: DWORD x = 0x12345678; 00401458 mov dword ptr [ebp-14h],12345678h 71: 75: DWORD y = ((BYTE*)&x)[3] | 76: (((BYTE*)&x)[2]) << 8 | 77: (((BYTE*)&x)[1]) << 16 | 78: (((BYTE*)&x)[0]) << 24; 0040145F mov eax,dword ptr [ebp-11h] ; [3] を load 00401462 and eax,0FFh ; 0x00000012 00401467 mov ecx,dword ptr [ebp-12h] ; [2] を load 0040146A and ecx,0FFh ; 0x00000034 00401470 shl ecx,8 ; 0x00003400 00401473 or eax,ecx ; 0x00003412 00401475 mov edx,dword ptr [ebp-13h] ; 以下略 00401478 and edx,0FFh ; 0040147E shl edx,10h ; 00401481 or eax,edx ; 00401483 mov ecx,dword ptr [ebp-14h] ; 00401486 and ecx,0FFh ; 0040148C shl ecx,18h ; 0040148F or eax,ecx ; 0x78563412 00401491 mov dword ptr [ebp-18h],eax ; y へ返す。
Debug 版だとそのまんまな感じだが、Release 版(最適化)にすると少し変形された↓
004010B4 mov ecx,dword ptr [ebp+9] ; [1] を load ... 0x??123456 004010B7 mov edx,dword ptr [ebp+0Ah] ; [2] を load ... 0x????1234 004010BA xor eax,eax ; 004010BC and ecx,0FFh ; 0x00000056 004010C2 mov ah,byte ptr [ebp+8] ; [0] を load ... 0x00007800 004010C5 and edx,0FFh ; 0x00000034 004010CB or eax,ecx ; 0x00007856 004010CD mov ecx,dword ptr [ebp+0Bh] ; [3] を load ... 0x??????12 004010D0 shl eax,8 ; 0x00785600 004010D3 or eax,edx ; 0x00785634 004010D5 and ecx,0FFh ; 0x00000012 004010DB shl eax,8 ; 0x78563400 004010DE or eax,ecx ; 0x78563412
ま、この程度なら C/C++ で書いても(バグが無ければ)大差無さそう…。