国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > C++ > 正文

C++編譯器無(wú)法捕捉到的8種錯(cuò)誤實(shí)例分析

2020-01-26 15:21:13
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

本文實(shí)例分析了C++編譯器無(wú)法捕捉到的8種錯(cuò)誤,分享給大家供大家參考之用。有助于深入理解C++運(yùn)行原理,具體分析如下:

眾所周知,C++是一種復(fù)雜的編程語(yǔ)言,其中充滿了各種微妙的陷阱。在C++中幾乎有數(shù)不清的方式能把事情搞砸。幸運(yùn)的是,如今的編譯器已經(jīng)足夠智能化了,能夠檢測(cè)出相當(dāng)多的這類編程陷阱并通過(guò)編譯錯(cuò)誤或編譯警告來(lái)通知程序員。最終,如果處理得當(dāng)?shù)脑挘魏尉幾g器能檢查到的錯(cuò)誤都不會(huì)是什么大問(wèn)題,因?yàn)樗鼈冊(cè)诰幾g時(shí)會(huì)被捕捉到,并在程序真正運(yùn)行前得到解決。最壞的情況下,一個(gè)編譯器能夠捕獲到的錯(cuò)誤只會(huì)造成程序員一些時(shí)間上的損失,因?yàn)樗麄儠?huì)尋找解決編譯錯(cuò)誤的方法并修正。

那些編譯器無(wú)法捕獲到的錯(cuò)誤才是最危險(xiǎn)的。這類錯(cuò)誤不太容易察覺(jué)到,但可能會(huì)導(dǎo)致嚴(yán)重的后果,比如不正確的輸出、數(shù)據(jù)被破壞以及程序崩潰。隨著項(xiàng)目的膨脹,代碼邏輯的復(fù)雜度以及眾多的執(zhí)行路徑會(huì)掩蓋住這些bug,導(dǎo)致這些bug只是間歇性的出現(xiàn),因此使得這類bug難以跟蹤和調(diào)試。盡管本文的這份列表對(duì)于有經(jīng)驗(yàn)的程序員來(lái)說(shuō)大部分都只是回顧,但這類bug產(chǎn)生的后果往往根據(jù)項(xiàng)目的規(guī)模和商業(yè)性質(zhì)有不同程度的增強(qiáng)效果。

這些示例全部都在Visual Studio 2005 Express上測(cè)試過(guò),使用的是默認(rèn)告警級(jí)別。根據(jù)你選擇的編譯器,你得到的結(jié)果可能會(huì)有所不同。我強(qiáng)烈建議所有的程序員朋友都采用最高等級(jí)的告警級(jí)別!有一些編譯提示在默認(rèn)告警級(jí)別下可能不會(huì)被標(biāo)注為一個(gè)潛在的問(wèn)題,而在最高等級(jí)的告警級(jí)別下就會(huì)被捕捉到!

1)變量未初始化

變量未初始化是C++編程中最為常見(jiàn)和易犯的錯(cuò)誤之一。在C++中,為變量所分配的內(nèi)存空間并不是完全“干凈的”,也不會(huì)在分配空間時(shí)自動(dòng)做清零處理。其結(jié)果就是,一個(gè)未初始化的變量將包含某個(gè)值,但沒(méi)辦法準(zhǔn)確地知道這個(gè)值是多少。此外,每次執(zhí)行這個(gè)程序的時(shí)候,該變量的值可能都會(huì)發(fā)生改變。這就有可能產(chǎn)生間歇性發(fā)作的問(wèn)題,是特別難以追蹤的。看看如下的代碼片段:

if (bValue)   // do Aelse   // do B

如果bValue是未經(jīng)初始化的變量,那么if語(yǔ)句的判斷結(jié)果就無(wú)法確定,兩個(gè)分支都可能會(huì)執(zhí)行。在一般情況下,編譯器會(huì)對(duì)未初始化的變量給予提示。下面的代碼片段在大多數(shù)編譯器上都會(huì)引發(fā)一個(gè)警告信息。

int foo(){  int nX;  return nX;}

但是,還有一些簡(jiǎn)單的例子則不會(huì)產(chǎn)生警告:

void increment(int &nValue){  ++nValue;}int foo(){  int nX;  increment(nX);  return nX;}

以上的代碼片段可能不會(huì)產(chǎn)生一個(gè)警告,因?yàn)榫幾g器一般不會(huì)去跟蹤查看函數(shù)increment()到底有沒(méi)有對(duì)nValue賦值。

