前言:在寫(xiě)程序時(shí)候遇到了一些關(guān)于數(shù)據(jù)類(lèi)型轉(zhuǎn)換的問(wèn)題,編譯器也沒(méi)有報(bào)錯(cuò),運(yùn)行時(shí)才發(fā)現(xiàn)數(shù)據(jù)不對(duì),找bug花費(fèi)了很多時(shí)間,但最終也發(fā)現(xiàn)是一些細(xì)節(jié)上的問(wèn)題,特地在這里整理出一篇文章記錄。
實(shí)驗(yàn)環(huán)境:
- 芯片架構(gòu):Cortex-M0+
- 開(kāi)發(fā)IDE:Keil_v5
- 編譯器:armcc
問(wèn)題代碼:
uint64_t TempData2;
uint8_t KeyeBuf1[8];
TempData2 = ( KeyeBuf1[0] + (KeyeBuf1[1] << 8) + (KeyeBuf1[2] << 16) + (KeyeBuf1[3] << 24)+
(KeyeBuf1[4] << 32) + (KeyeBuf1[5] << 40) + (KeyeBuf1[6] << 48));
在上述代碼中,先前已經(jīng)聲明了TempData2的數(shù)據(jù)類(lèi)型為無(wú)符號(hào)64位整型類(lèi)型,在接下來(lái)的移位后累加數(shù)據(jù)總是出現(xiàn)差錯(cuò),檢查累加后數(shù)據(jù)范圍也沒(méi)有超過(guò)無(wú)符號(hào)64位整型數(shù)據(jù)范圍。
問(wèn)題分析:
經(jīng)過(guò)一系列的排查,最終發(fā)現(xiàn)了問(wèn)題所在,主要是以下三方面造成的影響:
1.機(jī)器平臺(tái)與編譯器的影響
??stdint.h是C99中引進(jìn)的一個(gè)標(biāo)準(zhǔn)C庫(kù)的文件,目前大部分單片機(jī)的C編譯器均支持。關(guān)于編譯器支的更多數(shù)據(jù)類(lèi)型完全可以在該文件中找到。以armcc編譯器為例,<stdint.h>文件中,對(duì)于8位,16位,32位,64位無(wú)符號(hào)整數(shù)類(lèi)型的數(shù)據(jù)定義為如下:
#include<stdint.h>
uint64_t TempData2;
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned __INT64 uint64_t;
??查找該文件可得uint64_t的類(lèi)型是unsigned __INT64類(lèi)型,__INT64類(lèi)型的解釋與機(jī)器平臺(tái)和編譯器相關(guān)。
??機(jī)器平臺(tái)的硬件架構(gòu)以及編譯器的編譯最終都會(huì)影響執(zhí)行代碼的生成。Cortex-M0+是32位的處理器,內(nèi)部的寄存器大多都是32位,這就導(dǎo)致了在默認(rèn)情況下進(jìn)行的運(yùn)算,不管是8位,16位,32位都在32位寄存器中運(yùn)算。
2.“=”符號(hào)運(yùn)算順序的影響
??=符號(hào)的執(zhí)行過(guò)程細(xì)分下來(lái)可以分成兩步:
- 計(jì)算
=右邊部分得出最終結(jié)果 - 最后把結(jié)果賦給
=左邊變量
??由于上述的運(yùn)算順序?qū)е拢?code>=左邊變量不管原先定位為多少位,對(duì)先執(zhí)行的=右邊計(jì)算過(guò)程沒(méi)有影響,也就是說(shuō),在上述代碼過(guò)程中,即使左邊變量先定義為64位,在先進(jìn)行右邊部分計(jì)算時(shí),還是按照右邊部分?jǐn)?shù)據(jù)最大的數(shù)據(jù)類(lèi)型來(lái)數(shù)據(jù)對(duì)齊,而后進(jìn)行計(jì)算。
3.移位程序移植性的影響
??對(duì)于移位程序來(lái)說(shuō),右移數(shù)據(jù)基本不會(huì)出太大問(wèn)題,但是左移就需要注意很多。在32位機(jī)器平臺(tái)上,8位,16位,32位數(shù)據(jù)類(lèi)型都會(huì)在32位寄存器中進(jìn)行移位,只要我們確保移位后的數(shù)據(jù)不會(huì)超過(guò)32位數(shù)據(jù)類(lèi)型,那么程序就會(huì)正常運(yùn)行。而上述數(shù)據(jù)類(lèi)型一旦移位后的數(shù)據(jù)類(lèi)型超過(guò)32位,那么處理器會(huì)丟失左移向前進(jìn)的數(shù)據(jù),留下最低的32位。
問(wèn)題解決:
??對(duì)于上述代碼進(jìn)行分析,( KeyeBuf1[0] + (KeyeBuf1[1] << 8) + (KeyeBuf1[2] << 16) + (KeyeBuf1[3] << 24)+ (KeyeBuf1[4] << 32) + (KeyeBuf1[5] << 40) + (KeyeBuf1[6] << 48));中的Keye1數(shù)組數(shù)據(jù)類(lèi)型都是8位,計(jì)算時(shí)都在32位寄存器中計(jì)算。前幾個(gè)數(shù)據(jù)的移位沒(méi)有超過(guò)32位數(shù)據(jù)類(lèi)型不會(huì)出太大錯(cuò)誤,從(KeyeBuf1[4] << 32)起,理論上移位后的數(shù)據(jù)超過(guò)了32位,只留下最低32位,導(dǎo)致數(shù)據(jù)出錯(cuò)。即使最后賦給了一個(gè)64位的變量,也是將一個(gè)32位數(shù)據(jù)賦給64位,而這些問(wèn)題在編譯時(shí)期編譯器并不會(huì)指出,需要我們自己多加注意。
??最后的解決方法是將KeyeBuf1數(shù)據(jù)在運(yùn)算時(shí)強(qiáng)制轉(zhuǎn)換為64位數(shù)據(jù)類(lèi)型,這樣進(jìn)行運(yùn)算時(shí)都是64位數(shù)據(jù)對(duì)齊,最后賦給一個(gè)64位數(shù)據(jù)類(lèi)型,就不會(huì)出現(xiàn)數(shù)據(jù)丟失的情況了。代碼如下:
TempData2 = ( KeyeBuf1[0] + (KeyeBuf1[1] << 8) + (KeyeBuf1[2] << 16) + ((uint64_t)KeyeBuf1[3] << 24)+
((uint64_t)KeyeBuf1[4] << 32) + ((uint64_t)KeyeBuf1[5] << 40) + ((uint64_t)KeyeBuf1[6] << 48));