다음 예제에서 uint32_t
의 값 표현은 uint8_t
배열로 복사됩니다. 이것은 std::memcpy
에 의해 수행됩니다. C++ 표준을 이해함에 따라 이것은 완전히 합법적입니다. 에 캐스팅 된 T*
을 통해 T
유형의 객체에 액세스하고 있습니다. 앨리어싱 문제가없고 정렬 문제가 없습니다.std :: memcpy 또는 명시 적 char 값 할당 - C++에서 동일하고 유효합니다.
다른 방법으로는 덜 명확합니다. unsigned char*
을 통해 T
의 객체 표현에 액세스하고 있습니다. 이는 유효합니다. 그러나 에 액세스 할 때에 이 변경되면이 포함됩니까?
물론 앨리어싱과 앨리어스 문제는 없습니다. 버퍼 s
의 값이 외국 출처 인 경우에는 문제가 있습니다. 올바른 엔디안을 보장하고 트랩 표현을 생략해야합니다. 오른쪽 엔디안을 확인할 수 있으므로이를 해결할 수 있습니다. 그러나 함정 표현은 어떨까요? 우리는 어떻게 그것을 피할 수 있습니까? 또는 uint
유형에 double
과 반대되는 트랩 표현이 없습니까?
uint_t
개체로 uint8_t
값을 이동하는 방법이 더 적합하다는 것을 알고 있습니다. 우리는 여전히 엔디안에 복종해야하지만 이것은 함정 표현을 안전하게 생략해야합니다.
그러나 작은 μC (8 비트)에서 큰 유형의 시프트는 매우 비쌀 수 있습니다!
두 번째 시도 (코드의 아래 참조)가 적법성 및 기능과 관련하여 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'; .... "가 반복되는 백그라운드 프로세스를 가져야한다. 그러나'std :: uint32_t a; '를 보면 그것에 대해서 생각조차하지 않습니다. 나는 즉시, 거의 본능적으로, 그것이 무엇인지를 안다. – bolov
* access *는 읽거나 쓰는 것을 의미합니다 –