char *와 std :: uint8_t * 사이의 reinterpret_cast-안전합니까?
이제 우리 모두는 때때로 이진 데이터로 작업해야합니다. C ++에서 우리는 일련의 바이트로 작업하며 처음부터 char
우리의 빌딩 블록이었습니다. sizeof
1로 정의되어 있는 바이트입니다. 그리고 모든 라이브러리 I / O 기능 char
은 기본적으로 사용 됩니다. 모든 것이 좋지만 항상 약간의 우려가 있었고 일부 사람들을 괴롭히는 약간의 이상한 점이있었습니다. 바이트의 비트 수는 구현에 따라 정의됩니다.
따라서 C99에서는 개발자가 고정 너비 정수 유형 인 자신을 쉽게 표현할 수 있도록 여러 typedef를 도입하기로 결정했습니다. 물론 이식성을 손상시키고 싶지 않기 때문에 선택 사항입니다. 그중 에서 고정 너비 8 비트 부호없는 정수 유형 인 uint8_t
C ++ 11로 마이그레이션 std::uint8_t
된은 실제로 8 비트 바이트로 작업하려는 사람들에게 완벽한 선택이었습니다.
그래서, 개발자들은 같은 8 비트 바이트 시퀀스에 동의 함을 표정 상태라는 새로운 도구와 구축을 시작 라이브러리를 받아 std::uint8_t*
, std::vector<std::uint8_t>
또는 그렇지.
그러나 아마도 매우 깊은 생각으로 표준화위원회는 std::char_traits<std::uint8_t>
개발자가 s를 이진 데이터로 std::basic_fstream<std::uint8_t>
쉽고 이식 가능하게 인스턴스화 하고 쉽게 읽을 수 없도록 하는 구현을 요구하지 않기로 결정했습니다 std::uint8_t
. 또는 우리 중 일부는 바이트의 비트 수에 관심이없고 만족할 수도 있습니다.
그러나 불행하게도, 두 세계가 충돌하고 때로는 같은 데이터를 가지고 가야 char*
하고 기대하는 도서관에 전달 std::uint8_t*
. 그러나 잠깐, char
가변 비트 가 아니고 std::uint8_t
8로 고정되어 있습니까? 데이터 손실이 발생합니까?
음, 이것에 대한 흥미로운 Standardese가 있습니다. char
정확히 하나의 바이트를 유지하도록 정의 바이트의 메모리가 낮은 어드레싱 청크, 그래서보다 적은 폭 비트 형태가 될 수 없다 char
. 다음으로 UTF-8 코드 단위를 보유 할 수 있도록 정의됩니다. 이것은 우리에게 최소 8 비트를 제공합니다. 이제 우리는 8 비트 너비가 필요한 typedef와 최소 8 비트 너비의 유형이 있습니다. 그러나 대안이 있습니까? 예, unsigned char
. 의 서명 char
은 구현에 따라 정의됩니다. 다른 유형 은요? 고맙게도 아닙니다. 다른 모든 정수 유형에는 8 비트를 벗어나는 필수 범위가 있습니다.
마지막으로은 std::uint8_t
선택 사항입니다. 즉,이 유형을 사용하는 라이브러리는 정의되지 않은 경우 컴파일되지 않습니다. 하지만 컴파일되면 어떨까요? 나는 이것이 우리가 8 비트 바이트와 CHAR_BIT == 8
.
이 지식이 있으면 또는 std::uint8_t
로 구현되는 8 비트 바이트가 있다는 사실을 알게 되면 from to 또는 그 반대로 할 수 있다고 가정 할 수 있습니까? 휴대용입니까?char
unsigned char
reinterpret_cast
char*
std::uint8_t*
이것이 나의 Standardese 읽기 능력이 저를 실패하는 곳입니다. 안전하게 파생 된 포인터 ( [basic.stc.dynamic.safety]
) 에 대해 읽었 으며 내가 이해하는 한 다음을 읽었습니다 .
std::uint8_t* buffer = /* ... */ ;
char* buffer2 = reinterpret_cast<char*>(buffer);
std::uint8_t buffer3 = reinterpret_cast<std::uint8_t*>(buffer2);
만지지 않으면 안전합니다 buffer2
. 틀 렸으면 말해줘.
따라서 다음과 같은 전제 조건이 주어집니다.
CHAR_BIT == 8
std::uint8_t
정의됩니다.
우리가 바이너리 데이터로 작업하고 있고 잠재적 인 부호 부족이 중요하지 않다고 가정 할 때 캐스트 char*
및 std::uint8_t*
앞뒤로 이동하는 것이 이식 가능하고 안전 char
합니까?
설명과 함께 표준에 대한 참조를 부탁드립니다.
편집 : 감사합니다, Jerry Coffin. 표준 ([basic.lval], §3.10 / 10)의 인용문을 추가하겠습니다.
프로그램이 다음 유형 중 하나가 아닌 다른 glvalue를 통해 객체의 저장된 값에 액세스하려고하면 동작이 정의되지 않습니다.
...
— char 또는 unsigned char 유형.
EDIT2 : 좋아, 더 깊이 들어가. std::uint8_t
typedef가라는 보장은 없습니다 unsigned char
. 확장 된 부호없는 정수 유형 으로 구현할 수 있으며 확장 된 부호없는 정수 유형은 §3.10 / 10에 포함되지 않습니다. 이제 뭐야?
좋아, 진정으로 현학 해보자. 이 , this 및 this를 읽은 후 두 표준의 의도를 이해한다고 확신합니다.
따라서 결과 포인터 reinterpret_cast
를 std::uint8_t*
to 에서 char*
역 참조하는 것은 안전 하고 이식 가능하며 [basic.lval]에 의해 명시 적으로 허용됩니다 .
그러나 일을 reinterpret_cast
에서 char*
에게 std::uint8_t*
다음 결과 포인터를 역 참조하는 것은 위반입니다 엄격한 앨리어싱 규칙 이고 정의되지 않은 동작을 하는 경우 std::uint8_t
로 구현 확장 된 부호없는 정수 타입 .
그러나 다음과 같은 두 가지 가능한 해결 방법이 있습니다.
static_assert(std::is_same_v<std::uint8_t, char> ||
std::is_same_v<std::uint8_t, unsigned char>,
"This library requires std::uint8_t to be implemented as char or unsigned char.");
이 어설 션을 사용하면 코드가 정의되지 않은 동작이 발생하는 플랫폼에서 컴파일되지 않습니다.
둘째:
std::memcpy(uint8buffer, charbuffer, size);
Cppreference 는 std::memcpy
객체를 배열로 액세스 unsigned char
하므로 안전 하고 이식 가능 하다고 말합니다 .
투자 의견에, 위해 할 수있는 reinterpret_cast
사이 char*
와 std::uint8_t*
작업 포인터를 결과와 이식 및 안전 100 % 표준 부합하는 방법으로, 다음 조건이 충족되어야합니다 :
CHAR_BIT == 8
.std::uint8_t
정의됩니다.std::uint8_t
char
또는 로 구현됩니다unsigned char
.
실질적으로 위의 조건은 플랫폼의 99 %에서 참이며 첫 번째 두 조건이 참이고 세 번째 조건이 거짓 인 플랫폼이 없을 가능성이 높습니다.
uint8_t
존재하는 경우 본질적으로 유일한 선택은 typedef unsigned char
(또는 char
서명되지 않은 경우)입니다. 어떤 것도 (비트 필드를 제외하고) a보다 적은 저장 공간을 나타낼 char
수 없으며 8 비트만큼 작을 수있는 유일한 다른 유형은 bool
. 다음으로 가장 작은 일반 정수 유형은이며 short
, 이는 16 비트 이상이어야합니다.
경우에 따라서, uint8_t
모든 존재, 당신은 정말 단 두 가지 가능성이있다 : 당신이 중 하나를 캐스팅하고 unsigned char
로 unsigned char
, 또는 주조 signed char
에 unsigned char
.
전자는 신원 변환이므로 분명히 안전합니다. 후자는 §3.10 / 10의 char 또는 unsigned char 시퀀스로 다른 유형에 액세스하기 위해 제공된 "특별한 경륜"에 속하므로 정의 된 동작도 제공합니다.
Since that includes both char
and unsigned char
, a cast to access it as a sequence of char also gives defined behavior.
Edit: As far as Luc's mention of extended integer types goes, I'm not sure how you'd manage to apply it to get a difference in this case. C++ refers to the C99 standard for the definitions of uint8_t
and such, so the quotes throughout the remainder of this come from C99.
§6.2.6.1/3 specifies that unsigned char
shall use a pure binary representation, with no padding bits. Padding bits are only allowed in 6.2.6.2/1, which specifically excludes unsigned char
. That section, however, describes a pure binary representation in detail -- literally to the bit. Therefore, unsigned char
and uint8_t
(if it exists) must be represented identically at the bit level.
To see a difference between the two, we have to assert that some particular bits when viewed as one would produce results different from when viewed as the other -- despite the fact that the two must have identical representations at the bit level.
To put it more directly: a difference in result between the two requires that they interpret bits differently -- despite a direct requirement that they interpret bits identically.
Even on a purely theoretical level, this appears difficult to achieve. On anything approaching a practical level, it's obviously ridiculous.
ReferenceURL : https://stackoverflow.com/questions/16260033/reinterpret-cast-between-char-and-stduint8-t-safe
'Development Tip' 카테고리의 다른 글
상속 된 함수에 대한 오버로드 해결 (0) | 2020.12.31 |
---|---|
블룸 필터의 반대? (0) | 2020.12.31 |
Pandoc 및 외국 문자 (0) | 2020.12.31 |
TypeScript 코딩 스타일 가이드? (0) | 2020.12.31 |
prettify.js를 확장하여 Mathematica를 지원할 수 있습니까? (0) | 2020.12.31 |