2016-08-12 105 views
1

考慮以下代碼:功能sscanf的必須分配給變量,否則奇怪的行爲

#define TRANSLATOR_requestElectricityMeterWrite() do{addr = word_getAddress(); value = word_getValue(); }while(0) 

uint16_t value; 
uint8_t addr; 

bool dispatcher(void) 
{ 
    TRANSLATOR_requestElectricityMeterWrite(); 
    return true; 
} // AFTER this point (during debug) program goes to default handler 

int main(void) 
{ 
    if(dispatcher()) 
     continue; 
     . . . . 
     . . . . 
} 

uint16_t word_getValue(void) 
{ 
    uint16_t value; 
    sscanf("ABCD", "%4x", (unsigned int *)&value); 
    return value; 
} 

uint8_t word_getAddress(void) 
{ 
    uint8_t address; 
    sscanf("00", "%2x", (unsigned int *)&address); 
     ; 
    return address; 
} 

當上面的代碼運行時,內部if原因程序語句崩潰(去一些默認的處理程序)。

但是,當我改變兩個(word_getValue和word_ getAddres)功能,以這樣的:

uint16_t word_getValue(void) 
{ 
    uint16_t value; 
    int i = 0;i++; 
    i = sscanf(WORD_getValueString(), "%4x", (unsigned int *)(&value)); 
    return value; 
} 

uint8_t word_getAddress(void) 
{ 
    uint8_t address; 
    int i = 0;i++; 
    i = sscanf(WORD_getNameString(), "%2x", (unsigned int *)(&address)); 
    return address; 
} 

它的工作原理。此外如果虛擬i似乎解決了這個問題。但爲什麼它不以另一種方式工作呢?

GNU ARM工具鏈v4.8.3

+0

'WORD_getValueString()'和'WORD_getNameString()'定義在哪裏?看起來您正在向我們展示您的代碼的不同版本。 –

+0

在另一個文件中。但是他們的聲明也包括在內。構建報告既沒有警告也沒有錯誤報告。 – Hairi

+0

我建議發佈[最小,完整和可驗證代碼](http://stackoverflow.com/help/mcve) –

回答

3

這兩個函數都會調用未定義的行爲,因此會發生任何事情。添加一個額外的本地變量會更改目標變量的位置,從而隱藏其大小錯誤的影響。

sscanf("ABCD", "%4x", (unsigned int *)&value); 

sscanfsizeof(unsigned int)字節(大概4)存儲到變量value,僅具有2個字節。

sscanf(WORD_getNameString(), "%2x", (unsigned int *)(&address)); 

將存儲sizeof(unsigned int)字節到可變address,它只有1個字節。

來解決這個問題,最簡單的方法是解析爲一個unsigned int並單獨存放解析值到目的地,或者乾脆返回值:

uint16_t word_getValue(void) { 
    unsigned int value; 
    if (sscanf(WORD_getValueString(), "%4x", &value) == 1) 
     return value; 
    // could not parse a value, return some default value or error code 
    return 0; 
} 

uint8_t word_getAddress(void) { 
    unsigned int address; 
    if (sscanf(WORD_getNameString(), "%2x", &address) == 1) 
     return address; 
    // could not parse a value, return some default value or error code 
    return 0; 
} 

您可能還需要驗證,如果分析得到的值位於目標類型的範圍內,但由於您將解析分別限制爲4位和2位十六進制數字,因此不會發生溢出。

+0

返回類型「uint8_t」和「uint16_t」是否明確地轉換返回的值?因爲現在我返回'unsigned int',它在我的平臺上是'uint32_t'。 – Hairi

+1

這些函數返回的'unsigned int'值分別隱式轉換爲'uint8_t'和'uint16_t'返回類型。轉換是完全定義的,值被截斷並給出它們如何由'sscanf'計算,它們在返回類型的範圍內。 – chqrlie

+0

太棒了,所以你提供的例子確實做得很好。我不得不承認,我非常沮喪的是我正在調試的電路板的行爲。對於我和其他C語言危險的新手來說,這是一個很好的例子,尤其是涉及內存的時候。 10x :) @chqrlie – Hairi

1

%x格式要求unsigned參數(假設它是uint32_t平臺)。如果您通過uint16_tuint8_t它可能會損壞內存。在你的情況下,它會損壞堆棧並覆蓋返回地址。嘗試使用%4hx代替uint16_t%2hhx代替uint8_t

+1

'%4hx'假設目的地是一個'unsigned short'。儘管可能是這種情況,但它仍然存在技術風險,因爲我們不知道「uint16_t」是否與'unsigned short'類型相同。 – chqrlie