C에서 빅 엔디안을 리틀 엔디안으로 변환 [제공된 함수를 사용하지 않고] [닫기]


91

C에서 빅 엔디안을 리틀 엔디안으로 변환하는 함수를 작성해야합니다. 라이브러리 함수를 사용할 수 없습니다.


5
16 비트 값? 32 비트 값? 흙손? 배열?
John Knoeller 2010

19
답을 고를 시간인가?
Aniket Inge 2012

7
재 개설 투표. C ++의 경우 stackoverflow.com/questions/105252/… 와 동일합니다 . 더 명확하게하기 위해 편집 할 수 있습니다.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

답변:


168

필요한 것이 간단한 바이트 스왑이라고 가정하고 다음과 같이 시도하십시오.

부호없는 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)가 있습니다. 그러나 이러한 대안은 환경에 따라 다릅니다. 실제로 엔디안은 사용 가능한 접근 방식을 혼합하여 사용하는 것이 가장 좋습니다.


5
플랫폼 / 하드웨어 별 방법을 언급하는 경우 +1. 프로그램은 항상 일부 하드웨어에서 실행되며 하드웨어 기능은 항상 가장 빠릅니다.
eonil

21
16 비트 변환이으로 수행 ((num & 0xff) >> 8) | (num << 8)되면 gcc 4.8.3은 단일 rol명령어를 생성합니다 . 32 비트 변환이로 쓰여지면 ((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24)동일한 컴파일러가 단일 bswap명령어를 생성합니다 .
user666412 2015-04-30

이것이 얼마나 효율적인지 모르겠지만 바이트 순서를 다음과 같은 비트 필드로 바꿨습니다. 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)]; }하여 정수에서 바이트 단위로 반전합니다.
Ilian Zapryanov

식은 다음과 같아야한다고 생각합니다. (num >> 8) | (숫자 << 8)는 바이트 순서를 반대로하고 NOT : ((num & 0xff) >> 8) | (num << 8), 잘못된 예제는 하위 바이트에서 0을 얻습니다.
jscom

@IlianZapryanov 명확성을 위해 +1 일지 모르지만 C에서 비트 필드를 사용하는 것이 아마도 가장 효율적인 방법 일 것입니다.
sherrellbc

104

포함함으로써 :

#include <byteswap.h>

최적화 된 버전의 시스템 종속 바이트 스왑 기능을 얻을 수 있습니다. 그러면 다음 기능을 쉽게 사용할 수 있습니다.

__bswap_32 (uint32_t input)

또는

__bswap_16 (uint16_t input)

3
답변 해 주셔서 감사합니다.하지만 라이브러리 기능을 사용할 수 없습니다
Mark Ransom

4
읽어야 #include <byteswap.h>합니다. .h 파일 자체의 주석을 참조하십시오. 이 게시물에는 유용한 정보가 포함되어 있으므로 저자가 lib 함수를 사용하지 않는 OP 요구 사항을 무시 했음에도 불구하고 찬성했습니다.
Eli Rosencruft

30
사실, __bswap_32 / __ bswap_16 함수는 사실 라이브러리 함수가 아니라 매크로이므로 찬성 투표를해야하는 또 다른 이유입니다.
Eli Rosencruft

7
내 이해는이 헤더가 모든 아키텍처의 모든 운영 체제에 대해 존재한다고 보장되지 않는다는 것입니다. 엔디안 문제를 처리 할 수있는 휴대용 방법을 아직 찾지 못했습니다.
Edward Falk

2
Windows에는 존재하지 않습니다. 최소한 Linux에서 mingw 32 또는 64 비트를 사용하여 크로스 컴파일 할 때는 없습니다
bph

61
#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_tint64_t변형의 마스크 뒤에 추론 무엇 ... & 0xFFFF... & 0xFFFFFFFFULL? 여기에 부호 확장과 관련된 문제가 있습니까? 또한 왜 swap_int64돌아 오나요 uint64_t? 그럴까요 int64_t?
bgoodr

