【C語言】優(yōu)先級,結(jié)合方向和執(zhí)行順序

1. 問題的提出

K&R的書中一再強調(diào)"C is not a big language",當(dāng)時看書的時候無法理解這句話的意思∪雎瑁現(xiàn)在我的理解是C標準本身的限制比較小恢暖,留給程序員的空間較大。這樣一來C中的而有些問題標準就沒有給出限定狰右,就會產(chǎn)生一些讓人迷惑的地方。今天我遇到了一個這樣的問題舆床,解決這些問題有的時候看起來是“鉆牛角尖”棋蚌,好吧,我就是一個愛鉆牛角尖的人挨队。這里需要解決的問題是執(zhí)行順序的問題谷暮,先給出幾個問題。

int i = 0, j = 0;
j = i++ + i++ + i++;

執(zhí)行完這兩個語句后i和j的值各是什么盛垦?(看著是不是很熟悉湿弦,上次筆試考C中是不是有這個呢)

int i=0;
int arry[10];
arry[i] = i++;

執(zhí)行完上述語句后,i和數(shù)組arry中的值是多少腾夯?

#define PRINT(x, y, z)  printf(" x = %d, y = %d, z = %d\n", (x), (y),(z))
x = y = z = 1;
++x || ++y && ++z; PRINT(x, y, z);     

輸出結(jié)果是什么颊埃?(來自于C Puzzle Book Operators 1.6 )
先把這幾個問題放在這里,先思考下蝶俱,下面將會解決他們班利。

2. 明確兩個概念:side effects, sequence point

2.1 side effects

C語言經(jīng)典著作 “The C Programming Language” 中對于side effects的定義:

Function calls, nested assignment statements, and increment anddecrement
operators cause ‘side effects’ – some variable is changed as a by-product
of the evaluation of an expression.

這里述說的side effect可以理解為一種“副作用”酥艳,這種作用是改變一個變量的值预侯。
“C In Nutshell” 中關(guān)于side effects的定義:

In addition to yielding a value, the evaluation of an expression can result in other changes in the execution environment, called side effects. Examples of such changes include modifications of a variable's value, or of input or output streams.

相對于K&R中的定義這里使用了對于環(huán)境的改變吏祸,這應(yīng)該更加準確谬莹『芭總結(jié):side effects 就是程序中的實體產(chǎn)生的改變院崇,這里所說的實體通常指變量曙咽。
賦值省店,自增竿拆,自減表達式會產(chǎn)生side effects,函數(shù)調(diào)用表達式也有可能產(chǎn)生side effects宙拉。

2.2 sequence points

sequence points: A sequence point is a point in time at which the dust has settled and all side effects which have been seen so far are guaranteed to be complete. The sequence points listed in the C standard are:

  1. at the end of the evaluation of full expression ( a full expression is an expression statement, or any other expression which is not subexpression within any large expression);
  2. at the ||, &&, ?:,and comma operators; and
  3. at a function call (after the evaluation of all arguments, and just before the actual call).

序列點(sequence points)是一種邏輯意義的點,它的意義在于如输,邏輯點前的副作用(side effects)都在這時生效鼓黔。C標準中定義的序列點總共有三類央勒,第一類是完全表達式(full expression);第二類是||,&&,?:和澳化;第三類是函數(shù)調(diào)用崔步,在所有的參數(shù)確定后、函數(shù)真正調(diào)用之前缎谷。

2.3 side effects 和sequence points對于編寫程序有什么意義井濒?

2.3.1我的“SS1”和“SS2”原則

標準中規(guī)定了在前一個序列點前的副作用都會在前一個序列點后完成,但是標準沒有規(guī)定兩個序列點之間的副作用生效的順序列林,不同的C語言實現(xiàn)的順序可能不同瑞你。請注意這一點,這是所有問題產(chǎn)生的根本原因希痴。如果兩個序列點之間有超過兩個的副作用作用在同一個實體上者甲,這樣不同的編譯器產(chǎn)生的結(jié)果就不同,這種情況在標準中稱為unspecified 砌创。所以在實際應(yīng)用中應(yīng)該避免這種情況的出現(xiàn)虏缸,我把這一個原則稱為為SS1。

是不是遵守了SS1原則就不會產(chǎn)生unspecified了呢嫩实?非也刽辙。可以設(shè)想這樣一種情況:每一個實體(A)在兩個序列點之間被兩次使用甲献,只有一次對這個實體本身產(chǎn)生副作用宰缤,另外一次被間接的用來產(chǎn)生副作用作用于另外一個實體(B)。在前面設(shè)想的這種情況下雖然符合SS1原則晃洒,但是我們會發(fā)現(xiàn)被間接用來產(chǎn)生副作用時慨灭,對于實體(B)產(chǎn)生的副作用肯定會跟實體(A)有關(guān),但是這個實體(A)在這個序列點區(qū)間中有被副作用作用锥累,那么我們就無法確定這個實體(A)的值了缘挑,從而實體(B)也就無法確定了。這里可以歸納為:在兩個序列點之間桶略,如果出現(xiàn)對一個實體的多次引用语淘,并且只有一次會對該實體產(chǎn)生副作用(SS1),那么所有的這些引用都必須用來產(chǎn)生這個副作用 际歼,我把這一個原則稱為SS2惶翻。只有同時遵守了SS1和SS2,寫出的表達式才不是unspecified類型的鹅心。

2.3.2 標準

