5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C++の共用体やタイプパニングについて

Last updated at Posted at 2022-12-01

はじめに

同じメモリ領域に別の型を格納したい, 同じメモリ領域を初期化したときとは別の型で読み出したい, シリアライザの実装などで必要になることがあると思います.
解決策として, 共用体を悪用活用したり, ポインタの指す型を読み替えたりする違法行為が行われてきました (JPCERT EXP32-C).

共用体の誤った使い方

次のコードはC言語の場合は未規定で動作しますが, C++では未定義です.

union U
{
    int32_t i_;
    float f_;
};
U u;
u.i_ = 0;
float f = u.f_; //< 最後に値を入れたメンバ以外でアクセスすることは, 未定義

型の読み替えの誤った使い方

ポインタの指す型が互換性のない型の場合, 未定義動作になります.

int32_t i=0;
float f = *((float*)&i);

タイプパニング

型変換ではなく, ビット単位で同じ内容の別の型に変換することを, タイプパニング(type-punning)といいます. 共用体やポインタの型を読み替える方法は, 多くの処理系で期待したとおりに動作してしまいます(全ては調査していないです).
安全な方法を次に記載します.

C++17以前

memcpyを使う方法があります. シリアライザなどで, エンディアンを入れ替える場合は, バイト毎に入れ替えてからmemcpyを行うことになるでしょう.
コードの面倒くささはどうしようもないですが, パフォーマンスに関してはコンパイラ組み込み関数を使うと型の読み替えと同等になると思います.

#include <cstdint>
#include <cstring>
#include <iostream>

int main(void)
{
    int32_t x=0;
    float f;
    ::memcpy(&f, &x, sizeof(f));
    std::cout << f << std::endl;
    return 0;
}

C++20以降

正にタイプパニングを行うstd::bit_castが加わりました.

#include <cstdint>
#include <bit>
#include <iostream>

int main(void)
{
    int32_t x=0;
    float f = std::bit_cast<float>(x);
    std::cout << f << std::endl;
    return 0;
}

なんでも型

C++17からより安全な共用体として, std::variantstd::anyが追加されました. 誤った使い方をした場合例外を投げてくれます.

#include <cstdint>
#include <iostream>
#include <variant>
#include <any>

int main(void)
{
    using namespace std;
    variant<int32_t, float> v = 0;
    any a = 0;
    cout << get<int32_t>(v) << endl;
    cout << any_cast<int32_t>(a) << endl;
    cout << get<float>(v) << endl; //bad_variant_access例外
    cout << any_cast<float>(a) << endl; //std::bad_any_cast例外
    return 0;
}

std::variantはコンパイル時に入れることができる型が決まるので, 従来のunionに近いです. 入れた型を忘れてしまった場合は, std::variant::get_ifstd::variant::visitを使用します.

std::anyはランタイムで動的に入れる型を変更することができます. 入れた型を忘れた場合は, std::any::has_valuestd::any::typeを使用します.

まとめ

簡単な方法がないため, 処理系も仕方なく上手く処理していたようで, 間違った方法が解説されていたように思います. プロダクトでは移植性のある方法に切り替えていくとよいでしょう.

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?