1
uint64를 반환하는 swap_int64는 실제로 오류입니다. 부호있는 int 값으로 마스킹하는 것은 실제로 부호를 제거하는 것입니다. 오른쪽으로 이동하면 왼쪽에 부호 비트가 삽입됩니다. 서명되지 않은 int 스와핑 작업을 호출하여이를 방지 할 수 있습니다.
chmike

감사. swap_int64답변에서 반환 값의 유형을 변경할 수 있습니다 . 도움이되는 답변에 +1, BTW!
bgoodr

비트 및 값 엔디안이 종속적입니까?
MarcusJ 2015 년

1
LL불필요한 있습니다 (u)swap_uint64()이처럼 많이 L필요하지 않습니다 (u)swap_uint32(). 은 U필요하지 않습니다 uswap_uint64()이처럼 많이 U필요하지 않습니다uswap_uint32()
chux - 분석 재개 모니카

13

다음은 상당히 일반적인 버전입니다. 컴파일하지 않았으므로 오타가있을 수 있지만 아이디어를 얻어야합니다.

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 ()를 추가했습니다.


1
더 나은 성능을 위해 xorSwap을 사용할 수 있습니다. ... 모든 크기의 특정 것들 위의 일반 버전을 선호

나는 그것을 테스트했는데 이것이 x86에서 xorSwap ...보다 빠릅니다. stackoverflow.com/questions/3128095/...

1
@nus-매우 간단한 코드의 장점 중 하나는 컴파일러 옵티마이 저가 때때로 매우 빠르게 만들 수 있다는 것입니다.
Michael J

@MichaelJ OTOH, chmike의 답변에서 위의 32 비트 버전 bswap은 최적화가 활성화 된 괜찮은 X86 컴파일러에 의해 단일 명령어로 컴파일됩니다 . 크기에 대한 매개 변수가있는이 버전은 그렇게 할 수 없습니다.
Alnitak

@Alnitak-내가 말했듯이, 나는 내 코드를 최적화하기 위해 노력하지 않았다. 사용자 nus가 코드가 매우 빠르게 실행된다는 것을 알았을 때 (한 경우) 단순한 코드가 종종 컴파일러에 의해 고도로 최적화 될 수 있다는 일반적인 아이디어를 언급했습니다. 내 코드는 다양한 경우에서 작동하며 이해하기 쉽고 디버깅하기 쉽습니다. 그것은 내 목표를 충족했습니다.
Michael J

9

매크로가 필요한 경우 (예 : 임베디드 시스템) :

#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))

이러한 매크로는 괜찮지 만 부호있는 정수가 0x80000000과 0xffffffff 사이이면 ((x) >> 24) 실패합니다. 여기서 비트 AND를 사용하는 것이 좋습니다. 참고 : ((x) << 24)는 완벽하게 안전합니다. (x) >> 8) 상위 16 비트가 0이 아니면 (또는 부호있는 16 비트 값이 제공됨) 실패합니다.

2
@ PacMan--이 매크로는 부호없는 정수만 교체하는 데 사용 됩니다. 그것이 UINT그들의 이름에 있는 이유 입니다.
kol

예, 사실입니다. 소음이있어서 죄송합니다. typecast를 포함하는 것이 가장 좋지 않습니까?

5

편집 : 라이브러리 기능입니다. 그것들을 따르는 것은 그것을 수행하는 수동 방법입니다.

__byteswap_ushort, __byteswap_ulong 및 __byteswap_uint64를 알지 못하는 사람들의 수에 절대적으로 놀랐습니다 . 물론 그들은 Visual C ++에 특화되어 있지만 x86 / IA-64 아키텍처에서 맛있는 코드로 컴파일됩니다. :)

다음 은이 페이지에서 가져온bswap 지침 의 명시적인 사용법입니다 . 위의 내장 형식은 항상 이것보다 빠르다 는 점에 유의하십시오 . 라이브러리 루틴없이 답변을 제공하기 위해 추가했습니다.

uint32 cq_ntohl(uint32 a) {
    __asm{
        mov eax, a;
        bswap eax; 
    }
}

21
C 질문의 경우 Visual C ++에 특정한 것을 제안하고 있습니까?
Alok Singhal

3
@Alok : Visual C ++는 Microsoft의 제품입니다. C 코드를 컴파일하는 데 잘 작동합니다. :)
Sam Harwell

