首页 文章

用于将big endian转换为little endian的C / C代码

提问于
浏览
0

我曾经遇到过一段似乎有用的代码,但是我很难过为什么会这么做 .

基本上,有一个char缓冲区,在某个位置,包含一个4字节的int存储为big-endian . 代码将提取整数并将其存储为本机小端 . 这是一个简短的例子:

char test[8] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
char *ptr = test;
int32_t value = 0;
value =  ((*ptr) & 0xFF)       << 24;
value |= ((*(ptr + 1)) & 0xFF) << 16;
value |= ((*(ptr + 2)) & 0xFF) << 8;
value |= (*(ptr + 3)) & 0xFF;
printf("value: %d\n", value);

Value :66051

上面的代码占用前四个字节,将其存储为小端,并打印结果 . 任何人都可以一步一步解释这是如何工作的?我很困惑为什么((* ptr)&0xFF)<< X不会仅仅为任何X> = 8评估为0 .

6 回答

  • 2

    我很困惑为什么((* ptr)&0xFF)<< X对于任何X> = 8都不会评估为0 .

    我认为你误解了转换功能 .

    value = ((*ptr) & 0xFF) << 24;
    

    表示使用0xff(字节)屏蔽ptr处的值,然后移位24位(不是字节) . 这是一个24/8字节(3字节)到最高字节的转换 .

  • 1

    我写了下面的代码 . 此代码包含两个函数 swapmem()swap64() .

    • swapmem() 交换任意维度的内存区域的字节 .

    • swap64() 交换64位整数的字节 .

    在本回复结束时,我指出了一个用字节缓冲区解决问题的想法 .

    这里的代码:

    #include <stdio.h>
    #include <stdint.h>
    #include <inttypes.h>
    #include <malloc.h>
    
    void * swapmem(void *x, size_t len, int retnew);
    uint64_t swap64(uint64_t k);
    
    /**
        brief swapmem
    
             This function swaps the byte into a memory buffer.
    
        param x
             pointer to the buffer to be swapped
    
        param len
             lenght to the buffer to be swapped
    
        param retnew
             If this parameter is 1 the buffer is swapped in a new
             buffer. The new buffer shall be deallocated by using
             free() when it's no longer useful.
    
             If this parameter is 0 the buffer is swapped in its
             memory area.
    
        return
            The pointer to the memory area where the bytes has been
            swapped or NULL if an error occurs.
    */
    void * swapmem(void *x, size_t len, int retnew)
    {
        char *b = NULL, app;
        size_t i;
    
        if (x != NULL) {
            if (retnew) {
                b = malloc(len);
                if (b!=NULL) {
                    for(i=0;i<len;i++) {
                        b[i]=*((char *)x+len-1-i);
                    }
                }
            } else {
                b=(char *)x;
                for(i=0;i<len/2;i++) {
                    app=b[i];
                    b[i]=b[len-1-i];
                    b[len-1-i]=app;
                }
            }
        }
        return b;
    }
    
    uint64_t swap64(uint64_t k)
    {
        return ((k << 56) |
                ((k & 0x000000000000FF00) << 40) |
                ((k & 0x0000000000FF0000) << 24) |
                ((k & 0x00000000FF000000) << 8) |
                ((k & 0x000000FF00000000) >> 8) |
                ((k & 0x0000FF0000000000) >> 24)|
                ((k & 0x00FF000000000000) >> 40)|
                (k >> 56)
               );
    }
    
    int main(void)
    {
        uint32_t x,*y;
        uint16_t s,z;
        uint64_t k,t;
    
        x=0xFF567890;
    
        /* Dynamic allocation is used to avoid to change the contents of x */
        y=(uint32_t *)swapmem(&x,sizeof(x),1);
        if (y!=NULL) {
            printf("LE=%08X BE=%08X\n",x,*y);
            free(y);
        }
    
        /* Dynamic allocation is not used. The contents of z and k will change */
        z=s=0x7891;
        swapmem(&z,sizeof(z),0);
        printf("LE=%04X BE=%04X\n",s,z);
    
        k=t=0x1120324351657389;
        swapmem(&k,sizeof(k),0);
        printf("LE=%16"PRIX64" BE=%16"PRIX64"\n",t,k);
    
        /* LE64 to BE64 (or viceversa) using shift */
        k=swap64(t);
        printf("LE=%16"PRIX64" BE=%16"PRIX64"\n",t,k);
    
        return 0;
    }
    

    编译程序后,我有好奇心看到汇编代码gcc生成 . 我发现函数swap64的生成如下所示 .

    00000000004007a0 <swap64>:
      4007a0:       48 89 f8                mov    %rdi,%rax
      4007a3:       48 0f c8                bswap  %rax
      4007a6:       c3                      retq
    

    这个结果是在具有Intel I3 CPU的PC上使用gcc选项编译代码:-Ofast,或-O3,或-O2或-Os .

    您可以使用 swap64() 函数之类的东西来解决您的问题 . 像下面的函数我命名为 swap32()

    uint32_t swap32(uint32_t k)
    {
        return ((k << 24) |
                ((k & 0x0000FF00) << 8) |
                ((k & 0x00FF0000) >> 8) |
                (k >> 24)
               );
    }
    

    您可以将其用作:

    uint32_t j=swap32(*(uint32_t *)ptr);
    
  • 0

    您可能使用的代码基于以下思想:网络上的数字应以BIG ENDIAN模式发送 .

    函数 htonl()htons() 在BIG ENDIAN中转换32位整数和16位整数,其中您的系统使用LITTLE ENDIAN,否则它们将数字保留为BIG ENDIAN .

    这里的代码:

    #include <stdio.h>
    #include <stdint.h>
    #include <inttypes.h>
    #include <arpa/inet.h>
    
    int main(void)
    {
        uint32_t x,y;
        uint16_t s,z;
    
        x=0xFF567890;
    
        y=htonl(x);
    
        printf("LE=%08X BE=%08X\n",x,y);
    
        s=0x7891;
    
        z=htons(s);
    
        printf("LE=%04X BE=%04X\n",s,z);
    
        return 0;
    
    }
    

    编写此代码是为了在LE机器上从LE转换为BE .

    您可以使用相反的函数 ntohl()ntohs() 从BE转换为LE,这些函数将LE上的整数从BE转换为LE并且不在BE机器上转换 .

  • 0

    如果你想将little endian represantion转换为big endian,你可以使用htonl,htons,ntohl,ntohs . 这些函数在主机和网络字节顺序之间转换值 . Big endian也用于基于arm的平台 . 看到这里:https://linux.die.net/man/3/endian

  • 1

    此代码构造值,一次一个字节 .

    首先,它捕获最低字节

    (*ptr) & 0xFF
    

    然后将其移至最高字节

    ((*ptr) & 0xFF) << 24
    

    然后将其分配给先前的0初始化值 .

    value =((*ptr) & 0xFF) << 24
    

    现在"magic"开始发挥作用了 . 由于 ptr 值被声明为 char* ,因此将指针前移一个字符 .

    (ptr + 1) /* the next character address */
     *(ptr + 1) /* the next character */
    

    在看到他们使用指针数学来更新相对起始地址之后,其余的操作与已经描述的操作相同,只是为了保留部分移位的值,它们将 or 值存入现有的 value 变量

    value |= ((*(ptr + 1)) & 0xFF) << 16
    

    注意,指针数学是你可以做的事情的原因

    char* ptr = ... some value ...
    
     while (*ptr != 0) {
         ... do something ...
         ptr++;
     }
    

    但它的代价是可能真的搞乱你的指针地址,大大增加了你的SEGFAULT违规风险 . 有些语言认为这是一个问题,他们删除了做指针数学的能力 . 几乎不能进行指针数学运算的指针通常称为引用 .

  • 0

    理解 ((*ptr) & 0xFF) << X 评估的关键点之一

    Integer Promotion . 值 (*ptr) & 0xff 在被移位之前被提升为 Integer .

相关问题