未初始化變量更常出現(xiàn)于類中,成員的初始化一般是通過(guò)構(gòu)造函數(shù)的實(shí)現(xiàn)來(lái)完成的。

class Foo{private:  int m_nValue;public:  Foo();  int GetValue() { return m_bValue; }}; Foo::Foo(){  // Oops, 我們忘記初始化m_nValue了} int main(){  Foo cFoo;  if (cFoo.GetValue() > 0)    // do something  else    // do something else}

注意,m_nValue從未初始化過(guò)。結(jié)果就是,GetValue()返回的是一個(gè)垃圾值,if語(yǔ)句的兩個(gè)分支都有可能會(huì)執(zhí)行。

新手程序員通常在定義多個(gè)變量時(shí)會(huì)犯下面這種錯(cuò)誤:

int nValue1, nValue2 = 5;

這里的本意是nValue1和nValue2都被初始化為5,但實(shí)際上只有nValue2被初始化了,nValue1從未被初始化過(guò)。

由于未初始化的變量可能是任何值,因此會(huì)導(dǎo)致程序每次執(zhí)行時(shí)呈現(xiàn)出不同的行為,由未初始化變量而引發(fā)的問(wèn)題是很難找到問(wèn)題根源的。某次執(zhí)行時(shí),程序可能工作正常,下一次再執(zhí)行時(shí),它可能會(huì)崩潰,而再下一次則可能產(chǎn)生錯(cuò)誤的輸出。當(dāng)你在調(diào)試器下運(yùn)行程序時(shí),定義的變量通常都被清零處理過(guò)了。這意味著你的程序在調(diào)試器下可能每次都是工作正常的,但在發(fā)布版中可能會(huì)間歇性的崩掉!如果你碰上了這種怪事,罪魁禍?zhǔn)壮36际俏闯跏蓟淖兞俊?/p>

2)整數(shù)除法

C++中的大多數(shù)二元操作都要求兩個(gè)操作數(shù)是同一類型。如果操作數(shù)的不同類型,其中一個(gè)操作數(shù)會(huì)提升到和另一個(gè)操作數(shù)相匹配的類型。在C++中,除法操作符可以被看做是2個(gè)不同的操作:其中一個(gè)操作于整數(shù)之上,另一個(gè)是操作于浮點(diǎn)數(shù)之上。如果操作數(shù)是浮點(diǎn)數(shù)類型,除法操作將返回一個(gè)浮點(diǎn)數(shù)的值:

float fX = 7;float fY = 2;float fValue = fX / fY; // fValue = 3.5

如果操作數(shù)是整數(shù)類型,除法操作將丟棄任何小數(shù)部分,并只返回整數(shù)部分。

int nX = 7;int nY = 2;int nValue = nX / nY;  // nValue = 3

如果一個(gè)操作數(shù)是整型,另一個(gè)操作數(shù)是浮點(diǎn)型,則整型會(huì)提升為浮點(diǎn)型:

float fX = 7.0;int nY = 2;float fValue = fX / nY; // nY 提升為浮點(diǎn)型,除法操作將返回浮點(diǎn)型值// fValue = 3.5

有很多新手程序員會(huì)嘗試寫(xiě)下如下的代碼:

int nX = 7;int nY = 2;float fValue = nX / nY; // fValue = 3(不是3.5哦!)

這里的本意是nX/nY將產(chǎn)生一個(gè)浮點(diǎn)型的除法操作,因?yàn)榻Y(jié)果是賦給一個(gè)浮點(diǎn)型變量的。但實(shí)際上并非如此。nX/nY首先被計(jì)算,結(jié)果是一個(gè)整型值,然后才會(huì)提升為浮點(diǎn)型并賦值給fValue。但在賦值之前,小數(shù)部分就已經(jīng)丟棄了。

要強(qiáng)制兩個(gè)整數(shù)采用浮點(diǎn)型除法,其中一個(gè)操作數(shù)需要類型轉(zhuǎn)換為浮點(diǎn)數(shù):

int nX = 7;int nY = 2;float fValue = static_cast<float>(nX) / nY; // fValue = 3.5

因?yàn)閚X顯式的轉(zhuǎn)換為float型,nY將隱式地提升為float型,因此除法操作符將執(zhí)行浮點(diǎn)型除法,得到的結(jié)果就是3.5。

通常一眼看去很難說(shuō)一個(gè)除法操作符究竟是執(zhí)行整數(shù)除法還是浮點(diǎn)型除法:

