你如何编写(便携)反向网络字节顺序?

Pro*_*ica 10 c c++ portability endianness

背景

在设计二进制文件格式时,通常建议以网络字节顺序写入整数.为此,有像宏一样的宏htonhl().但对于像WAV这样的格式,实际上使用的是小端格式.

无论您的代码运行的CPU是大端还是小端架构,您如何便携地编写小端值?(想法:可以使用标准宏ntohl()htonl()以某种方式"反向"使用吗?或者代码只是测试运行时是否在小端或大端CPU上运行并选择适当的代码路径?)

所以问题不在于文件格式,文件格式只是一个例子.它可以是任何类型的序列化,其中需要"线上"的小端,例如(异端)网络协议.

R. *_*des 19

C已经提供了对主机字节序的抽象:数字 †或int †.

以给定的字节顺序生成输出可以通过不尝试聪明来完成:只需将数字解释为数字并使用位移来提取每个字节:

uint32_t value;
uint8_t lolo = (value >> 0) & 0xFF;
uint8_t lohi = (value >> 8) & 0xFF;
uint8_t hilo = (value >> 16) & 0xFF;
uint8_t hihi = (value >> 24) & 0xFF;
Run Code Online (Sandbox Code Playgroud)

然后你只需按你想要的顺序写字节.

当您将带有某些字节顺序的字节序列作为输入时,您可以通过再次使用位操作构造数字来在主机的字节顺序中重构它们:

uint32_t value = (hihi << 24)
               | (hilo << 16)
               | (lohi << 8)
               | (lolo << 0);
Run Code Online (Sandbox Code Playgroud)

†只有作为字节序列的数字表示具有字节顺序; 数字(即数量)没有.

  • `hihi`,`lolo`,这些是我见过的最好的名字.你是什​​么意思数量? (2认同)
  • 它基本上意味着C++使得知道主机的字节序无关紧要.您不需要检测它,您只需要按所需顺序写出字节. (2认同)

did*_*erc 5

这是一个基于模板的版本:

#include <iostream>
#include <iomanip>

enum endianness_t {
   BIG,         // 0x44332211  => 0x44 0x33 0x22 0x11
   LITTLE,      // 0x44332211  => 0x11 0x22 0x33 0x44
  UNKNOWN
};

const uint32_t test_value    = 0x44332211;
const bool is_little_endian  = (((char *)&test_value)[0] == 0x11) && (((char *)&test_value)[1] == 0x22);
const bool is_big_endian     = (((char *)&test_value)[0] == 0x44) && (((char *)&test_value)[1] == 0x33);

const endianness_t endianness = 
   is_big_endian ? BIG: 
  (is_little_endian ? LITTLE : UNKNOWN);


template <typename T>
T identity(T v){
  return v;
}

// 16 bits values ------

uint16_t swap_(uint16_t v){
  return ((v & 0xFF) << 8) | ((v & 0xFF00) >> 8);
}

// 32 bits values ------

uint32_t swap_(uint32_t v){
  return ((v & 0xFF) << 24) | ((v & 0xFF00) << 8) | ((v & 0xFF0000) >> 8) | ((v & 0xFF000000) >> 24);
}

template <typename T, endianness_t HOST, endianness_t REMOTE>
 struct en_swap{
  static T conv(T v){
    return swap_(v);
  }
};

template <typename T>
struct en_swap<T, BIG, BIG>{
  static T conv(T v){
    return v;
  }
};

template <typename T>
struct en_swap<T, LITTLE, LITTLE> {
  static T conv(T v){
    return v;
  }
};

template <typename T>
T to_big(T v) {

  switch (endianness){
  case LITTLE :
    return en_swap<T,LITTLE,BIG>::conv(v);
  case BIG :
    return en_swap<T,BIG,BIG>::conv(v);
  }
}

template <typename T>
T to_little(T v) {
   switch (endianness){
   case LITTLE :
     return en_swap<T,LITTLE,LITTLE>::conv(v);
   case BIG :
     return en_swap<T,BIG,LITTLE>::conv(v);
  }
}


int main(){

  using namespace std;

  uint32_t x = 0x0ABCDEF0;
  uint32_t y = to_big(x);
  uint32_t z = to_little(x);

  cout << hex << setw(8) << setfill('0') << x << " " << y << " " << setw(8) << setfill('0') << z << endl;

}
Run Code Online (Sandbox Code Playgroud)