簡體   English   中英

C ++:如何將數組中的2個字節轉換為unsigned short

[英]C++: how to cast 2 bytes in an array to an unsigned short

我一直致力於傳統的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])

好吧,你正在將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];

這可能遠低於您關心的內容,但請記住,您可以輕松獲得未對齊的訪問權限。 x86是寬容的,未對齊訪問導致的中止將在內部被捕獲並最終會復制並返回值,因此您的應用程序將不會知道任何不同(盡管它比對齊訪問慢得多)。 但是,如果此代碼將在非x86上運行(您沒有提及目標平台,因此我假設x86桌面Windows),那么這樣做會導致處理器數據中止,您將不得不手動復制在嘗試投射之前將數據轉換為對齊的地址。

簡而言之,如果您要對此進行大量訪問,您可能會考慮對代碼進行調整,以便不會出現未對齊的讀取,並且您將看到一個性能優勢。

上面的位移有一個錯誤:

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);

無法保證完美無錯誤的編譯器將創建您期望的代碼。 發送到do_something_with()函數的字節數組b可能永遠不會被*s操作修改。 上面代碼中的任何內容都沒有說它應該。 如果您沒有優化代碼,那么您可能永遠不會看到此問題(直到某人優化或更改編譯器或編譯器版本)。 如果你使用調試器,你可能永遠不會看到這個問題(直到為時已晚)。

編譯器看不到s和b之間的連接,它們是兩個完全獨立的項。 優化器可能會選擇不將*s寫回內存,因為它看到*s有許多操作,因此它可以將該值保存在寄存器中,並且只將其保存到內存中(如果有的話)。

修復上面的指針問題有三種基本方法:

  1. 聲明s為volatile。
  2. 使用工會。
  3. 更改類型時使用一個或多個功能。

也許這是一個非常晚的解決方案,但我只想與您分享。 如果要轉換基元或其他類型,可以使用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);

不應該將unsigned char指針強制轉換為無符號短指針(對於從較小數據類型的指針轉​​換為較大數據類型)。 這是因為假設地址將正確對齊。 更好的方法是將字節轉換為真正的無符號短對象,或將memcpy轉換為無符號短數組。

毫無疑問,您可以調整編譯器設置以克服此限制,但這是一個非常微妙的事情,如果代碼傳遞並重用,將來會破壞。

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

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

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

我意識到這是一個老線程,我不能說我嘗試了這里提出的每一個建議。 我只是讓自己對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);

希望這有助於某人。

沒有人看到輸入是一個字符串!

/* 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(短)),大多數系統都會給您一個內存錯誤!

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

我(不得不)一直這樣做。 大端是一個明顯的問題。 當機器不喜歡錯位讀取時,真正能得到的是不正確的數據! (和寫)。

您可能想要編寫測試版和斷言以查看它是否正確讀取。 因此,當在大端機器上運行或更重要的是一台不喜歡未對齊讀取的機器時,將發生斷言錯誤,而不是一個奇怪的難以追蹤'bug';)

在Windows上,您可以使用:

unsigned short i = MAKEWORD(lowbyte,hibyte);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM