C에서 빅 엔디안을 리틀 엔디안으로 변환하는 함수를 작성해야합니다. 라이브러리 함수를 사용할 수 없습니다.
C에서 빅 엔디안을 리틀 엔디안으로 변환하는 함수를 작성해야합니다. 라이브러리 함수를 사용할 수 없습니다.
답변:
필요한 것이 간단한 바이트 스왑이라고 가정하고 다음과 같이 시도하십시오.
부호없는 16 비트 변환 :
swapped = (num>>8) | (num<<8);
부호없는 32 비트 변환 :
swapped = ((num>>24)&0xff) | // move byte 3 to byte 0
((num<<8)&0xff0000) | // move byte 1 to byte 2
((num>>8)&0xff00) | // move byte 2 to byte 1
((num<<24)&0xff000000); // byte 0 to byte 3
이렇게하면 위치 1234에서 4321로 바이트 순서가 바뀝니다. 입력이 0xdeadbeef
이면 32 비트 엔디안 스왑의 출력은 0xefbeadde
.
위의 코드는 매직 넘버 대신 매크로 또는 적어도 상수로 정리해야하지만, 그대로 도움이되기를 바랍니다.
편집 : 다른 답변이 지적했듯이 플랫폼, OS 및 명령 세트 특정 대안이 위보다 훨씬 빠를 수 있습니다. Linux 커널에는 엔디안을 매우 잘 처리하는 매크로 (예 : cpu_to_be32)가 있습니다. 그러나 이러한 대안은 환경에 따라 다릅니다. 실제로 엔디안은 사용 가능한 접근 방식을 혼합하여 사용하는 것이 가장 좋습니다.
((num & 0xff) >> 8) | (num << 8)
되면 gcc 4.8.3은 단일 rol
명령어를 생성합니다 . 32 비트 변환이로 쓰여지면 ((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24)
동일한 컴파일러가 단일 bswap
명령어를 생성합니다 .
struct byte_t reverse(struct byte_t b) { struct byte_t rev; rev.ba = b.bh; rev.bb = b.bg; rev.bc = b.bf; rev.bd = b.be; rev.be = b.bd; rev.bf = b.bc; rev.bg = b.bb; rev.bh = b.ba; return rev;}
여기서 이것은 8 필드가 각각 1 비트 인 비트 필드입니다. 그러나 그것이 다른 제안들만큼 빠른지 확실하지 않습니다. int의 경우를 사용 union { int i; byte_t[sizeof(int)]; }
하여 정수에서 바이트 단위로 반전합니다.
포함함으로써 :
#include <byteswap.h>
최적화 된 버전의 시스템 종속 바이트 스왑 기능을 얻을 수 있습니다. 그러면 다음 기능을 쉽게 사용할 수 있습니다.
__bswap_32 (uint32_t input)
또는
__bswap_16 (uint16_t input)
#include <byteswap.h>
합니다. .h 파일 자체의 주석을 참조하십시오. 이 게시물에는 유용한 정보가 포함되어 있으므로 저자가 lib 함수를 사용하지 않는 OP 요구 사항을 무시 했음에도 불구하고 찬성했습니다.
#include <stdint.h>
//! Byte swap unsigned short
uint16_t swap_uint16( uint16_t val )
{
return (val << 8) | (val >> 8 );
}
//! Byte swap short
int16_t swap_int16( int16_t val )
{
return (val << 8) | ((val >> 8) & 0xFF);
}
//! Byte swap unsigned int
uint32_t swap_uint32( uint32_t val )
{
val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF );
return (val << 16) | (val >> 16);
}
//! Byte swap int
int32_t swap_int32( int32_t val )
{
val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF );
return (val << 16) | ((val >> 16) & 0xFFFF);
}
업데이트 : 64 비트 바이트 스와핑 추가
int64_t swap_int64( int64_t val )
{
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL);
}
uint64_t swap_uint64( uint64_t val )
{
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
return (val << 32) | (val >> 32);
}
int32_t
와 int64_t
변형의 마스크 뒤에 추론 무엇 ... & 0xFFFF
과 ... & 0xFFFFFFFFULL
? 여기에 부호 확장과 관련된 문제가 있습니까? 또한 왜 swap_int64
돌아 오나요 uint64_t
? 그럴까요 int64_t
?
swap_int64
답변에서 반환 값의 유형을 변경할 수 있습니다 . 도움이되는 답변에 +1, BTW!
LL
불필요한 있습니다 (u)swap_uint64()
이처럼 많이 L
필요하지 않습니다 (u)swap_uint32()
. 은 U
필요하지 않습니다 uswap_uint64()
이처럼 많이 U
필요하지 않습니다uswap_uint32()
다음은 상당히 일반적인 버전입니다. 컴파일하지 않았으므로 오타가있을 수 있지만 아이디어를 얻어야합니다.
void SwapBytes(void *pv, size_t n)
{
assert(n > 0);
char *p = pv;
size_t lo, hi;
for(lo=0, hi=n-1; hi>lo; lo++, hi--)
{
char tmp=p[lo];
p[lo] = p[hi];
p[hi] = tmp;
}
}
#define SWAP(x) SwapBytes(&x, sizeof(x));
주의 : 이것은속도 나 공간에 최적화되어 있지 않습니다 . 명확하고 (디버그하기 쉬움) 이식 가능하도록 설계되었습니다.
업데이트 2018-04-04 주석 작성자 @chux가 발견 한대로 n == 0의 잘못된 대소 문자를 트랩하기 위해 assert ()를 추가했습니다.
bswap
은 최적화가 활성화 된 괜찮은 X86 컴파일러에 의해 단일 명령어로 컴파일됩니다 . 크기에 대한 매개 변수가있는이 버전은 그렇게 할 수 없습니다.
매크로가 필요한 경우 (예 : 임베디드 시스템) :
#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
UINT
그들의 이름에 있는 이유 입니다.
편집 : 라이브러리 기능입니다. 그것들을 따르는 것은 그것을 수행하는 수동 방법입니다.
__byteswap_ushort, __byteswap_ulong 및 __byteswap_uint64를 알지 못하는 사람들의 수에 절대적으로 놀랐습니다 . 물론 그들은 Visual C ++에 특화되어 있지만 x86 / IA-64 아키텍처에서 맛있는 코드로 컴파일됩니다. :)
다음 은이 페이지에서 가져온bswap
지침 의 명시적인 사용법입니다 . 위의 내장 형식은 항상 이것보다 빠르다 는 점에 유의하십시오 . 라이브러리 루틴없이 답변을 제공하기 위해 추가했습니다.
uint32 cq_ntohl(uint32 a) {
__asm{
mov eax, a;
bswap eax;
}
}
농담으로 :
#include <stdio.h>
int main (int argc, char *argv[])
{
size_t sizeofInt = sizeof (int);
int i;
union
{
int x;
char c[sizeof (int)];
} original, swapped;
original.x = 0x12345678;
for (i = 0; i < sizeofInt; i++)
swapped.c[sizeofInt - i - 1] = original.c[i];
fprintf (stderr, "%x\n", swapped.x);
return 0;
}
int i, size_t sizeofInt
두 가지 모두에 대해 동일한 유형이 아닌 사용이 궁금 합니다.
다음은 Intel 내장 함수를 사용하여 SSSE3 명령 pshufb를 사용하는 방법이며, 4 int
초의 배수가 있다고 가정합니다 .
unsigned int *bswap(unsigned int *destination, unsigned int *source, int length) {
int i;
__m128i mask = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
for (i = 0; i < length; i += 4) {
_mm_storeu_si128((__m128i *)&destination[i],
_mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&source[i]), mask));
}
return destination;
}
이 작업이 더 빠를까요?
uint32_t swapped, result;
((byte*)&swapped)[0] = ((byte*)&result)[3];
((byte*)&swapped)[1] = ((byte*)&result)[2];
((byte*)&swapped)[2] = ((byte*)&result)[1];
((byte*)&swapped)[3] = ((byte*)&result)[0];
char
아니라고 생각합니다 byte
.
다음은 내가 사용해온 함수입니다. 모든 기본 데이터 유형에서 테스트되고 작동합니다.
// SwapBytes.h
//
// Function to perform in-place endian conversion of basic types
//
// Usage:
//
// double d;
// SwapBytes(&d, sizeof(d));
//
inline void SwapBytes(void *source, int size)
{
typedef unsigned char TwoBytes[2];
typedef unsigned char FourBytes[4];
typedef unsigned char EightBytes[8];
unsigned char temp;
if(size == 2)
{
TwoBytes *src = (TwoBytes *)source;
temp = (*src)[0];
(*src)[0] = (*src)[1];
(*src)[1] = temp;
return;
}
if(size == 4)
{
FourBytes *src = (FourBytes *)source;
temp = (*src)[0];
(*src)[0] = (*src)[3];
(*src)[3] = temp;
temp = (*src)[1];
(*src)[1] = (*src)[2];
(*src)[2] = temp;
return;
}
if(size == 8)
{
EightBytes *src = (EightBytes *)source;
temp = (*src)[0];
(*src)[0] = (*src)[7];
(*src)[7] = temp;
temp = (*src)[1];
(*src)[1] = (*src)[6];
(*src)[6] = temp;
temp = (*src)[2];
(*src)[2] = (*src)[5];
(*src)[5] = temp;
temp = (*src)[3];
(*src)[3] = (*src)[4];
(*src)[4] = temp;
return;
}
}
source
필요에 따라 정렬되지만 그 가정이 유지되지 않으면 코드는 UB입니다.
편집 :이 기능은 정렬 된 16 비트 단어의 엔디안 만 교체합니다. UTF-16 / UCS-2 인코딩에 종종 필요한 함수입니다. 편집 끝.
메모리 블록의 엔디안을 변경하려면 저의 엄청나게 빠른 접근 방식을 사용할 수 있습니다. 메모리 어레이의 크기는 8의 배수 여야합니다.
#include <stddef.h>
#include <limits.h>
#include <stdint.h>
void ChangeMemEndianness(uint64_t *mem, size_t size)
{
uint64_t m1 = 0xFF00FF00FF00FF00ULL, m2 = m1 >> CHAR_BIT;
size = (size + (sizeof (uint64_t) - 1)) / sizeof (uint64_t);
for(; size; size--, mem++)
*mem = ((*mem & m1) >> CHAR_BIT) | ((*mem & m2) << CHAR_BIT);
}
이러한 종류의 함수는 유니 코드 UCS-2 / UTF-16 파일의 엔디안을 변경하는 데 유용합니다.
t know if it
: s의 빠른 속도가 wokrs 제안으로하지만 github.com/heatblazer/helpers/blob/master/utils.h
CHAR_BIT
에 의존하는 대신 8
호기심이 많습니다 . 참고 상수에 필요하지 않습니다. 0xFF00FF00FF00FF00ULL
CHAR_BIT == 8
LL
CHAR_BIT
매크로의 노출을 늘리기 위해 로만 썼습니다 . LL의 경우 다른 어떤 것보다 주석입니다. 또한 옳은 일을하지 않는 버그가있는 컴파일러 (사전 표준)로 오래 전에 잡았던 습관이기도합니다.
이 코드 조각은 32 비트 리틀 엔디안 수를 빅 엔디안 수로 변환 할 수 있습니다.
#include <stdio.h>
main(){
unsigned int i = 0xfafbfcfd;
unsigned int j;
j= ((i&0xff000000)>>24)| ((i&0xff0000)>>8) | ((i&0xff00)<<8) | ((i&0xff)<<24);
printf("unsigned int j = %x\n ", j);
}
x86 또는 x86_64 프로세서에서 실행중인 경우 big endian은 기본입니다. 그래서
16 비트 값
unsigned short wBigE = value;
unsigned short wLittleE = ((wBigE & 0xFF) << 8) | (wBigE >> 8);
32 비트 값
unsigned int iBigE = value;
unsigned int iLittleE = ((iBigE & 0xFF) << 24)
| ((iBigE & 0xFF00) << 8)
| ((iBigE >> 8) & 0xFF00)
| (iBigE >> 24);
컴파일러가 이것이 바이트 레벨 조작임을 인식하고 바이트 스와핑 코드를 생성하지 않는 한 이것은 가장 효율적인 솔루션이 아닙니다. 그러나 메모리 레이아웃 트릭에 의존하지 않으며 매우 쉽게 매크로로 변환 할 수 있습니다.