【问题标题】:How to reverse endianness?如何反转字节顺序?
【发布时间】:2021-11-17 01:23:22
【问题描述】:

谁能帮我理解这段代码?

int reverse_endianess(int value) {
    int resultat = 0;
    char *source, *destination;
    int i;

    source = (char *) &value;
    destination = ((char *) &resultat) + sizeof(int);
    for (i = 0; i < sizeof(int); i++)
        *(--destination) = *(source++);
    return resultat;
}

这部分代码我看不懂:

destination = ((char *) &resultat) + sizeof(int);
for (i = 0; i < sizeof(int); i++)
    *(--destination) = *(source++);

【问题讨论】:

  • 这一行 *(--destination) = *(source++); 正在改变 value 的字节顺序,即字节序
  • 你可以使用htonl
  • @kichik 它只会在字节序与网络顺序相同的系统上“反转”。这不适用于 BE 系统
  • 虽然代码看起来正确,答案也不错,但我还是想补充一点:最好使用按位运算&lt;&lt;&gt;&gt;|&amp; 而不是这种方法。与类型转换相比,它们留下的问题更少(参见答案中提到的严格别名),并且倾向于更好地优化(更有可能在单个 cpu 指令中编译)。
  • 这不是一种有效的方法。纯算术一生成最好的代码,如我的回答

标签: c endianness


【解决方案1】:

以下原因导致destination 指向resultat 后面的字节(只要resultatint):

destination = ((char *) &resultat) + sizeof(int);

也可以这样写:

destination = (char *)(&resultat + 1);

以下只是一个简单的内存复制循环:

for (i = 0; i < sizeof(int); i++)
        *(--destination) = *(source++);

相当于如下:

for (i = 0; i < sizeof(int); i++) {
   --destination;             // Point to the one byte earlier.
   *destination = *source;    // Copy one byte.
   source++;                  // Point to one byte later.
}

程序流程(假设 32 位 int 和 8 位 char

设置后:

source               value
+----------+         +---+---+---+---+
|        -------+    | a | b | c | d |
+----------+    |    +---+---+---+---+
                |      ^
                +------+

destination          resultat 
+----------+         +---+---+---+---+
|        -------+    | 0 | 0 | 0 | 0 |
+----------+    |    +---+---+---+---+
                |                      ^
                +----------------------+

循环一次后:

source               value
+----------+         +---+---+---+---+
|        -------+    | a | b | c | d |
+----------+    |    +---+---+---+---+
                |          ^
                +----------+

destination          resultat 
+----------+         +---+---+---+---+
|        -------+    | 0 | 0 | 0 | a |
+----------+    |    +---+---+---+---+
                |                  ^
                +------------------+

完成后:

source               value
+----------+         +---+---+---+---+
|        -------+    | a | b | c | d |
+----------+    |    +---+---+---+---+
                |                      ^
                +----------------------+

destination          resultat 
+----------+         +---+---+---+---+
|        -------+    | d | c | b | a |
+----------+    |    +---+---+---+---+
                |      ^
                +------+

【讨论】:

  • 不是写作 int as char * 违反严格别名吗?我的意思是使用int 类型访问写为char 数组的东西。
  • @Eugene Sh.你会更清楚。如果是,则表示此代码不可移植。
  • 我认为是,对我来说有点没有意义。
  • @EugeneSh.: 代码没有将int 写成char *。它写成char;使用的左值是char,而不是char *。 C 2018 6.5 7 中的别名规则明确允许这样做;它说“一个对象的存储值只能由具有以下类型之一的左值表达式访问:……字符类型。” C标准中的“访问”是指读或写(C 2018 3.1:“访问(动词)读取或修改对象的值”)。
  • @EricPostpischil destination 写为 char 数组,但稍后通过 resultat 访问为 int。这就是我的意思可能的问题所在。
【解决方案2】:

让我们说sizeof(int) = 4 所以 32 位 int。 一个 char 大小为 1,1 个字节。 一个普通的 int* 看起来像这样:

aabbccdd // the int in hexadecimal
^ pointer points to start

如果我们将其转换为 char*,我们会得到 aa。这就是source 所做的。

如果我们现在添加 sizeof 4,我们会向右跳转 4 个字节:

aabbccdd?? 
                  ^

我们现在比值落后一个字节,访问它可能会导致程序段错误或只是读取垃圾。由于使用--destination 而不是destination--,这不会发生。它首先递减。 现在我们只是从前面读取传入的整数,而从后面写入:

a1b2c3d4 // original int
->

d4c3b2a1 // destination
              <-

注意两个十六进制数字是一个字节,这就是为什么我们没有得到 4d3c2b1a。我们以正确的方式保留字节,但将第一个字节放在最后。

【讨论】:

  • 谢谢,这很有帮助:-)
