c語言的算術運算溢出問題

1、 關於溢出的結論:可能出現的情況是結果的數據類型定義的小了,導致結果不正確。

關於計算溢出,看書上說的c語言中有符號數計算溢出的話會不知道發生什麼(溢出結果未定義),看編譯器怎麼處理。我在keil上試了下,溢出會把溢出部分砍掉,比如定義的是short型的,結果保留2個字節。

short aa=-32767;

short bb=32;

short cc;

cc= aa- bb;

cc=7fe1;

如果擴展到jint,結果是ffff7fe1.

檢查是否溢出加 if(aa < 0 && aa -(-32768) >=bb)

參考:https://blog.csdn.net/weiqifa0/article/details/24320437?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

這篇文章中說的整數轉型溢出感覺比較常見還容易忽略,謹記。主要是負數的時候。

示例二:整形轉型時的溢出

1

2

3

4

5

6

7

8

9

10

11

12

13

int copy_something(char *buf, int len)

{

    #define MAX_LEN 256

    charmybuf[MAX_LEN];</pre>

<pre>     ... ...

     ... ...

 

     if(len > MAX_LEN){// <---- [1]

         return-1;

     }

 

     returnmemcpy(mybuf, buf, len);

}

上面這個例子中,還是[1]處的if語句,看上去沒有會問題,但是len是個signed int,而memcpy則需一個size_t的len,也就是一個unsigned 類型。於是,len會被提升爲unsigned,此時,如果我們給len傳一個負數,會通過了if的檢查,但在memcpy裏會被提升爲一個正數,於是我們的mybuf就是overflow了。這個會導致mybuf緩衝區後面的數據被重寫。

uint8_t合併成uint16_t時   在keil上試的不加uint16_t也可以。

uint16_t LEBufToUint16(uint8_t *_pBuf)
{
    return (((uint16_t)_pBuf[1] << 8) | _pBuf[0]);
}

再試:

short  a = INT16_MAX;

shor b = 32;

int cc = a + b;

結果正確。再試:

int a = INT32_MAX;

int b = 32;

uint32_t  c = a+ b;

c=0x8000001f

並沒有像鏈接中文章說的a+b的結果按int存會溢出.可能是編譯器處理了,雖然在c語言上結果是未知的。

"我們來看一段代碼:

1

2

3

4

5

void foo(int m, int n)

{

    size_ts = m + n;

    .......

}

上面這段代碼有兩個風險:1)有符號轉無符號2)整型溢出。這兩個情況在前面的那些示例中你都應該看到了。所以,你千萬不要把任何檢查的代碼寫在 s = m + n 這條語名後面,不然就太晚了。undefined行爲就會出現了——用句純正的英文表達就是——“Dragon is here”——你什麼也控制不住了。(注意:有些初學者也許會以爲size_t是無符號的,而根據優先級 m 和 n 會被提升到unsigned int。其實不是這樣的,m 和 n 還是signed int,m + n 的結果也是signed int,然後再把這個結果轉成unsigned int 賦值給s)"

3、用“UL”避免Keil C51大整數常量運算溢出錯誤

Keil C51是與ANSI C兼容的編譯器,ANSI C規範規定十進制整數常量的默認數據類型是int、long int和unsigned long int的其中一種,對給定的常量是其中的哪一種要看這個常量的實際大小,如果常數在-32768~32767之間則按int類型處理,如果按int類型處理會溢出就考慮long int或更大的數據類型unsigned long int。總之,編譯器總是按儘可能的原則指定常量的類型。
但這一原則並不總能奏效,當兩個常量做運算時就可能導致溢出。如:
#define SYSCLK                        22118400    // SYSCLK in Hz (22.1184 MHz external crystal oscillator)
#define SLIDER_REST_TIME    100              // in ms,slider rest time
#define REST_DELAY               SYSCLK * SLIDER_REST_TIME / (65536 * 1000)
unsigned char i;
i = REST_DELAY;
在keil c51中運行i爲0xE1,即225,並不是期望的結果22118400 * 100 / (65536 * 1000) = 33.75,取整爲33。原因分析如下:
宏替換後爲:i = 22118400 * 100 / (65536 * 1000);,編譯器首先爲22118400定義類型,因爲22118400不在int的表示範圍內,而在long int的範圍-2147483648~2147483647內,所以22118400按long int類型處理,在做乘積運算時100被自動按long int處理,22118400 * 100將按兩帶符號長整型常量進行運算,運算結果仍爲帶符號長整型,結果寫成十六進制是0x83D60000,其十進制是-2083127296,顯然出現了溢出錯誤。keil編譯器並沒有給出任何錯誤或警告提示信息(VC++6.0還給出警告warning C4307: '*' : integral constant overflow),繼續進行下一個運算65536 * 1000,結果爲帶符號長整型,十六進制爲0x3E80000,十進制爲65536000,最後按兩長整型除法計算-2083127296 / 65536000,結果爲0xFFFFFFE1,由於i爲字符類型,取0xFFFFFFE1的最低有效字節爲0xE1賦值給i,i的最終值爲0xE1。
解決這種溢出錯誤的方法用C語言的一個術語就是“提升”(promotion),拿上例來說就是將22118400指定爲無符號長整型,即:
#define SYSCLK                        22118400UL
注:雖然只要將22118400、100、65536和1000四個常數中的一個指定爲無符號長整型即可得到正確的結果,但考慮到可讀性及規範性,應選擇大整數指定其類型。
小結:按C51編譯器的默認類型整數常量運算可能出現溢出錯誤,對大整數應指定其數據類型以避免出現可能的運算錯誤。

轉自:幽幽靈貓

http://www.cnblogs.com/civet/archive/2011/05/31/2064959.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章