2012-01-19 118 views
5

我遇到了一些我編寫的c代碼中的錯誤,雖然它相對容易修復,但我希望能夠更好地理解它的基礎問題。基本上發生了什麼是我有兩個無符號整數(uint32_t,實際上),當應用模數運算時,產生了一個負數的無符號等價物,這個數是一個被包裝的數,因此是「大的」。這裏是一個例子程序來演示:無符號溢出模運算符C

#include <stdio.h> 
#include <stdint.h> 

int main(int argc, char* argv[]) { 

    uint32_t foo = -1; 
    uint32_t u = 2048; 
    uint64_t ul = 2048; 

    fprintf(stderr, "%d\n", foo); 
    fprintf(stderr, "%u\n", foo); 
    fprintf(stderr, "%lu\n", ((foo * 2600000000) % u)); 
    fprintf(stderr, "%ld\n", ((foo * 2600000000) % u)); 
    fprintf(stderr, "%lu\n", ((foo * 2600000000) % ul)); 
    fprintf(stderr, "%lu\n", foo % ul); 

    return 0; 

} 

這產生以下輸出,我的x86_64的機器上:

-1 
4294967295 
18446744073709551104 
-512 
1536 
2047 

1536是我期待的數目,但(uint32_t的)( - 512)是我得到的這個數字,正如你所想象的那樣,這個數字會有所下降。

所以,我想我的問題是:爲什麼兩個無符號數之間的模操作,在這種情況下,產生了一些比除數(即負數)還大嗎?這種行爲是首選的原因嗎?

+1

2600000000是一個int(或可能是一個64位int長或long long),這可能會導致乘法的結果成爲(signed)long。你在哪個平臺上? – Random832

回答

3

我想原因是編譯器解釋2600000000面值作爲符號的64位數字,因爲它不適合帶符號的32位int。如果您用2600000000U替換號碼,您應該得到您期望的結果。

+2

實際上,OP通過將錯誤的格式字符串傳遞給'printf'來調用UB。 –

2

我沒有一個參考方便,但我很確定,當你做這個乘法時,它將它們提升到int64_t,因爲它需要強制兩個被乘數爲有符號整型。嘗試2600000000u代替2600000000 ....