【问题标题】:Bit swapping and pointer位交换和指针
【发布时间】:2012-10-14 07:29:18
【问题描述】:

我有一个struct,它由多个 32 位元素组成。我应用了#pragma pack (4),因此以下结构是线性且对齐的。

struct
{
  int a; // 4 bytes
  int b; // 4 bytes
  int c; // 4 bytes
} mystruct; // total 16 bytes

如何交换这些元素中的每一个(小端 -> 大端)?

方法是void swap(void* a, int b);a指向结构体,b整数给出结构体的大小。

例如:

void swap(void* a, int b)
{
  //FIXME !
  for (int i = 0; i < b; i+= 32)
  {
    a = (a & 0x0000FFFF) << 16 | (a & 0xFFFF0000) >> 16;
    a = (a & 0x00FF00FF) << 8 | (a & 0xFF00FF00) >> 8;
    a += 32;
  }
}

【问题讨论】:

  • 您的元素 d 不是四个字节,而是一个字节。通过使用#pragma pack(4),您已强制编译器在元素 d 之后添加三个填充字节,但您的代码访问这些字节仍然是错误的。如果你真的想要四个字节,char d[4]; 有什么问题?
  • @john 帖子已编辑,这不是我要找的。现在假设结构仅由 3 个 int 组成。
  • 顺便说一句,您当前的编译指示可能已经过时,因为一个充满ints 的结构至少与int 的对齐对齐,这很可能至少有4 个字节。另一方面,我不明白你为什么假设结构与 16 个字节对齐(除非你认为 #pragma pack 考虑元素而不是字节,它当然不会)。
  • 你的意思是字节交换而不是位交换。字节序改变是字节交换,而不是位。
  • @ChristianRau 结构中的int 不完整,我清理了示例以使其更易于理解。当“对齐”时,我的意思是结构的元素在内存中相互跟随

标签: c++ byte swap endianness


【解决方案1】:
void swap(void* A, int b)
{
   //FIXME !
   int *c = (int*)A; // have to cast to an existing type. 'void' doesnt exist
   for (int i = 0; i < b; i+=4)  // we are flipping 4 bytes at a time
   {
        unsigned int a=*c;  // read the content, not the address
         // have to have unsigned, otherwise (a&0xff00ff00)>>8 extends the sign bit
        a = (a & 0x0000FFFF) << 16 | (a & 0xFFFF0000) >> 16;
        a = (a & 0x00FF00FF) << 8 | (a & 0xFF00FF00) >> 8;
        *c++= a;  // write back the content
   }
 }

无论如何:我最喜欢在 c 或类 c 语言中进行字节交换的方法是通过副本: 这个概念比内置函数的名称更容易记住。

void swap(char *d, char *s, int N) { for (i=0;i&lt;N;i++) d[i^3]=*s++;}

【讨论】:

  • 此代码假定大小 b 是整数大小。可能是对的,但我认为它更可能是字节大小。
【解决方案2】:

我假设 little->big endian 的意思是颠倒每个 4 字节元素中的字节顺序。我还假设“结构上的a 空指针”是指“结构上的a 指针”。 您的代码中有一些错误。试试这个:

void swap(void* a, int b)
{
  for (int *pInt = (int *)a; pInt < ((char *)a + b); pInt++)
  {
    *pInt = (*pInt & 0x0000FFFF) << 16 | (*pInt  & 0xFFFF0000) >> 16;
    *pInt = (*pInt & 0x00FF00FF) << 8 | (*pInt & 0xFF00FF00) >> 8;
  }
}

【讨论】:

    【解决方案3】:

    你可以不使用临时交换两个字节:

    void byteswap( unsigned char & a, unsigned char & b )
    {
       a ^= b;
       b ^= a;
       a ^= b;
    }
    

    现在让我们将它应用于可变长度的数字

    template< typename T >
    void endianSwap( T & t )
    {
        unsigned char * first = reinterpret_cast< unsigned char * >( &t );
        unsigned char * last = first + sizeof(T) - 1;
        while( first < last )
        {
           byteswap( *first, *last );
           ++first;
           --last;
        }
    }
    

    对于您的结构,您可以:

    void endianSwap( mystruct & s )
    {
         endianSwap( s.a );
         endianSwap( s.b );
         endianSwap( s.c );
    }
    

    当然,作为使用 byteswap 的 endianSwap 的替代方案,我们可以只使用 std::reverse。

    template<typename T> endianSwap( T& t )
    {
        unsigned char * begin = reinterpret_cast<unsigned char *>(&t);
        unsigned char * end = begin + sizeof( T );
        std::reverse( begin, end );
    }
    

    【讨论】:

      【解决方案4】:

      如果您使用的是基于 *nix 的机器。有一个有用的功能可以做到这一点:

      #include <arpa/inet.h>
      uint32_t htonl(uint32_t hostint32);   // from host to network
                                            // means little to big if your machine is x86
      

      【讨论】:

      • 如果你想换成大端。但是,如果您想从大到小交换,则该功能将不起作用。
      • 那又怎样?还有ntohl
      • @Patouf 基本上是的。简称还有另一个版本。
      • 是的,但是如果您在大端平台上,则无法切换到小端。当然,您可以将其用作小端系统的实现。
      【解决方案5】:

      这是您日常工作的可能解决方案

      void swap(void* a, int b)
      {
        for (int i = 0; i < b; i += 4)
        {
          int* p = (char*)a + i;
          *p = (*p & 0x0000FFFF) << 16 | (*p & 0xFFFF0000) >> 16;
          *p = (*p & 0x00FF00FF) << 8 | (*p & 0xFF00FF00) >> 8;
        }
      }
      

      未经测试,但我希望更接近正确。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-05-27
        • 2021-11-21
        • 2014-08-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多