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비트 endian 스왑의 출력은 다음과 같습니다.0xefbeadde
.
위의 코드는 매직넘버 대신 매크로 또는 적어도 상수로 정리해야 합니다만, 이대로 도움이 되었으면 합니다.
편집: 또 다른 답변에서 지적한 바와 같이 플랫폼, OS 및 명령 세트 고유의 선택지가 있으며, 이는 상기보다 훨씬 더 빠를 수 있습니다.Linux 커널에는 endianness를 꽤 잘 처리하는 매크로(예를 들어 cpu_to_be32)가 있습니다.그러나 이러한 대안은 환경에 따라 다릅니다.실제로는 사용 가능한 접근 방식을 혼합하여 엔디안을 가장 잘 처리합니다.
다음을 포함함:
#include <byteswap.h>
머신 의존형 바이트 분할 함수의 최적화된 버전을 얻을 수 있습니다.그러면 다음 기능을 쉽게 사용할 수 있습니다.
__bswap_32 (uint32_t input)
또는
__bswap_16 (uint16_t input)
#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);
}
여기 꽤 일반적인 버전이 있습니다. 아직 컴파일하지 않았기 때문에 오타가 있을 수 있습니다.하지만 이해하셔야 합니다.
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));
NB: 속도나 공간에 최적화되어 있지 않습니다.투명하고(디버깅이 용이함) 휴대하기 위한 것입니다.
업데이트 2018-04-04 코멘터 @chux에 의해 발견된 비활성 대소문자 n == 0을 트랩하기 위해 assert()가 추가되었습니다.
편집: 라이브러리 기능입니다.그것들을 따르는 것은 수동적인 방법입니다.
__byteswap_ushort, __byteswap_ulong 및 __byteswap_uint64를 모르는 사람이 많다는 사실에 정말 놀랐습니다.물론 Visual C++에 따라 다르지만 x86/IA-64 아키텍처에서는 몇 가지 유용한 코드가 컴파일됩니다.:)
다음은 의 명시적인 사용법입니다.bswap
이 페이지에서 가져온 명령입니다.위의 본질적인 양식은 항상 이것보다 빠르기 때문에 라이브러리 루틴 없이 답변을 주기 위해 추가했을 뿐입니다.
uint32 cq_ntohl(uint32 a) {
__asm{
mov eax, a;
bswap eax;
}
}
매크로가 필요한 경우(예를 들어 임베디드 시스템):
#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
이 코드 조각은 32비트의 작은 Endian 번호를 Big Endian 번호로 변환할 수 있습니다.
#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);
}
명령어 pshufb를 4의 SSE3의 pshufb의 합니다.pshufb는 4개입니다.int
s:
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;
}
장난삼아:
#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;
}
다음은 제가 사용하고 있는 기능입니다.테스트된 기능은 모든 기본 데이터 유형에서 작동합니다.
// 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;
}
}
이게 더 빠를까요?
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];
x86 또는 x86_64 프로세서에서 실행 중인 경우 빅 엔디안이 네이티브입니다.그렇게
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);
컴파일러가 이것이 바이트레벨 조작임을 인식하고 바이트 스왑 코드를 생성하지 않는 한 이것은 가장 효율적인 솔루션이 아닙니다.그러나 메모리 레이아웃에 의존하지 않고 매크로로 쉽게 전환할 수 있습니다.
편집: 이 기능은 정렬된 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);
}
이런 종류의 기능은 Unicode UCS-2/UTF-16 파일의 엔디안을 변경할 때 유용합니다.
언급URL : https://stackoverflow.com/questions/2182002/convert-big-endian-to-little-endian-in-c-without-using-provided-func
'itsource' 카테고리의 다른 글
자바에서는 어레이 내의 모든 숫자의 합계를 어떻게 찾을 수 있습니까? (0) | 2022.08.17 |
---|---|
요소 UI의 입력 구성 요소와 $ref 사용 (0) | 2022.08.17 |
Gradle을 사용하여 실행 가능한 JAR 생성 (0) | 2022.08.17 |
CMakeLists.txt를 생성하는 방법 (0) | 2022.08.17 |
vue.js for loop 내의 함수를 호출하여 현재 항목을 전달하려면 어떻게 해야 합니까? (0) | 2022.08.17 |