周二強(qiáng) 張妍琰
(河南城建學(xué)院計(jì)算機(jī)科學(xué)與工程學(xué)院 河南平頂山 467036)
國內(nèi)現(xiàn)行C語言教材大多不介紹序列點(diǎn),這使得讀者只能記憶而不是遵循求值規(guī)則分析有序列點(diǎn)表達(dá)式的求值順序。以簡單的逗號(hào)表達(dá)式a=3,++a為例,設(shè)變量a為整型變量(下面表達(dá)式中出現(xiàn)的變量也默認(rèn)為整型)。“逗號(hào)表達(dá)式自左向右依次求值”,故先對(duì)子表達(dá)式a=3求值,再對(duì)子表達(dá)式++a求值。在實(shí)際教學(xué)中善于質(zhì)疑的學(xué)生往往會(huì)問:表達(dá)式中自增操作符++的優(yōu)先級(jí)最高,為何要先求子表達(dá)式a=3的值?表達(dá)式求值時(shí)不是先算高優(yōu)先級(jí)的操作符嗎?表達(dá)式究竟有沒有求值原則呢?序列點(diǎn)作用的缺失不僅使得學(xué)生對(duì)C語言知識(shí)的認(rèn)知?dú)埲辈蝗?,而且也影響了學(xué)生自主學(xué)習(xí)的積極性,不利于創(chuàng)新型人才的培養(yǎng)。
根據(jù)C語言標(biāo)準(zhǔn)[1][2][3],序列點(diǎn)就是執(zhí)行序列中的一些特定點(diǎn),在這些點(diǎn)上,前面求值的副效應(yīng)(sideeffect)應(yīng)徹底完成且其后求值的副效應(yīng)均未發(fā)生。在教材中照搬標(biāo)準(zhǔn)讓初學(xué)者學(xué)習(xí)理解序列點(diǎn)這個(gè)概念是不明智的,應(yīng)直接向初學(xué)者指出序列點(diǎn)在表達(dá)式求值中起的作用。如表達(dá)式中有序列點(diǎn),則求值時(shí)序列點(diǎn)左邊的操作數(shù)要先于其右邊的操作數(shù)求值。C語言表達(dá)式求值的原則為:先考慮序列點(diǎn),再根據(jù)操作符的優(yōu)先級(jí)和結(jié)合性。
雖然表達(dá)式求值時(shí)要首先根據(jù)序列點(diǎn)確定求值順序,但在確定有序列點(diǎn)操作符的操作數(shù)時(shí)需要結(jié)合操作符的優(yōu)先級(jí)和結(jié)合性。操作數(shù)是指操作符進(jìn)行操作的操作對(duì)象,如表達(dá)式3+2中加法操作符左邊的操作數(shù)為3,右邊的操作數(shù)為2。在復(fù)雜的表達(dá)式中,需結(jié)合操作符的優(yōu)先級(jí)和結(jié)合性來確定某操作符的操作數(shù)。對(duì)于表達(dá)式3+2*5,加法操作符左邊的操作數(shù)為3,但其右邊的操作數(shù)不為2,進(jìn)行加法運(yùn)算時(shí)3顯然不可能和2相加。因?yàn)榧臃ú僮鞣疫呄噜彽牟僮鞣麨槌朔ú僮鞣?,?yōu)先級(jí)比它高,所以它右邊的操作數(shù)為2*5(的積)。而乘法操作符左邊的操作數(shù)為2,右邊的操作數(shù)為5。對(duì)于表達(dá)式3+2-5,加法操作符右邊相鄰的操作符為減法操作符,兩者優(yōu)先級(jí)相同,但結(jié)合性為左結(jié)合,故它右邊的操作數(shù)為2。
表達(dá)式a=0&&++a中邏輯與左邊的操作數(shù)為0,右邊的操作數(shù)為子表達(dá)式++a,整個(gè)表達(dá)式為賦值表達(dá)式;而表達(dá)式(a=0)&&++a中邏輯與左邊的操作數(shù)為子表達(dá)式(a=0),右邊的操作數(shù)為子表達(dá)式++a,整個(gè)表達(dá)式為邏輯表達(dá)式。邏輯與操作符有序列點(diǎn),因此表達(dá)式(a=0)&&++a求值時(shí),雖然自增操作符的優(yōu)先級(jí)最高,但求值時(shí)首先考慮序列點(diǎn),邏輯與操作符左邊的操作數(shù)(a=0)需先于其右邊的操作數(shù)++a求值。
逗號(hào)操作符(,)有序列點(diǎn)。逗號(hào)操作符多用于把多條語句變成一條語句,如a=2;和++a;為兩條語句,而a=2,++a;是一條語句。語句a=2,++a;執(zhí)行時(shí),如果逗號(hào)操作符沒有序列點(diǎn),子表達(dá)式++a就會(huì)先執(zhí)行,即這條語句的執(zhí)行順序與上面兩條語句的并不相同?;诙禾?hào)操作符的作用,逗號(hào)操作符只能優(yōu)先級(jí)最低,且含有序列點(diǎn)。
邏輯與操作符(&&)有序列點(diǎn)。C語言中邏輯與操作符實(shí)行“短路計(jì)算”,即當(dāng)其左邊的操作數(shù)值為0即假時(shí),不對(duì)右邊的操作數(shù)求值而直接把0(假)作為求值的最終結(jié)果。如果邏輯與操作符沒有序列點(diǎn),表達(dá)式3>5&&++a求值時(shí),自增操作符的優(yōu)先級(jí)最高,子表達(dá)式++a應(yīng)先求值。邏輯與操作符左邊的操作數(shù)3>5求值的結(jié)果為0,即假,根據(jù)短路計(jì)算,作為邏輯與右邊的操作數(shù),子表達(dá)式++a不會(huì)被求值。顯然兩者矛盾。為了短路計(jì)算,邏輯與操作符&&需要序列點(diǎn)。有了序列點(diǎn)之后,求值時(shí)序列點(diǎn)左邊的操作數(shù)將先于其右邊的操作數(shù)求值,即子表達(dá)式3>5先求值,由于短路計(jì)算,整個(gè)表達(dá)式的值為0,即假,且右操作數(shù)子表達(dá)式++a不會(huì)被求值。
邏輯或操作符(||)有序列點(diǎn)。C語言中邏輯或操作符也實(shí)行“短路計(jì)算”,因此,其有序列點(diǎn)的必要性與邏輯與操作符的相同。
條件操作符?:的問號(hào)處?有序列點(diǎn)。條件操作符常用于改寫簡單的if-else選擇結(jié)構(gòu),如下面的語句:
可用條件操作符改寫為a>b?++a:++b;。
如果條件操作符沒有序列點(diǎn),語句a>b?++a:++b;執(zhí)行時(shí),++a和++b會(huì)先于子表達(dá)式a>b執(zhí)行,這樣的執(zhí)行順序顯然與if-else選擇結(jié)構(gòu)的不同,因此,條件操作符?:的問號(hào)?處有序列點(diǎn)。語句a>b?++a:++b;執(zhí)行時(shí),問號(hào)處?左邊的操作數(shù)a>b先執(zhí)行,值為真時(shí),對(duì)++a求值,不對(duì)++b求值;值為假時(shí),反之。
設(shè)整型變量a的值為0。對(duì)于表達(dá)式'a'||(a=1)&&(a+=2),邏輯或||左邊的操作數(shù)為'a',右邊的操作數(shù)為(a=1)&&(a+=2);邏輯與&&左邊的操作數(shù)為(a=1),右邊的操作數(shù)為(a+=2)。求值時(shí)首先考慮序列點(diǎn),邏輯或左邊的操作數(shù)'a'的值非“0”為真,執(zhí)行短路計(jì)算,其右邊的操作數(shù)不會(huì)被求值,因此,整個(gè)表達(dá)式的值為1,即真。在表達(dá)式求值的過程中,變量a的值沒有改變。
對(duì)于表達(dá)式(a=0)&&(a=5)||(a+=1),邏輯與&&左邊的操作數(shù)為(a=0),右邊的操作數(shù)為(a=5);邏輯或||左邊的操作數(shù)為(a=0)&&(a=5),右邊的操作數(shù)為(a+=1)。求值時(shí)首先考慮序列點(diǎn),邏輯與&&左邊的操作數(shù)(a=0)先求值,求值時(shí)一方面變量a的值會(huì)變成0,另一方面這個(gè)子表達(dá)式的值為0即假,執(zhí)行短路計(jì)算,其右邊的操作數(shù)(a=5)不會(huì)被求值,因此,原表達(dá)式變?yōu)?||(a+=1)。邏輯或||左邊的操作數(shù)值為0,即假,不能短路計(jì)算,只能再求其右邊操作數(shù)的值。子表達(dá)式(a+=1)求值時(shí)一方面變量a的值由0變成1,另一方面這個(gè)子表達(dá)式的值為1即真,因此,原表達(dá)式的值為1,即真。表達(dá)式的求值完成后,變量a的值變?yōu)?。
特別強(qiáng)調(diào),用賦值表達(dá)式作為邏輯操作符的操作數(shù)僅僅為了清晰地表明在表達(dá)式求值過程中某個(gè)子表達(dá)式是否被求值,這類表達(dá)式的實(shí)用性和可讀性非常差,實(shí)際編程時(shí)不能使用這類表達(dá)式。
序列點(diǎn)不僅可以使低優(yōu)先級(jí)的操作符先于高優(yōu)先級(jí)的操作符求值,而且也可以影響操作符的結(jié)合性。表達(dá)式a>b?++a:c>d?++c:++d有兩個(gè)條件操作符,條件操作符為右結(jié)合,因此,原表達(dá)式的求值順序應(yīng)為(a>b?++a:(c>d?++c:++d))。左邊條件操作符的3個(gè)操作數(shù)分別為子表達(dá)式a>b,子表達(dá)式++a和子表達(dá)式c>d?++c:++d。按照結(jié)合性,子表達(dá)式(c>d?++c:++d)應(yīng)先求值,但是,由于左邊條件操作符的問號(hào)處?有序列點(diǎn),故子表達(dá)式a>b先求值,如果其為真,則只會(huì)對(duì)子表達(dá)式++a求值,而不會(huì)再對(duì)子表達(dá)式c>d?++c:++d求值了。顯然,此表達(dá)式中條件操作符的右結(jié)合只用于確認(rèn)左邊條件操作符的右操作數(shù),而沒有使得右邊條件操作符先求值。
[1]InternationalOrganization forStandardization.ISO/IEC9899:1999.
[2]InternationalOrganization forStandardization.ISO/IEC9899:1990.
[3]國家技術(shù)監(jiān)督局.GB/T15272-1994程序設(shè)計(jì)語言C[R].中國標(biāo)準(zhǔn)出版社,1994.
[4]周二強(qiáng).C語言內(nèi)涵教程[M].北京:中國鐵道出版社,2013.