【问题标题】:C 40bit byte swap (endian)C 40 位字节交换(字节序)
【发布时间】:2016-06-28 17:01:39
【问题描述】:

我正在使用来自字节交换的 C 和 bswap_{16,32,64} 宏从 big-endian 读取/写入 little-endian 格式的二进制文件。 h 用于字节交换。

所有值都可以正确读取和写入,除了 40 位的位域。

bswap_40 宏不存在,我不知道该怎么做,也不知道是否有更好的解决方案。

这是一个显示此问题的小代码:

#include <stdio.h>
#include <inttypes.h>
#include <byteswap.h>

#define bswap_40(x) bswap_64(x)

struct tIndex {
  uint64_t val_64;
  uint64_t val_40:40;
} s1 = { 5294967296, 5294967296 };

int main(void)
{
    // write swapped values
    struct tIndex s2 = { bswap_64(s1.val_64), bswap_40(s1.val_40) };
    FILE *fp = fopen("index.bin", "w");
    fwrite(&s2, sizeof(s2), 1, fp);
    fclose(fp);

    // read swapped values
    struct tIndex s3;
    fp = fopen("index.bin", "r");
    fread(&s3, sizeof(s3), 1, fp);
    fclose(fp);
    s3.val_64 = bswap_64(s3.val_64);
    s3.val_40 = bswap_40(s3.val_40);

    printf("val_64: %" PRIu64 " -> %s\n", s3.val_64, (s1.val_64 == s3.val_64 ? "OK" : "Error"));
    printf("val_40: %" PRIu64 " -> %s\n", s3.val_40, (s1.val_40 == s3.val_40 ? "OK" : "Error"));

    return 0;
}

该代码是用以下方式编译的:

gcc -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE swap_40.c -o swap_40

如何定义 bswap_40 宏来读写这些 40 位的值进行字节交换?

【问题讨论】:

    标签: c swap endianness bit-fields


    【解决方案1】:

    通过将 bswap_40 定义为与 bswap_64 相同,您将交换 8 个字节而不是 5 个。因此,如果您从这个开始:

    00 00 00 01 02 03 04 05
    

    你会得到这个:

    05 04 03 02 01 00 00 00
    

    而不是这个:

    00 00 00 05 04 03 02 01
    

    处理此问题的最简单方法是获取 bswap_64 的结果并将其右移 24:

    #define bswap_40(x) (bswap_64(x) >> 24)
    

    【讨论】:

      【解决方案2】:

      编辑

      我在编写这个宏时获得了更好的性能(与我的初始代码相比,这产生了更少的汇编指令):

      #define bswap40(s)                                                \
        ((((s)&0xFF) << 32) | (((s)&0xFF00) << 16) | (((s)&0xFF0000)) | \
         (((s)&0xFF000000) >> 16) | (((s)&0xFF00000000) >> 32))
      

      使用:

      s3.val_40 = bswap40(s3.val_40);
      

      ...但这可能是优化器问题。我认为它们应该针对相同的东西进行优化。

      原帖

      我更喜欢 dbush 的回答……我正要写这个:

      static inline void bswap40(void* s) {
        uint8_t* bytes = s;
        bytes[0] ^= bytes[3];
        bytes[1] ^= bytes[2];
        bytes[3] ^= bytes[0];
        bytes[2] ^= bytes[1];
        bytes[0] ^= bytes[3];
        bytes[1] ^= bytes[2];
      }
      

      这是一个用于切换字节的破坏性内联函数...

      【讨论】:

      • 谢谢,优化版很有趣。
      【解决方案3】:

      我正在使用 C 和来自 byteswap.h 的 bswap_{16,32,64} 宏进行字节交换,从 big-endian 读取/写入 little-endian 格式的二进制文件。

      提出解决此问题的不同方法:更多情况下,代码需要读取已知字节序格式的文件,然后转换为代码的字节序。这可能涉及字节交换,它可能不是诀窍是编写在所有条件下都能工作的代码。

       unsigned char file_data[5];
       // file data is in big endidan
       fread(file_data, sizeof file_data, 1, fp);
      
       uint64_t y = 0;
       for (i=0; i<sizeof file_data; i++) {
         y <<= 8;
         y |= file_data[i];
       }
      
       printf("val_64: %" PRIu64 "\n", y);
      

      uint64_t val_40:40; 不可移植。 intsigned intunsigned 以外的类型的位范围不可移植并且具有实现指定的行为。

      顺便说一句:以二进制模式打开文件:

      // FILE *fp = fopen("index.bin", "w");
      FILE *fp = fopen("index.bin", "wb");
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-07
        • 1970-01-01
        • 2019-07-05
        • 2020-05-22
        • 1970-01-01
        相关资源
        最近更新 更多