一時糊涂了,請問,if(temp--==0) 請問是先用temp也0比較之后再減,還是先減后比較,
網(wǎng)友評論:最好不要這樣寫啊
分成2句寫是最好的
不然,你會后悔的!
網(wǎng)友評論:教科書是比較詳細(xì),實(shí)際中我還沒有真正用到這個方式
網(wǎng)友評論:先比較再減
網(wǎng)友評論:不一定的,我想即然有這種語法的存在,肯定有人會用的.現(xiàn)在大家不是記住了,以后也會用了么
網(wǎng)友評論:哈哈哈,就是啊,還是不要這么寫了。。。看著就暈。
不過++和--的優(yōu)先級很低。。。。
:)
fengfeng 發(fā)表于 2009-8-2 21:41
你這就說錯了,++,--是單目操作符,比==高,優(yōu)先級很高的來,排行第二,從右到左結(jié)合,
這里相當(dāng)于(temp--)==0,只不過--在后面是先運(yùn)算后自減。
網(wǎng)友評論:好象有爭議的東西都是好東西一樣,有空看下高質(zhì)量程序設(shè)計(jì)。有些東西并不是優(yōu)點(diǎn)。
網(wǎng)友評論:先判斷,再減
但是這樣編寫的代碼會給自己調(diào)試和維護(hù)帶來麻煩,而且不符合編碼規(guī)范
網(wǎng)友評論:teep--:先比較,再減;
--teep:先減,在比較。
網(wǎng)友評論:寫出這樣的代碼來實(shí)在該打PP
網(wǎng)友評論:調(diào)試一下
網(wǎng)友評論:這不是自找麻煩嗎
網(wǎng)友評論:括號的優(yōu)先級高 自加自減都比較低了 這個肯定是先比再減
網(wǎng)友評論:++和--是看在變量的哪邊,左邊的話,先
計(jì)算在判斷的,在右邊的話是先判斷再計(jì)算
網(wǎng)友評論:++ 和-- 的首先級別是最高的,比括號還高,
如果 X =5;y =(x++) *(x--);那么,結(jié)果y就等于 25
如果 X =5;y =(++x) *(x);那么,結(jié)果y就等于 30
網(wǎng)友評論:是的,LS理解正確。
網(wǎng)友評論:先判斷
網(wǎng)友評論:存在必有其合理性,如果用的方便就用
網(wǎng)友評論:k&r 里就這么寫。。。。
一個不錯的總結(jié)。
-----------------------------------------------------------------------------------------
3. Side Effect與Sequence Point 請點(diǎn)評
如果你只想規(guī)規(guī)矩矩地寫代碼,那么基本用不著看這一節(jié)。本節(jié)的內(nèi)容基本上是鉆牛角尖兒的,除了Short-circuit比較實(shí)用,其它寫法都應(yīng)該避免使用。但沒辦法,有時候不是你想鉆牛角尖兒,而是有人逼你去鉆牛角尖兒。這是我們的學(xué)員在找工作筆試時碰到的問題:
int a=0;
a = (++a)+(++a)+(++a)+(++a);
據(jù)我了解,似乎很多公司都有出這種筆試題的惡趣味。答案應(yīng)該是Undefined,我甚至有些懷疑出題人是否真的知道答案。下面我來解釋為什么是Undefined。
我們知道,調(diào)用一個函數(shù)可能產(chǎn)生Side Effect,使用某些運(yùn)算符(++ -- = 復(fù)合賦值)也會產(chǎn)生Side Effect,如果一個表達(dá)式中隱含著多個Side Effect,究竟哪個先發(fā)生哪個后發(fā)生呢?C標(biāo)準(zhǔn)規(guī)定代碼中的某些點(diǎn)是Sequence Point,當(dāng)執(zhí)行到一個Sequence Point時,在此之前的Side Effect必須全部作用完畢,在此之后的Side Effect必須一個都沒發(fā)生。至于兩個Sequence Point之間的多個Side Effect哪個先發(fā)生哪個后發(fā)生則沒有規(guī)定,編譯器可以任意選擇各Side Effect的作用順序。下面詳細(xì)解釋各種Sequence Point。
1、調(diào)用一個函數(shù)時,在所有準(zhǔn)備工作做完之后、函數(shù)調(diào)用開始之前是Sequence Point。比如調(diào)用foo(f(), g())時,foo、f()、g()這三個表達(dá)式哪個先求值哪個后求值是Unspecified,但是必須都求值完了才能做最后的函數(shù)調(diào)用,所以f()和g()的Side Effect按什么順序發(fā)生不一定,但必定在這些Side Effect全部作用完之后才開始調(diào)用foo函數(shù)。
2、條件運(yùn)算符?:、逗號運(yùn)算符、邏輯與&&、邏輯或||的第一個操作數(shù)求值之后是Sequence Point。我們剛講過條件運(yùn)算符和逗號運(yùn)算符,條件運(yùn)算符要根據(jù)表達(dá)式1的值是否為真決定下一步求表達(dá)式2還是表達(dá)式3的值,如果決定求表達(dá)式2的值,表達(dá)式3就不會被求值了,反之也一樣,逗號運(yùn)算符也是這樣,表達(dá)式1求值結(jié)束才繼續(xù)求表達(dá)式2的值。
邏輯與和邏輯或早在第 3 節(jié) “布爾代數(shù)”就講了,但在初學(xué)階段我一直回避它們的操作數(shù)求值順序問題。這兩個運(yùn)算符和條件運(yùn)算符類似,先求左操作數(shù)的值,然后根據(jù)這個值是否為真,右操作數(shù)可能被求值,也可能不被求值。比如例 8.5 “剪刀石頭布”這個程序中的這幾句:
ret = scanf("%d", &man);
if (ret != 1 || man < 0 || man > 2) {
printf("Invalid input!\n");
return 1;
}
其實(shí)可以寫得更簡單(類似于[K&R]的簡潔風(fēng)格):
if (scanf("%d", &man) != 1 || man < 0 || man > 2) {
printf("Invalid input!\n");
return 1;
}
這個控制表達(dá)式的求值順序是:先求scanf("%d", &man) != 1的值,如果scanf調(diào)用失敗,則返回值不等于1成立,||運(yùn)算有一個操作數(shù)為真則整個表達(dá)式為真,這時直接執(zhí)行下一句printf,根本不會再去求man < 0或man > 2的值;如果scanf調(diào)用成功,則讀入的數(shù)保存在變量man中,并且返回值等于1,那么說它不等于1就不成立了,第一個||運(yùn)算的左操作數(shù)為假,就會去求右操作數(shù)man < 0的值作為整個表達(dá)式的值,這時變量man的值正是scanf讀上來的值,我們判斷它是否在[0, 2]之間,如果man < 0不成立,則整個表達(dá)式scanf("%d", &man) != 1 || man < 0 的值為假,也就是第二個||運(yùn)算的左操作數(shù)為假,所以最后求右操作數(shù)man > 2的值作為整個表達(dá)式的值。
&&運(yùn)算與此類似,a && b的計(jì)算過程是:首先求表達(dá)式a的值,如果a的值是假則整個表達(dá)式的值是假,不會再去求b的值;如果a的值是真,則下一步求b的值作為整個表達(dá)式的值。所以,a && b相當(dāng)于“if a then b”,而a || b相當(dāng)于“if not a then b”。這種特性稱為Short-circuit,很多人喜歡利用Short-circuit特性簡化代碼。
3、在一個完整的聲明末尾是Sequence Point,所謂完整的聲明是指這個聲明不是另外一個聲明的一部分。比如聲明int a[10], b[20];,在a[10]末尾是Sequence Point,在b[20]末尾也是。
4、在一個完整的表達(dá)式末尾是Sequence Point,所謂完整的表達(dá)式是指這個表達(dá)式不是另外一個表達(dá)式的一部分。所以如果有f(); g();這樣兩條語句,f()和g()是兩個完整的表達(dá)式,f()的Side Effect必定在g()之前發(fā)生。
5、在庫函數(shù)即將返回時是Sequence Point。這條規(guī)則似乎可以包含在上一條規(guī)則里面,因?yàn)楹瘮?shù)返回時必然會結(jié)束掉一個完整的表達(dá)式。而事實(shí)上很多庫函數(shù)是以宏定義的形式實(shí)現(xiàn)的(第 2.1 節(jié) “函數(shù)式宏定義”),并不是真正的函數(shù),所以才需要有這條規(guī)則。
還有兩種Sequence Point和某些C標(biāo)準(zhǔn)庫函數(shù)的執(zhí)行過程相關(guān),此處從略,有興趣的讀者可參考[C99]的Annex C。
現(xiàn)在可以分析一下本節(jié)開頭的例子了。a = (++a)+(++a)+(++a)+(++a);的結(jié)果之所以是Undefined,因?yàn)樵谶@個表達(dá)式中有五個Side Effect都在改變a的值,這些Side Effect按什么順序發(fā)生不一定,只知道在整個表達(dá)式求值結(jié)束時一定都發(fā)生了。比如現(xiàn)在求第二個++a的值,這時第一個、第三個、第四個++a的Side Effect發(fā)生了沒有,a的值被加過幾次了,這些都不確定,所以第二個++a的值也不確定。這行代碼用不同平臺的不同編譯器來編譯結(jié)果是不同的,甚至在同一平臺上用同一編譯器的不同版本來編譯也可能不同。
寫表達(dá)式應(yīng)遵循的原則一:在兩個Sequence Point之間,同一個變量的值只允許被改變一次。僅有這一條原則還不夠,例如a[i++] = i;的變量i只改變了一次,但結(jié)果仍是Undefined,因?yàn)榈忍栕筮吀膇的值,等號右邊讀i的值,到底是先改還是先讀?這個讀寫順序是不確定的。但為什么i = i + 1;就沒有歧義呢?雖然也是等號左邊改i的值,等號右邊讀i的值,但你不讀出i的值就沒法計(jì)算i + 1,那拿什么去改i的值呢?所以這個讀寫順序是確定的。寫表達(dá)式應(yīng)遵循的原則二:如果在兩個Sequence Point之間既要讀一個變量的值又要改它的值,只有在讀寫順序確定的情況下才可以這么寫。
-----------------------------------------------------------------------------------------
4. 運(yùn)算符總結(jié)
到此為止,除了和指針相關(guān)的運(yùn)算符還沒講之外,其它運(yùn)算符都講過了,是時候做一個總結(jié)了。
運(yùn)算符+ - * / % > < >= <= == != & | ^ 以及各種復(fù)合賦值運(yùn)算符要求兩邊的操作數(shù)類型一致,條件運(yùn)算符?:要求后兩個操作數(shù)類型一致,這些運(yùn)算符在計(jì)算之前都需要做Usual Arithmetic Conversion。
下面按優(yōu)先級從高到低的順序總結(jié)一下C語言的運(yùn)算符,每一條所列的各運(yùn)算符具有相同的優(yōu)先級,對于同一優(yōu)先級的多個運(yùn)算符按什么順序計(jì)算也有說明,雙目運(yùn)算符就簡單地用“左結(jié)合”或“右結(jié)合”來說明了。和指針有關(guān)的運(yùn)算符* & ->也在這里列出來了,到第 23 章 指針再詳細(xì)解釋。
1、標(biāo)識符、常量、字符串和用()括號套起來的表達(dá)式是組成表達(dá)式的最基本單元,在運(yùn)算中做操作數(shù),優(yōu)先級最高。
2、后綴運(yùn)算符,包括數(shù)組取下標(biāo)[]、函數(shù)調(diào)用()、結(jié)構(gòu)體取成員“.”、指向結(jié)構(gòu)體的指針取成員->、后綴自增++、后綴自減--。如果一個操作數(shù)后面有多個后綴,按照離操作數(shù)從近到遠(yuǎn)的順序(也就是從左到右)依次計(jì)算,比如a.name++,先算a.name,再++,這里的.name應(yīng)該看成a的一個后綴,而不是把.看成雙目運(yùn)算符。
3、單目運(yùn)算符,包括前綴自增++、前綴自減--、sizeof、類型轉(zhuǎn)換()、取地址運(yùn)算&、指針間接尋址*、正號+、負(fù)號-、按位取反~、邏輯非!。如果一個操作數(shù)前面有多個前綴,按照離操作數(shù)從近到遠(yuǎn)的順序(也就是從右到左)依次計(jì)算,比如!~a,先算~a,再求!。
4、乘*、除/、模%運(yùn)算符。這三個運(yùn)算符是左結(jié)合的。
5、加+、減-運(yùn)算符。左結(jié)合。
6、移位運(yùn)算符<<和>>。左結(jié)合。
7、關(guān)系運(yùn)算符< > <= >=。左結(jié)合。
8、相等性運(yùn)算符==和!=。左結(jié)合。
9、按位與&。左結(jié)合。
10、按位異或^。左結(jié)合。
11、按位或|。左結(jié)合。
12、邏輯與&&。左結(jié)合。
13、邏輯或||。左結(jié)合。
14、條件運(yùn)算符:?。在第 2 節(jié) “if/else語句”講過Dangling-else問題,條件運(yùn)算符也有類似的問題。例如a ? b : c ? d : e是看成(a ? b : c) ? d : e還是a ? b : (c ? d : e)呢?C語言規(guī)定是后者。
15、賦值=和各種復(fù)合賦值(*= /= %= += -= <<= >>= &= ^= |=)。在雙目運(yùn)算符中只有賦值和復(fù)合賦值是右結(jié)合的。
16、逗號運(yùn)算符。左結(jié)合。
[K&R]第2章也有這樣一個列表,但是對于結(jié)合性解釋得不夠清楚。左結(jié)合和右結(jié)合這兩個概念只對雙目運(yùn)算符有意義,對于前綴、后綴和三目運(yùn)算符我單獨(dú)做了說明。C語言表達(dá)式的詳細(xì)語法規(guī)則可以參考[C99]的Annex A.2,其實(shí)語法規(guī)則并不是用優(yōu)先級和結(jié)合性這兩個概念來表述的,有一些細(xì)節(jié)用優(yōu)先級和結(jié)合性是表達(dá)不了的,只有看C99才能了解完整的語法規(guī)則。
網(wǎng)友評論:剛學(xué)C語言的時候,喜歡縮寫。
現(xiàn)在反而不喜歡縮寫了。寧可多寫幾句,加個括號等看似麻煩的事。
網(wǎng)友評論:看不懂的寫法,不是好寫法。
程序匠人 發(fā)表于 2009-8-4 00:25
頂
網(wǎng)友評論:多用括號,除非學(xué)校的考試
網(wǎng)友評論:很多人被教科書蒙騙了,書上只是用這個寫法來說明運(yùn)算符的優(yōu)先級,而實(shí)際應(yīng)用時要看編譯器怎么做,比較煩。
網(wǎng)友評論:這種寫法的功能是用來開闊思路,編程時不用為好
網(wǎng)友評論:應(yīng)該是先判斷啦,從右到左,不過連我都不會這樣寫的
網(wǎng)友評論:簡單即是美~~
如果我們是做工程的,而不是做學(xué)術(shù)的,建議記住以下“三不”:
不要挑戰(zhàn)自己的記性;
不要挑戰(zhàn)同事的耐心;
不要挑戰(zhàn)編譯器的水平。
網(wǎng)友評論:一個記憶這個問題的小竅門:如果++或--在變量的前面,表示先做遞加或遞減運(yùn)算并回存結(jié)果,再用遞加或遞減結(jié)果參與其它的計(jì)算;如果++或--在變量的后面,則先使用變量的數(shù)值參與其它的計(jì)算,隨后再把遞加或遞減結(jié)果存回這個變量。
使用++或--的好處是可以使程序簡練。如果樓主的問題不這樣寫,而是分開寫,則是這樣:
if(temp==0){
temp--;
......
}
else {
temp--;
......
}
使用--,則可以這樣寫:
if(temp--==0){
......
}
else {
......
}
估計(jì)當(dāng)初是為了某種優(yōu)化指令而設(shè)計(jì)了這2個操作。
網(wǎng)友評論:根據(jù)c語言的用法應(yīng)該就是先判斷temp是否等于0再進(jìn)行自減
網(wǎng)友評論:很多人的心聲啊,呵呵,如果考試了,講技巧還差不多
實(shí)際應(yīng)用中,講的是實(shí)用,通俗易懂。
不過C語言中有講,“temp--”這一句代碼中,temp的值將不改變。只有執(zhí)行完這段代碼
后,temp的值才減1,所以------應(yīng)該是先判斷=0,執(zhí)行完if語句之后,temp的值才減1。
如果是想先減1再判斷=0的話,可以if(--temp == 0 )
但是誰會去做不保險(xiǎn)的事呢,所以寧可if((temp--) == 0)加多個括號,也不玩弄技巧!
網(wǎng)友評論:
掌握基本概念就好理解了。以前咱也搞不清楚,還總抱怨搞不清誰的優(yōu)先級高,今天終于掌握了“訣竅”。
剛學(xué)的基本概念,現(xiàn)學(xué)現(xiàn)賣:
“i ++ 表示先使用 i 的值,然后++。”(呵呵,什么叫“先使用 i 的值”?)
“++ i 表示先++ ,然后使用 i的值!
所以,LZ的問題不用實(shí)驗(yàn),肯定是16樓說的“先判斷,然后減!保ㄏ仁褂胻emp的值,后考慮++ 。)
網(wǎng)友評論:我肯定會這么寫if(temp-- == 0),而不會寫成像51樓香版所展開的那樣,想必香版也不會把它展開寫吧。
if(temp-- == 0)才是正真講效率的寫法(特別在嵌入式軟件里),不管是對閱讀者還是對編譯器來說,都是有好處的。
首先是閱讀者,高手是肯定看不慣展開的寫法,當(dāng)他/她看到if表達(dá)式后出現(xiàn)了一個語句temp--,而在else后又出現(xiàn)了語句temp--,他/她會覺得非常不舒服:語句(表達(dá)式)有冗余,這明明可以合成一步放在if表達(dá)式中。有TX要拍磚了:他/她為什么會覺得不舒服?他/她也有可能喜歡冗余啊。我下面會解釋的(自己先納悶一下:!@#$%^,有人喜歡冗余?)。
其二,編譯器,在年代久遠(yuǎn)的時候,那時候整個編譯優(yōu)化技術(shù)水平還相當(dāng)?shù)拖拢瑢@種展開的寫法基本無法優(yōu)化(表達(dá)式跨邏輯合并),編譯結(jié)果就是兩個temp--都編譯出來了(實(shí)際的匯編指令),但使用者希望兩個表達(dá)式合并,只編譯出一個。這要求相當(dāng)合理。C語言的創(chuàng)造者認(rèn)為,既然編譯器做不到這種優(yōu)化,那么就引進(jìn)一種語法,讓使用者自己去合并表達(dá)式。這就是++、--的由來。這樣一來,編譯器也省心了,來一個表達(dá)式,編譯一個表達(dá)式,來兩個表達(dá)式,就編譯兩個表達(dá)式。
雖然當(dāng)前的編譯優(yōu)化技術(shù)發(fā)展到了一個很高的高度,但想要完全扔掉++、--而不損失一點(diǎn)效率,還是不行的。
在現(xiàn)實(shí)中,有的人不懈地追求效率(這是一個人生態(tài)度),他們在算法、程序設(shè)計(jì)方面也是如此。一旦有一個語法(或者一個算法)可以讓他們的程序提高效率,他們是無論如何也不會拒絕的,而只會努力熟悉它、掌握它,不讓它產(chǎn)生負(fù)面影響。那么,可以想象,日積月累,形成習(xí)慣以后,如果再讓他們?nèi)タ茨欠N冗余的編程寫法,他們是多么的不舒服!也可以想象,這些人憑著他們精益求精的態(tài)度,必定都會有所成就。
本貼中反對使用++、--的TX,難道你們認(rèn)為自己水平高過那些創(chuàng)造++、--和熟練使用++、--的大牛們嗎?
附帶描述一下if(temp--==0)最優(yōu)化的編譯結(jié)果:
1、減法指令:temp -= 1。說明:由于做了減法,如果之前temp的值為0,那么狀態(tài)
寄存器的C標(biāo)志置1,否則C標(biāo)志清0。(這步在大多數(shù)CPU上只需要1條指令)
2、條件分枝指令:如果C == 0,則跳轉(zhuǎn)到else。(這步在大多數(shù)CPU上只需要1條指令)
TX們,看看吧,不用--,能達(dá)到這么高的效率嗎?而且,編譯結(jié)果是不是像你們想象的那樣先比較,再做減法,然后跳轉(zhuǎn)呢?
網(wǎng)友評論:++ 和-- 的首先級別是最高的,比括號還高,
如果 X =5;y =(x++) *(x--);那么,結(jié)果y就等于 25
如果 X =5;y =(++x) *(x);那么,結(jié)果y就等于 30 ...
呆板書生 發(fā)表于 2009-9-23 10:07
這個例子不能說明++/--比()優(yōu)先級高,僅僅說明++在前與在后的區(qū)別而已.++/--與()優(yōu)先級同等
網(wǎng)友評論:支持51L
網(wǎng)友評論:強(qiáng)烈建議不要這么寫!
我很贊同有幾樓說的:這種東西只是在考試的時候會用,只有教科書上喜歡這樣寫(可能是說明優(yōu)先級的必要吧)。
事實(shí)上這樣的寫法不但沒有技巧,反而很容易弄錯,也不便于閱讀。其實(shí)吧,我現(xiàn)在覺得對于程序來說,結(jié)構(gòu)清晰最重要了,代碼的繁簡關(guān)鍵要看你的思路和算法!
網(wǎng)友評論:考試過客解答
++i,--i(在使用i之前,先使i得值加(減)1)
i++,i--(在使用i之后,使i得值加(減)1)
所以LZ所問為先比較后自減1
網(wǎng)友評論:哦 是這樣的我還沒有用過--我只用過++
網(wǎng)友評論:哪先哪后并不重要,不知道也不為過,重要的是不要這樣寫
網(wǎng)友評論:看不懂的語法不是好語法~~
網(wǎng)友評論:我覺得不對吧應(yīng)該加()的 不然怎么執(zhí)行啊temp-- 是先判斷再減
網(wǎng)友評論:哈哈 跟著 你們學(xué)習(xí)一下
網(wǎng)友評論:路過