【C語言】a+=b和a=a+b 真的完全等價嗎?a[i] = i++ 到底對不對?

▎a+=b和a=a+b 真的完全等價嗎?▎

在C語言中,相信 a+=b 和 a = a+b大家都有寫過,那你有沒有思考過這二者究竟是不是完全等價的呢?

其實這個問題的難點就在a和b的數據類型上,要分兩種情況:

1、對於同樣類型的a,b來說

兩個式子執行的結果確實沒有什麼區別。但是從編譯的角度來看,a+=b;執行的時候效率高。

2、對於不同類型的a,b來說

不同類型的兩個變量在進行運算的時候,我們經常說到的是類型的轉換問題。這裡,請記住一點:運算過程中,低精度的類型向高精度類型轉換。

【C語言】a+=b和a=a+b 真的完全等價嗎?a[i] = i++ 到底對不對?

在上面的代碼中,如果使用+=,b會直接轉換成 char,如果使用a+b,a首先轉換成int,最後賦值的時候再轉換成char。

因此,總的來說,a=a+b;和a+=b;並不是任何時候都等價,要分情況視之,原因就在數據類型轉換這裡,希望大家以後對這兩者慎重使用。


▎a[i] = i++ 到底對不對?▎

編程中有時會遇到一些有歧義的表達式,比如 a[i] = i++ 。 那麼 a[i] = i++ 到底對不對呢?

首先請看如下代碼:

【C語言】a+=b和a=a+b 真的完全等價嗎?a[i] = i++ 到底對不對?

對於這個表達式中 a[i] = i++,子表達式i++有一個副作用,它會改變i的值,由於i在同一表達式中會被引用,因此這樣會導致未定義的行為。因為無法判定該引用(該公式中的左邊的a[i]中)是新值還是舊值。

不同的編譯器在解釋此類行為的時候會有不同的理解,比如下面三個編譯器(dev c++ 、 codeblocks、vs2019 )對於上述的代碼就有不同的理解。

【C語言】a+=b和a=a+b 真的完全等價嗎?a[i] = i++ 到底對不對?

【C語言】a+=b和a=a+b 真的完全等價嗎?a[i] = i++ 到底對不對?

【C語言】a+=b和a=a+b 真的完全等價嗎?a[i] = i++ 到底對不對?

從上面的運行的截圖可以觀察到相同的一段代碼,dev c++和codeblocks的執行結果是相同的,但是vs2019和它們卻並不相同。

對於此類行為,儘管有些文獻中認為這類表達式的行為是不確定的,但是c標準卻強烈聲明它是未定義的。

未定義行為的其他示例包括訪問超出其邊界的數組, 解除引用空指針, 在生命終結後訪問對象 或寫作 據稱聰明的表達 喜歡i++ + ++i。

未定義的行為還有兩個不那麼危險的兄弟,不確定的行為實現定義的行為。

那麼實現定義的行為、不確定的行為、未定義的行為這三者的區別在哪裡呢?

首先這三種情況都代表了c語言標準中沒有明確要求某個特定構造或使用它的程序必須完成的事情的領域。c語言定義中的這種鬆散性是傳統的,但是這種規定方式是經過深思熟慮的,這種定義方式允許作者:

1、選擇某些構造可以按照“硬件完成的方式”生成高效的代碼。

2、忽略某些太難準確定義、並且可能在良好書寫的程序中沒什麼實際用處的邊界構造。


▎對於這3種“標準中沒有準確定義的行為”的定義如下:

1、實現定義的行為

抽象機的某些方面和操作在本國際標準中描述為實現定義(例如,sizeof(int))。這些構成了抽象機器的參數。每個實施應包括描述其在這些方面的特徵和行為的文件。

2、不確定的行為

抽象機的某些其他方面和操作在本國際標準中描述為不明(例如,評估函數參數的順序)。在可能的情況下,C語言國際標準定義了一組允許的行為。這些定義了抽象機器的非確定性方面。

3、未定義的行為

任何事情都有可能發生,標準對此沒有任何要求,程序可能編譯失敗、運行錯誤(直接崩潰或者生成錯誤的結果)或者幸運的如程序員所願。

既然標準對編譯器沒有進行任何要求,那麼編譯器就可以做出任何可能的行為。在程序中忍受未定義的想法是極其危險的,未定義行為比你想象的還要未定義。

如果大家想書寫可移植代碼,那麼上述的三種行為都是需要極力避免的。因此我們在編寫代碼時最好避免 a[i] = i++ 這種C語言未定義的寫法。

對於熱愛編程的人來說,!筆者有一個編程零基礎入門學習交流俱樂部(群),私信我【編程學習】進入,有一群一起學習一起解答的小夥伴很重要,還有學習視頻文件,歡迎初學者和正在進階中的小夥伴們!



分享到:


相關文章: