C ++:如何将数组中的2个字节转换为unsigned short

时间:2008-11-19 02:05:43

标签: c++ pointers casting

我一直在研究传统的C ++应用程序,我绝对不在我的舒适区域(一件好事)。我想知道是否有人会非常友好地给我一些指示(双关语)。

我需要将unsigned char数组中的2个字节转换为unsigned short。字节是连续的。

关于我想要做的一个例子:

我从套接字接收一个字符串并将其放在unsigned char数组中。我可以忽略第一个字节,然后接下来的2个字节应转换为unsigned char。这将仅在Windows上,因此没有Big / Little Endian问题(我知道)。

这就是我现在所拥有的(显然没有工作):

//packetBuffer is an unsigned char array containing the string "123456789" for testing
//I need to convert bytes 2 and 3 into the short, 2 being the most significant byte
//so I would expect to get 515 (2*256 + 3) instead all the code I have tried gives me
//either errors or 2 (only converting one byte
unsigned short myShort;
myShort = static_cast<unsigned_short>(packetBuffer[1])

11 个答案:

答案 0 :(得分:22)

好吧,你正在将char扩大为一个短值。你想要的是将两个字节解释为short。 static_cast无法从unsigned char*投射到unsigned short*。您必须转为void*,然后转为unsigned short*

unsigned short *p = static_cast<unsigned short*>(static_cast<void*>(&packetBuffer[1]));

现在,您可以取消引用p并获取短值。但是这种方法的问题是你从unsigned char *转换为void *然后转换为某种不同的类型。标准不保证地址保持不变(此外,解除引用该指针将是未定义的行为)。更好的方法是使用位移,这总是有效:

unsigned short p = (packetBuffer[1] << 8) | packetBuffer[2];

答案 1 :(得分:4)

这可能远低于您关心的内容,但请记住,您可以轻松获得未对齐的访问权限。 x86是宽容的,未对齐访问导致的中止将在内部被捕获并最终会复制并返回值,因此您的应用程序将不会知道任何不同(尽管它比对齐访问慢得多)。但是,如果此代码将在非x86上运行(您没有提及目标平台,因此我假设x86桌面Windows),那么这样做会导致处理器数据中止,您将不得不手动复制在尝试投射之前将数据转换为对齐的地址。

简而言之,如果您要对此进行大量访问,您可能会考虑对代码进行调整,以免出现未对齐的读取,并且您将看到一个性能上的好处。

答案 2 :(得分:3)

上面的位移有一个错误:

unsigned short p = (packetBuffer[1] << 8) | packetBuffer[2];

如果packetBuffer以字节为单位(8位宽),则上述移位可以并且会将packetBuffer变为零,只留下packetBuffer[2];

尽管这仍然是指针的首选。为了避免上述问题,我浪费了几行代码(除了非常文字零优化),它会产生相同的机器代码:

unsigned short p;
p = packetBuffer[1]; p <<= 8; p |= packetBuffer[2];

或者保存一些时钟周期而不是将位移到最后:

unsigned short p;
p = (((unsigned short)packetBuffer[1])<<8) | packetBuffer[2];

你必须小心指针,优化器会咬你,以及内存对齐和一长串其他问题。是的,做得对,它更快,做错了,虫子可以长时间逗留,并在最不希望的时候打击。

说你很懒,想在8位数组上做一些16位数学运算。 (小端)

unsigned short *s;
unsigned char b[10];

s=(unsigned short *)&b[0];

if(b[0]&7)
{
   *s = *s+8;
   *s &= ~7;
}

do_something_With(b);

*s=*s+8;

do_something_With(b);

*s=*s+8;

do_something_With(b);

无法保证完美无错误的编译器会创建您期望的代码。发送到b函数的字节数组do_something_with()可能永远不会被*s操作修改。上面代码中的任何内容都没有说它应该。如果您没有优化代码,那么您可能永远不会看到此问题(直到某人优化或更改编译器或编译器版本)。如果你使用调试器,你可能永远不会看到这个问题(直到为时已晚)。

编译器看不到s和b之间的连接,它们是两个完全独立的项目。优化器可以选择不将*s写回内存,因为它看到*s有许多操作,因此它可以将该值保存在寄存器中,并且只将其保存到内存中(如果有的话) )。

解决上面的指针问题有三种基本方法:

  1. s声明为volatile。
  2. 使用工会。
  3. 每当更改类型时使用一个或多个功能。

