uint32_t
の値表現はuint8_t
配列にコピーされます。これはstd::memcpy
によって行われます。私がC++標準を理解しているので、これは完全に合法です。タイプT
のオブジェクトにT*
でアクセスしています。unsigned char*
にキャストされています。エイリアシングの問題はなく、位置合わせの問題はありません。std :: memcpyまたは明示的なchar値の代入 - C++での等価および正当性
他の方法ではあまり明らかではありません。 unsigned char*
でT
のオブジェクト表現にアクセスしています。これは正当なものです。しかし、という語にはにアクセスすると、が変更されます。?
もちろん、エイリアシングやアライメントの問題はありません。しかし、バッファーs
の値が外部ソースからのものであれば、問題はあります。正しいエンディアンを保証し、トラップ表現を省略する必要があります。右エンディアンをチェックすることができますので、解決できます。 しかし、トラップ表現はどうですか?どうすればそれを避けることができますか?またはuint
タイプには、double
というよりも、トラップ表現がありませんか?
の値をuint_t
オブジェクトにシフトする別の(より準拠した)方法があります。我々はまだエンディアンに従わなければならないが、これは安全にトラップ表現を省略するべきである。
しかし、小さなμC(8ビット)で大きなタイプをシフトするとかなり高価になることがあります。
次の質問は、2番目の試行(コードの下を参照)が合法性と機能性に関するmemcpy
のアプローチと同等であるかどうかです。さて、それはmemcpy
バージョンがよりオプティマイザに優しいようです。
#include <cstdint>
#include <cstring>
#include <cassert>
typedef uint32_t utype;
constexpr utype value = 0x01020304;
int main() {
utype a{value};
utype b{0};
uint8_t s[sizeof(utype)]{};
// first
std::memcpy(s, &a, sizeof(utype));
assert(s[0] == (value & 0xff));
std::memcpy(&b, s, sizeof(utype));
assert(b == value);
// second
const uint8_t* ap = reinterpret_cast<const uint8_t*>(&a);
s[0] = ap[0]; // explicitly legal in C++
s[1] = ap[1];
s[2] = ap[2];
s[3] = ap[3];
assert(s[0] == (value & 0xff));
uint8_t* bp = reinterpret_cast<uint8_t*>(&b);
bp[0] = s[0]; // same as memcpy or ist this UB ?
bp[1] = s[1];
bp[2] = s[2];
bp[3] = s[3];
assert(b == value);
}
'std :: memcpy'を実行するときにポインタが別の型を指しているオブジェクトにアクセスしていないときは、ポインタのポインティングされた型のオブジェクトにアクセスしていますが、ソースオブジェクトと同じ値を表します。 –
'typedef uint32_t utype;を使わないでください。コードが読みにくくなります。私は他人の名前で話したくないので、私は自分のために話しますが、もっとプログラマにも当てはまると思います。私が 'utype a; 'を見ると、' utype'が何であるかを知るために、余分な認知的なステップが私の心の中にあります。そして、私の心の中でコード全体を読んでいる間に、「utype」は 'std :: uint32_t'、バックグラウンドプロセスは' utype'は 'std :: uint32_t'; ...." on repeatです。しかし、もし私が 'std :: uint32_t a;'を参照すると、私はそれについて考えることさえしません。私はただ瞬時に、ほとんど本能的に、それが何であるか知っています。 – bolov
* access *は読み取りまたは書き込みを意味します –