【解决方案3】:

三种不同的方法。第一个在具有字节反转指令的系统上效率最高。

#define SWAPUC(a,b) do{unsigned char temp = (a); (a) = (b); (b) = temp;}while(0)

int reverse(int i)
{
    unsigned int val = i;

    if(sizeof(val) == 4)
        val = ((val & 0xff) << 24) | ((val & 0xff00) << 8) | ((val & 0xff0000) >> 8) | ((val & 0xff000000) >> 24);
    if(sizeof(val) == 8)
        val = ((val & 0x00000000000000ffULL) << 56) | ((val & 0xff00000000000000ULL) >> 56) |
              ((val & 0x000000000000ff00ULL) << 40) | ((val & 0x00ff000000000000ULL) >> 40) |
              ((val & 0x0000000000ff0000ULL) << 24) | ((val & 0x0000ff0000000000ULL) >> 24) |
              ((val & 0x00000000ff000000ULL) <<  8) | ((val & 0x000000ff00000000ULL) >> 8);
    
    return val;
}

int  reverse1(int val)
{
    union
    {
        unsigned i;
        unsigned char uc[sizeof(val)];
    }uni = {.i = val};

    if(sizeof(val) == 8)
    {
        SWAPUC(uni.uc[7], uni.uc[0]);
        SWAPUC(uni.uc[6], uni.uc[1]);
        SWAPUC(uni.uc[5], uni.uc[2]);
        SWAPUC(uni.uc[4], uni.uc[3]);
    }
    if(sizeof(val) == 4)
    {
        SWAPUC(uni.uc[3], uni.uc[0]);
        SWAPUC(uni.uc[2], uni.uc[1]);
    }
    return uni.i;
}

int reverse2(int val)
{
    unsigned char uc[sizeof(val)];
    memcpy(uc, &val, sizeof(uc));
    if(sizeof(val) == 8)
    {
        SWAPUC(uc[7], uc[0]);
        SWAPUC(uc[6], uc[1]);
        SWAPUC(uc[5], uc[2]);
        SWAPUC(uc[4], uc[3]);
    }
    if(sizeof(val) == 4)
    {
        SWAPUC(uc[3], uc[0]);
        SWAPUC(uc[2], uc[1]);
    }
    memcpy(&val, uc, sizeof(uc));
    return val;
}


int main(void)
{
    printf("%x\n", reverse2(0xaabbccdd));
}

生成的代码(x86):

reverse:
        mov     eax, edi
        bswap   eax
        ret
reverse1:
        mov     eax, edi
        xor     edx, edx
        mov     ecx, edi
        shr     eax, 24
        movzx   esi, ch
        sal     ecx, 24
        mov     dl, al
        mov     eax, edi
        sal     esi, 16
        shr     eax, 16
        mov     dh, al
        movzx   eax, dx
        or      eax, esi
        or      eax, ecx
        ret
reverse2:
        mov     eax, edi
        xor     edx, edx
        mov     ecx, edi
        shr     eax, 24
        movzx   esi, ch
        sal     ecx, 24
        mov     dl, al
        mov     eax, edi
        sal     esi, 16
        shr     eax, 16
        mov     dh, al
        movzx   eax, dx
        or      eax, esi
        or      eax, ecx
        ret
.LC0:
        .string "%x\n"

或者cortex M4(这个有字节交换指令)

reverse:
        rev     r0, r0
        bx      lr
reverse1:
        mov     r3, r0
        lsrs    r2, r3, #24
        movs    r0, #0
        bfi     r0, r2, #0, #8
        ubfx    r2, r3, #16, #8
        bfi     r0, r2, #8, #8
        ubfx    r2, r3, #8, #8
        bfi     r0, r2, #16, #8
        bfi     r0, r3, #24, #8
        bx      lr
reverse2:
        mov     r3, r0
        lsrs    r2, r3, #24
        movs    r0, #0
        bfi     r0, r2, #0, #8
        ubfx    r2, r3, #16, #8
        bfi     r0, r2, #8, #8
        ubfx    r2, r3, #8, #8
        bfi     r0, r2, #16, #8
        bfi     r0, r3, #24, #8
        bx      lr
.LC0:

所以获胜者是第一个仅使用按位算术的函数。

【讨论】:

    【解决方案4】:

    我用了很长时间。
    data 是指向要反转的值的指针
    n 是要反转的字符数;通常是 2、4、8 表示 short、int、long long。
    但这在各种架构/操作系统上可能有所不同

    void SwapEndianN(char *data, unsigned short n) {
      unsigned short k; char c;
      for ( k=0 ; k < (n/2) ;k++ ) {
        c = *(data+((n-1)-k));
        *(data+((n-1)-k)) = *(data+k);
        *(data+k) = c;
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多