答案 3 :(得分:2)

不应该将unsigned char指针强制转换为无符号短指针(对于从较小数据类型的指针转​​换为较大数据类型)。这是因为假设地址将正确对齐。更好的方法是将字节转换为真正的无符号短对象,或将memcpy转换为无符号短数组。

毫无疑问,您可以调整编译器设置以克服此限制,但如果代码传递并重用,这将是一个非常微妙的事情,将来会破坏。

答案 4 :(得分:2)

unsigned short myShort = *(unsigned short *)&packetBuffer[1];

答案 5 :(得分:2)

也许这是一个非常晚的解决方案,但我只想与您分享。如果要转换基元或其他类型,可以使用union。见下文:

union CharToStruct {
    char charArray[2];
    unsigned short value;
};


short toShort(char* value){
    CharToStruct cs;
    cs.charArray[0] = value[1]; // most significant bit of short is not first bit of char array
    cs.charArray[1] = value[0];
    return cs.value;
}

当你创建一个低于十六进制值的数组并调用Short函数时,你会得到一个带有3的短值。

char array[2]; 
array[0] = 0x00;
array[1] = 0x03;
short i = toShort(array);
cout << i << endl; // or printf("%h", i);

答案 6 :(得分:1)

static cast具有不同的语法,而且你需要使用指针,你想要做的是:

unsigned short *myShort = static_cast<unsigned short*>(&packetBuffer[1]);

答案 7 :(得分:0)

没有人看到输入是一个字符串!

/* If it is a string as explicitly stated in the question.
 */
int byte1 = packetBuffer[1] - '0'; // convert 1st byte from char to number.
int byte2 = packetBuffer[2] - '0';

unsigned short result = (byte1 * 256) + byte2;

/* Alternatively if is an array of bytes.
 */
int byte1 = packetBuffer[1];
int byte2 = packetBuffer[2];

unsigned short result = (byte1 * 256) + byte2;

这也避免了大多数其他解决方案在某些平台上可能存在的对齐问题。注意short是至少两个字节。如果您尝试取消引用非2字节对齐的短指针(或者系统上的sizeof(短)),大多数系统都会给出内存错误!

答案 8 :(得分:0)

char packetBuffer[] = {1, 2, 3};
unsigned short myShort = * reinterpret_cast<unsigned short*>(&packetBuffer[1]);

我(不得不)一直这样做。大端是一个明显的问题。当机器不喜欢错位读取时,真正能得到的是不正确的数据! (并写)。

您可能想要编写测试版和断言以查看它是否正确读取。因此,当在大端机器上运行或者更重要的是在不喜欢错误读取的机器上运行时,将发生断言错误而不是奇怪的难以追踪'错误';)

答案 9 :(得分:0)

在Windows上,您可以使用:

unsigned short i = MAKEWORD(lowbyte,hibyte);

答案 10 :(得分:0)

我意识到这是一个老话题,我不能说我尝试了这里提出的所有建议。我只是让自己对mfc感到舒服,而我正在寻找一种方法将uint转换为两个字节,然后再转换回套接字的另一端。

你可以在网上找到很多有点转移的例子,但它们似乎都没有。很多例子似乎过于复杂;我的意思是我们只是在谈论从一个uint中抓取2个字节,通过网络发送它们,然后将它们插回另一端的uint,对吗?

这是我最终提出的解决方案:

class ByteConverter
{
public:
 static void uIntToBytes(unsigned int theUint, char* bytes)
  {
   unsigned int tInt = theUint;

   void *uintConverter = &tInt;
   char *theBytes = (char*)uintConverter;

   bytes[0] = theBytes[0];
   bytes[1] = theBytes[1];
  }
 static unsigned int bytesToUint(char *bytes)
  {
   unsigned theUint = 0;

   void *uintConverter = &theUint;
   char *thebytes = (char*)uintConverter;

   thebytes[0] = bytes[0];
   thebytes[1] = bytes[1];

   return theUint;
  }
};

像这样使用:

unsigned int theUint;
char bytes[2];
CString msg;
ByteConverter::uIntToBytes(65000,bytes); theUint = ByteConverter::bytesToUint(bytes);
msg.Format(_T("theUint = %d"), theUint); AfxMessageBox(msg, MB_ICONINFORMATION | MB_OK);

希望这可以帮助别人。