The standard states that Between the previous and next sequence point an object shall have its stored value modified at most once by the evaluation of the expression. Furthermore, the prior value shall be accessed only to determine the value to be stored.

可以清晰的看到標準中使用了兩句話來概括這種問題吕粗,這正好對應(yīng)于SS1,SS2原則旭愧。

2.3.3 怎樣才能避免寫出未定義的表達式出來颅筋?

下面給出更為具體的方法:
1)在一個表達式中最多只改變一個實體宙暇。
2)如果一個實體在一個表達式被改變,并且出現(xiàn)次數(shù)大于一次议泵,請保證所有實體的出現(xiàn)都是為了產(chǎn)生這個“改變”占贫。
例如: i = i+1;
3)如果不能遵守1),那么請保證改變的是不同的實體先口。
例如:c = *p++;
4)如果1)和2)都不能遵守型奥,那么請使用序列點將表達式分開。
例如 : (c = getchar()) != EOF && c != ‘\n’;

3. 優(yōu)先級碉京、結(jié)合方向做了哪些事厢汹,沒有做哪些事?

3.1 優(yōu)先級谐宙、結(jié)合方向做了哪些事烫葬?

C語言中組成程序的基本單位是表達式(expression),表達式是指用操作符(operator)和操作數(shù)(operand)連接起來的式子凡蜻。C標準給出了最基本的操作符厘灼,通過這些操作符可以組成簡單表達式,同樣也可以通過復(fù)合產(chǎn)生復(fù)雜表達式咽瓷。當(dāng)一個表達式中出現(xiàn)多個操作符,多個操作數(shù)的時候舰讹,操作符合操作數(shù)是如何組合起來的呢茅姜?優(yōu)先級和結(jié)合方向就是用來解決這個問題的,可以這么說月匣,優(yōu)先級和結(jié)合方向給出了一個表達式的含義钻洒,這只是說明了各個操作符和操作數(shù)是怎么聚合起來的。

3.2 優(yōu)先級锄开、結(jié)合方向沒有做哪些事素标?

僅僅依靠優(yōu)先級和結(jié)合方向是無法確定一個復(fù)合表達式中對各個子表達式的求值順序。標準中對于這點的規(guī)定是:
兩個相鄰的操作符的執(zhí)行順序由它們的優(yōu)先級決定萍悴。如果它們優(yōu)先級相同头遭,它們的執(zhí)行順序由它們的結(jié)合性決定。除此之外癣诱,編譯器可以自由決定任何順序?qū)Ρ磉_式進行求值计维,只要它不違反逗號,&&撕予,||和鲫惶?:操作符所施加的限制。

4. 解決問題

1)j = i++ + i++ + i++;
這個表達式違反了SS1实抡,不同的編譯器產(chǎn)生的結(jié)果可能不同欠母。
2)arry[i] = i++;
這個表達式違反了SS2欢策,不同的編譯器產(chǎn)生的結(jié)果可能不同。
3)x = y = z = 1;
++x || ++y && ++z; PRINT(x, y, z);
&&的優(yōu)先級比|| 的優(yōu)先級高赏淌,所以:
++x || ++y && ++z 等效于++x || (++y && ++z)
這里就很容易犯錯踩寇,會認為先執(zhí)行++y && ++z 在執(zhí)行++x || ( … ),這種觀點是錯誤的,c中只對 逗號猜敢,姑荷、邏輯與 &&、
邏輯或 || 和 條件表達式規(guī)定了執(zhí)行順序缩擂,對于邏輯表達式方向是從左向右執(zhí)行的鼠冕。本例子中,先執(zhí)行 ++x = 2胯盯,邏輯
或表達式被短路懈费,++y && ++z沒有執(zhí)行,最后x = 2博脑, y = 1憎乙, z = 1;

5.后記

通過本課題的學(xué)習(xí)加深了對于c的理解,理解SS1叉趣,SS2原則泞边。在實際應(yīng)用中應(yīng)該遵守“一條語句只做一件事的原則”。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疗杉,一起剝皮案震驚了整個濱河市阵谚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌烟具,老刑警劉巖梢什,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異朝聋,居然都是意外死亡嗡午,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門冀痕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荔睹,“玉大人,你說我怎么就攤上這事金度∮γ模” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵猜极,是天一觀的道長中姜。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么丢胚? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任翩瓜,我火速辦了婚禮,結(jié)果婚禮上携龟,老公的妹妹穿的比我還像新娘兔跌。我一直安慰自己,他們只是感情好峡蟋,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布坟桅。 她就那樣靜靜地躺著,像睡著了一般蕊蝗。 火紅的嫁衣襯著肌膚如雪仅乓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天蓬戚,我揣著相機與錄音夸楣,去河邊找鬼。 笑死子漩,一個胖子當(dāng)著我的面吹牛豫喧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播幢泼,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼紧显,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了缕棵?” 一聲冷哼從身側(cè)響起鸟妙,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挥吵,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體花椭,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡忽匈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了矿辽。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丹允。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖袋倔,靈堂內(nèi)的尸體忽然破棺而出雕蔽,到底是詐尸還是另有隱情,我是刑警寧澤宾娜,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布批狐,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏嚣艇。R本人自食惡果不足惜承冰,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望食零。 院中可真熱鬧困乒,春花似錦、人聲如沸贰谣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吱抚。三九已至百宇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間频伤,已是汗流浹背恳谎。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留憋肖,地道東北人因痛。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像岸更,于是被迫代替她去往敵國和親鸵膏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容