標籤:

為什麼C語言在printf內賦值有「不及時」的情況?

我在做一道填空題:表達式x=2,y=x+3/2的值時,填的3,但答案是2時,驗證時覺得奇怪的問題:

//設實數x = 10,y;

//printf( 「%f」, x=2, y=x+3/2), 預想x=2,y=3,輸出3,但實際輸出2,x=2,y=11

//從圖一看,x的值最後確實被改變為2,但對同在第8行的y進行賦值時,x此時似乎仍為10,並沒有變化,出現了一個x發生了變化卻「不及時」的情況,為什麼?;

//如圖二,當我給逗號表達式加了個括弧,printf( "%f", (x=2,y=x+3/2) )時就改變了,這時的結果才是我本來預想的;

//問題有兩個:逗號表達返回的值為什麼不是最右y /*這個我覺得大概是printf輸出最近的變數*/ 為什麼有printf內賦值「不及時」的情況;

//請教各位;


首先需要知道三點,在C語言中:

  1. 函數參數的求值順序是未指定的(注意,這裡和調用規定無關)。
  2. 如果一個標量對象上的副作用和一個需要用到同一個標量對象的值之間的順序是不確定的,那麼行為未定義
  3. 逗號運算符(注意,這和函數參數列表中的逗號不同)保證從左到右運算。

那麼結果就很清楚了:不加括弧時,函數參數的求值順序是未指定的,這導致了 x = 2 中對x的賦值和 y = x + 3 / 2 中對x的使用之間的順序是不確定的,這是未定義行為。而加了括弧的時候,就會保證先 x = 2,再 y = x + 3 / 2,得到 3 並輸出。


因為printf是從右到左計算,從左到右輸出。

圖一先執行y=x+1

圖二因為有括弧,所以先執行x=2


這個問題要正確理解函數的處理順序就迎刃而解了。函數的處理順序為:

1、printf是一個可變參數的輸入函數。

2、編譯器會先計算每一個形參表達式。計算的順序是,先處理尾部的參數表達式,並把表達式結果參數的地址壓棧。所以,在這一步先計算y的結果會計算結束,為11。

3、跳轉到函數的處理地址。

4、printf函數從堆棧中取出每一個參數。

5、按照格式化字元串輸出x的數值。

6、函數退出,出棧,回到函數調用的下一行。

關於加括弧後,處理思路一樣,在括弧表達式中,括弧部分作為一個參數,先處理括弧內部的表達式,該表達式是我們理解的順序處理,先處理x=2,在處理y=x+3/2,並把y作為括弧內部的表達式處理結果,壓棧y的地址給printf.


吐個槽 答案在warning里


建議谷歌:序列點,未定義行為。


我覺得應該是printf裡面只有一個%f,所以自動對齊到第一個參數也就是a,所以只是輸出了a的值,y的值計算了,但是在格式化輸出裡面沒有說要輸出。

就像下面這代碼是正確的

printf("%d",a,b,c,d)

只會輸出a,因為格式化輸出裡面只有一個,所以自動對齊到a,至於後面的三個參數,是會進行運算的,但是結果不會輸出。這是printf裡面的逗號規則。

而加了括弧的才是逗號運算符

printf("%d",(a,b))

也就是返回右邊語句的值。另外,x=2是會返回一個值的副本的。所以x=2這個表達式的值是2。


加了括弧之後,你這個程序就變了。推薦你研究下代碼塊,位運算符的優先度。


函數里的逗號是另一種含義


函數的各個參數的執行的順序是undefined,完全取決於實現編譯器的人的個人愛好。

加了括弧之後,裡面的逗號是真的逗號表達式。

a, b

意為 先執行a,再執行b並返回b。順序是有明確規定的。

不要使用undefined behavior!!會翻車的!!


推薦閱讀:

用C++重寫minecraft到底有多難?
如何用C++實現一個視頻聊天伺服器,要用到那些協議和庫?
在c++中,可以使用一個分配了內存空間但是沒有構造的struct里的值類型變數嗎?
c++里,函數返回一個局部數組名可以嗎?
C++中String問題?

TAG:C編程語言 | CC |