potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++ size_t→DWORDキャスト時の注意(UINT、std::uint32_t、unsigned int等)

STLsize_t、Win32 APIDWORD(あるいはUINT)を使う場合が多いです。size_tからDWORDへは暗黙にキャストできますが、範囲外が切り捨てられる可能性があるのでコンパイル警告が発生します。Cスタイルキャスト(DWORD)...やキャスト演算子static_cast<DWORD>(...)により警告を消せますが、切り捨ての可能性については念頭に置くべきです。

次のコードでは64ビット環境のsize_tと同サイズのstd::uint64_tDWORDと同サイズのstd::uint32_tstatic_castしています。キャスト前後の値が異なること(0x12345678ABCDEF12 != (std::uint32_t)0x12345678ABCDEF12 == 0xabcdef12)に注意してください。

#include <cstdint>
#include <iostream>

int main()
{
    std::uint64_t ui64 = 0x12345678ABCDEF12;
    std::uint32_t ui32 = static_cast<std::uint32_t>(ui64);

    std::wcout << L"0x" << std::hex << ui64 << std::endl;
    std::wcout << L"0x" << std::hex << ui32 << std::endl;
    // > 0x12345678abcdef12
    // > 0xabcdef12
}

符号ありほど大きな問題は生じませんが、size_tDWORDを意図せず混在させるとバグの余地が残ることが推察されます。問題の解決にはビット演算や<numeric>numeric_limits<DWORD>::max()による変換前の確認、Boost C++ Librariesのnumeric_castの使用などが挙げられます。

なお、DWORDを扱うWin32 APIのみ使用する場合など、限定的な状況ではstatic_castを使用しても問題は生じません。