20
많은 사람들이 Microsoft 고유의 바이트 스와핑 구현을 인식하지 못하는 이유가 무엇입니까?
dreamlax 2010

36
멋지다. 이식 가능하거나 표준을 준수 할 필요가없는 비공개 소스 제품을 개발하는 모든 사람에게 좋은 정보입니다.
Sam Post

6
@Alok, OP는 컴파일러 | OS를 언급하지 않았습니다. 특정 도구 세트에 대한 경험에 따라 사람이 대답을 할 수 있습니다.
Aniket Inge 2012

5

농담으로 :


#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;
}

7
하하 하하하. 하하하. 하아. 하아? (어떤 농담?)

3
일부 Windows 소스 저장소에서 가져 왔습니까? :)
hochl

Nodejs는이 기술을 사용합니다! github.com/nodejs/node/blob/...
저스틴 모저

int i, size_t sizeofInt두 가지 모두에 대해 동일한 유형이 아닌 사용이 궁금 합니다.
chux - 분석 재개 모니카

5

다음은 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;
}

3

이 작업이 더 빠를까요?

 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];

2
제 생각에는 당신이 char아니라고 생각합니다 byte.
dreamlax 2010-08-04

이 전략을 사용하면 귀하와 비교하여 가장 많은 표를 얻은 솔루션이 동등하고 가장 효율적이고 이식성이 있습니다. 그러나 내가 제안한 솔루션 (두 번째로 많은 투표)은 더 적은 작업이 필요하고 더 효율적이어야합니다.
chmike

1

다음은 내가 사용해온 함수입니다. 모든 기본 데이터 유형에서 테스트되고 작동합니다.

//  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;
    }

}

2
코드는 매우 합리적인 가정에 의존합니다. source필요에 따라 정렬되지만 그 가정이 유지되지 않으면 코드는 UB입니다.
chux - 분석 재개 모니카

1

편집 :이 기능은 정렬 된 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 파일의 엔디안을 변경하는 데 유용합니다.


CHAR_BIT #define은 코드를 완성하기 위해 누락되었습니다.
Tõnu Samuel 2013

좋아, 누락 된 포함을 추가했습니다.
Patrick Schlüter 2013

여기에 C의 스왑에 대한 링크, ++ I 돈이다 t know if it: s의 빠른 속도가 wokrs 제안으로하지만 github.com/heatblazer/helpers/blob/master/utils.h
Ilian Zapryanov

CHAR_BIT에 의존하는 대신 8호기심이 많습니다 . 참고 상수에 필요하지 않습니다. 0xFF00FF00FF00FF00ULLCHAR_BIT == 8LL
chux - 분석 재개 모니카

당신 말이 맞아요. 해당 CHAR_BIT매크로의 노출을 늘리기 위해 로만 썼습니다 . LL의 경우 다른 어떤 것보다 주석입니다. 또한 옳은 일을하지 않는 버그가있는 컴파일러 (사전 표준)로 오래 전에 잡았던 습관이기도합니다.
Patrick Schlüter 2018

1

이 코드 조각은 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);    
}

감사합니다 @YuHao 저는 여기에 새로 왔으며 텍스트 서식을 지정하는 방법을 모릅니다.
Kaushal Billore 2013

2
((i>>24)&0xff) | ((i>>8)&0xff00) | ((i&0xff00)<<8) | (i<<24);일부 플랫폼에서는 사용 이 더 빠를 수 있습니다 (예 : AND 마스크 상수 재활용). 하지만 대부분의 컴파일러는이 작업을 수행하지만 일부 간단한 컴파일러는이를 최적화 할 수 없습니다.

-7

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);

컴파일러가 이것이 바이트 레벨 조작임을 인식하고 바이트 스와핑 코드를 생성하지 않는 한 이것은 가장 효율적인 솔루션이 아닙니다. 그러나 메모리 레이아웃 트릭에 의존하지 않으며 매우 쉽게 매크로로 변환 할 수 있습니다.


25
x86 및 x86_64 아키텍처에서는 little endian 체계가 기본 체계입니다.
MK aka Grisu 2014
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.