z = x / y; // 這是整數(shù)除法還是浮點(diǎn)型除法?

但采用匈牙利命名法可以幫助我們消除這種疑惑,并阻止錯(cuò)誤的發(fā)生:

int nZ = nX / nY;   // 整數(shù)除法double dZ = dX / dY; // 浮點(diǎn)型除法

有關(guān)整數(shù)除法的另一個(gè)有趣的事情是,當(dāng)一個(gè)操作數(shù)是負(fù)數(shù)時(shí)C++標(biāo)準(zhǔn)并未規(guī)定如何截?cái)嘟Y(jié)果。造成的結(jié)果就是,編譯器可以自由地選擇向上截?cái)嗷蛘呦蛳陆財(cái)啵”热纾?5/2可以既可以計(jì)算為-3也可以計(jì)算為-2,這和編譯器是向下取整還是向0取整有關(guān)。大多數(shù)現(xiàn)代的編譯器是向0取整的。

3)=  vs  ==

這是個(gè)老問(wèn)題,但很有價(jià)值。許多C++新手會(huì)弄混賦值操作符(=)和相等操作符(==)的意義。但即使是知道這兩種操作符差別的程序員也會(huì)犯下鍵盤敲擊錯(cuò)誤,這可能會(huì)導(dǎo)致結(jié)果是非預(yù)期的。

// 如果nValue是0,返回1,否則返回nValueint foo(int nValue){  if (nValue = 0) // 這是個(gè)鍵盤敲擊錯(cuò)誤 !    return 1;  else    return nValue;} int main(){  std::cout << foo(0) << std::endl;  std::cout << foo(1) << std::endl;  std::cout << foo(2) << std::endl;   return 0;}

函數(shù)foo()的本意是如果nValue是0,就返回1,否則就返回nValue的值。但由于無(wú)意中使用賦值操作符代替了相等操作符,程序?qū)a(chǎn)生非預(yù)期性的結(jié)果:

000

當(dāng)foo()中的if語(yǔ)句執(zhí)行時(shí),nValue被賦值為0。if (nValue = 0)實(shí)際上就成了if (nValue)。結(jié)果就是if條件為假,導(dǎo)致執(zhí)行else下的代碼,返回nValue的值,而這個(gè)值剛好就是賦值給nValue的0!因此這個(gè)函數(shù)將永遠(yuǎn)返回0。

在編譯器中將告警級(jí)別設(shè)置為最高,當(dāng)發(fā)現(xiàn)條件語(yǔ)句中使用了賦值操作符時(shí)會(huì)給出一個(gè)警告信息,或者在條件判斷之外,應(yīng)該使用賦值操作符的地方誤用成了相等性測(cè)試,此時(shí)會(huì)提示該語(yǔ)句沒(méi)有做任何事情。只要你使用了較高的告警級(jí)別,這個(gè)問(wèn)題本質(zhì)上都是可修復(fù)的。也有一些程序員喜歡采用一種技巧來(lái)避免=和==的混淆。即,在條件判斷中將常量寫(xiě)在左邊,此時(shí)如果誤把==寫(xiě)成=的話,將引發(fā)一個(gè)編譯錯(cuò)誤,因?yàn)槌A坎荒鼙毁x值。

4)混用有符號(hào)和無(wú)符號(hào)數(shù)

如同我們?cè)谡麛?shù)除法那一節(jié)中提到的,C++中大多數(shù)的二元操作符需要兩端的操作數(shù)是同一種類型。如果操作數(shù)是不同的類型,其中一個(gè)操作數(shù)將提升自己的類型以匹配另一個(gè)操作數(shù)。當(dāng)混用有符號(hào)和無(wú)符號(hào)數(shù)時(shí)這會(huì)導(dǎo)致出現(xiàn)一些非預(yù)期性的結(jié)果!考慮如下的例子:

cout << 10 

主站蜘蛛池模板:
柘城县|
错那县|
濮阳市|
周至县|
平原县|
肇庆市|
来安县|
广东省|
华容县|
确山县|
宣化县|
萨迦县|
阿坝县|
增城市|
岑巩县|
静安区|
威远县|
仙居县|
喀什市|
鄂伦春自治旗|
张掖市|
永城市|
南开区|
孝义市|
南丹县|
湛江市|
兴化市|
肥乡县|
桂阳县|
仲巴县|
盐津县|
柳林县|
平湖市|
乌拉特后旗|
合川市|
新安县|
大港区|
德化县|
许昌市|
增城市|
清远市|