0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀(guān)看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

C/C++靈魂拷問(wèn):++i與i++哪個(gè)執(zhí)行效率高?有什么區(qū)別?

Q4MP_gh_c472c21 ? 來(lái)源:高效程序員 ? 作者:高效程序員 ? 2022-03-31 14:04 ? 次閱讀

背景:相信很多人都遇到過(guò)這樣的問(wèn)題:printf("%d,%d",i++,++i); 也糾結(jié)過(guò)這個(gè)問(wèn)題,到底答案是什么,都沒(méi)有一個(gè)參考的資料……而唯一知道的是,幾乎所有C語(yǔ)言教材都這么講:i++就是先使用i的值再使i自身加一,而++i則是先使i自身加一,然后在使用i的值。 出于對(duì)真理的追求,今天我們就徹底弄明白此問(wèn)題! 譬如這樣的話(huà):
int a,b;int i=10,j=10;a=i++;b=++j;

我們可以很清楚的知道a和b的值分別將是10和11。這點(diǎn)毫無(wú)疑問(wèn),因?yàn)闊o(wú)論在任何平臺(tái)任何編譯器上運(yùn)行都是這個(gè)結(jié)果!

然而,對(duì)于這樣的程序:

int a,b;int i=10,j=10;a=(i++)+(i++)+(i++);b=(++j)+(++j)+(++j);

各位試想答案將是多少?我們可以放到編譯器上,運(yùn)行看一下結(jié)果。

先來(lái)看看windows下常用的VC6結(jié)果:

3500f10a-b0b2-11ec-aa7f-dac502259ad0.jpg

嗯,看到了,是30和37。但這個(gè)結(jié)果好像有點(diǎn)怪…… 那就再看看Linux下gcc的結(jié)果:

3515d1f6-b0b2-11ec-aa7f-dac502259ad0.png

哦,竟然也是30和37!
那么,我們?cè)倏纯垂爬弦稽c(diǎn)的TurboC的結(jié)果:

352cd126-b0b2-11ec-aa7f-dac502259ad0.jpg

354074e2-b0b2-11ec-aa7f-dac502259ad0.jpg

結(jié)果成了30和39,還真有點(diǎn)怪。 當(dāng)然,就C語(yǔ)言代碼來(lái)看,i++和++i都只有一行,看起來(lái)似乎二者的執(zhí)行效率一樣了?其實(shí)不是的,在學(xué)習(xí)C語(yǔ)言時(shí),教材和老師一般都會(huì)強(qiáng)調(diào)i++和++i的區(qū)別,例如下面這段C語(yǔ)言代碼:
inti,j,k;i = 0;j = i++;i = 0;k = ++i;
這段C語(yǔ)言代碼執(zhí)行后,j和k的值并不相等:j等于0,k等于1。 既然執(zhí)行結(jié)果有差異,那么執(zhí)行效率很有可能也是有差異的,事實(shí)的確如此。 查看上述C語(yǔ)言代碼對(duì)應(yīng)的匯編代碼,如下:

3553f83c-b0b2-11ec-aa7f-dac502259ad0.jpg

編譯器版本為gcc 4.8.4

可見(jiàn),j=i++;計(jì)算機(jī)需要4條指令來(lái)解釋?zhuān)葓?zhí)行k=++i;多出了一條指令。多出的一條指令為:在對(duì)i執(zhí)行自加操作之前,先保存i的當(dāng)前值留作稍后使用(賦值為j)。

這是怎么回事呢?不同的編譯器結(jié)果還不一樣呢?

而且這樣看來(lái),似乎++i的執(zhí)行效率比i++高一些?

為什么不同的編譯器結(jié)果不一樣?
要說(shuō)起這其中的原因,我們要先明白兩個(gè)知識(shí)點(diǎn),即“副作用”與“順序點(diǎn)”。 這里我們引用《C Primer Plus》的說(shuō)法:“現(xiàn)在我們?cè)儆懻撘恍〤的術(shù)語(yǔ)。副作用(side effect)是對(duì)數(shù)據(jù)對(duì)象或文件的修改?!?/span>
例如語(yǔ)句states=50,它的副作用是將變量states的值設(shè)置為50。這是副作用?這看起來(lái)更像是主要目的!然而,從C的角度來(lái)看,主要目的是對(duì)表達(dá)式求值。給C一個(gè)表達(dá)式4+6,C將計(jì)算它的值為10。給C一個(gè)表達(dá)式states=50,C將計(jì)算它的值為50。計(jì)算這個(gè)表達(dá)式的副作用就是把變量states的值改變?yōu)?0。跟賦值運(yùn)算符一樣,增量運(yùn)算符和減量運(yùn)算符也有副作用,它們主要由于副作用而被使用。
一個(gè)順序點(diǎn)(sequence point)是程序執(zhí)行中的一點(diǎn);在該點(diǎn)處,所有的副作用都在進(jìn)入下一步之前被計(jì)算。在C中,語(yǔ)句里的分號(hào)標(biāo)志了一個(gè)順序點(diǎn)。它意味著在一個(gè)語(yǔ)句中賦值運(yùn)算符、增量預(yù)算符及減量運(yùn)算符所做的全部改變必須在程序進(jìn)入下一個(gè)語(yǔ)句前發(fā)生。任何一個(gè)完整的表達(dá)式的結(jié)束也是一個(gè)順序點(diǎn)。
什么是完整的表達(dá)式呢?一個(gè)完整的表達(dá)式(full expression)是這樣一個(gè)表達(dá)式—-它不是一個(gè)更大的表達(dá)式的子表達(dá)式。完整的表達(dá)式的例子包括一個(gè)表達(dá)式語(yǔ)句里的表達(dá)式和在一個(gè)while循環(huán)里作為判斷條件的表達(dá)式。
順序點(diǎn)幫助闡明后綴增量動(dòng)動(dòng)作何時(shí)發(fā)生,比如考慮下面的代碼:
while(guests++<10)printf(“%d
”,guests);

有時(shí)C的初學(xué)者會(huì)設(shè)想在本程序中“先使用該值,然后增加它的值”的意思是在使用printf()語(yǔ)句后在增加guests的值。然而,因?yàn)間uests++<10是while循環(huán)的判斷條件,所以它是一個(gè)完整的表達(dá)式,這個(gè)表達(dá)式的結(jié)束就是一個(gè)順序點(diǎn)。

因此,C保證副作用(增加guests的值)在程序進(jìn)入printf()前發(fā)生。同時(shí)使用后綴形式保證了guests在于10比較后才增加。


現(xiàn)在考慮這個(gè)語(yǔ)句:

Y=(4+ x++)+(6+ x++);

表達(dá)式4+x++不是一個(gè)完整的表達(dá)式,所以C不能保證在計(jì)算子表達(dá)式4+x++后立即增加x。這里完整表達(dá)式是整個(gè)賦值語(yǔ)句,并且分號(hào)標(biāo)記了順序點(diǎn),所以C能保證的是在程序進(jìn)入后續(xù)語(yǔ)句前x將增加兩次。C沒(méi)有指明x是在每個(gè)子表達(dá)式被計(jì)算后增加還是在整個(gè)表達(dá)式被計(jì)算后增加,這就是我們要避免使用這類(lèi)語(yǔ)句的原因。

這是《C Primer Plus》的說(shuō)法,相信您應(yīng)該有一定答案了。


沒(méi)錯(cuò),那就是對(duì)于i=10;(++i)+(++i)+(++i);這樣的語(yǔ)句。C語(yǔ)言標(biāo)準(zhǔn)并沒(méi)有作規(guī)定。有的編譯器計(jì)算出來(lái)是39,因?yàn)闀?huì)使i的值自增三次變?yōu)?3,然后使用增加三次之后,也就是13的3個(gè)值相加為39。而有的編譯器計(jì)算結(jié)果則為37,如VisaulC++6.0則會(huì)先計(jì)算前兩個(gè)i的值為12,第三個(gè)i的值變成了加三次以后的值為13,因此結(jié)果是12+12+13=37。

如果有心的話(huà),您可以分別在VC6和TC上本別測(cè)試;(++i)+(++i)+(++i)+(++i)的值來(lái)洞悉不同編譯器的處理規(guī)則。

那么,回到最初的printf的問(wèn)題,明白求值的順序之后,再來(lái)看printf的求值問(wèn)題,printf的參數(shù)都是從左到右依次壓入棧內(nèi),所以計(jì)算起來(lái)求值運(yùn)算的時(shí)候則是由右至左(棧的特點(diǎn):即先進(jìn)后出)。那么至此,想必您已經(jīng)完全想明白了這類(lèi)問(wèn)題的全部了!

所以講到這里,想必大家就清楚緣由了,不同編譯器的處理過(guò)程是不同的。所以并沒(méi)有唯一的標(biāo)準(zhǔn)答案!現(xiàn)在大家明白了嗎?

為什么++i比i++執(zhí)行效率高一些呢? 為了寫(xiě)出效率更高的C語(yǔ)言程序,以后是不是應(yīng)該盡量使用++i,而不是i++了呢?例如下面這樣的C語(yǔ)言代碼:
for(i=0; i<10; i++);for(i=0; i<10; ++i);
是不是上面那行C語(yǔ)言代碼的執(zhí)行效率低于下面的呢?只能說(shuō)理論如此,實(shí)際上,現(xiàn)代C語(yǔ)言編譯器已經(jīng)足夠聰明,它會(huì)根據(jù)上下文編譯C語(yǔ)言代碼。 應(yīng)該明白,i++和++i的效率差異主要來(lái)自于處理i++時(shí),需要先保存i的當(dāng)前值留作稍后使用。如果之后沒(méi)有人使用i的當(dāng)前值,也就是說(shuō)沒(méi)有C語(yǔ)言代碼讀取i++的值,編譯器實(shí)在沒(méi)有必要保存i的當(dāng)前值了,因此就會(huì)將這一步優(yōu)化掉。 為了便于分析,我們編寫(xiě)下面這樣的C語(yǔ)言代碼:
int i = 0;i++;++i;
與上面的例子相比,區(qū)別在于在執(zhí)行i++時(shí),沒(méi)有人關(guān)心i的當(dāng)前值了。查看這段C語(yǔ)言代碼對(duì)應(yīng)的匯編代碼:

3567966c-b0b2-11ec-aa7f-dac502259ad0.png

顯然,i++和++i對(duì)應(yīng)的指令是一模一樣的,不再有執(zhí)行效率上的差異。 C語(yǔ)言中的i++和++i是有區(qū)別的,這就有可能帶來(lái)效率上的差異。如果有代碼關(guān)心i++執(zhí)行時(shí)的i當(dāng)前值,程序在對(duì)i進(jìn)行自加操作時(shí),將不得不先保存i的當(dāng)前值,而++i就無(wú)需保存當(dāng)前值,這就會(huì)帶來(lái)效率上的差異。如果沒(méi)人關(guān)心i++的當(dāng)前值,那么現(xiàn)代大多數(shù)C語(yǔ)言編譯器將會(huì)將這一差異優(yōu)化掉,此時(shí)i++和++i不再有效率上的差異。

審核編輯 :李倩

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀(guān)點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • C語(yǔ)言
    +關(guān)注

    關(guān)注

    180

    文章

    7581

    瀏覽量

    135589
  • C++
    C++
    +關(guān)注

    關(guān)注

    21

    文章

    2090

    瀏覽量

    73405
  • 編譯器
    +關(guān)注

    關(guān)注

    1

    文章

    1608

    瀏覽量

    48979

原文標(biāo)題:C/C++靈魂拷問(wèn):++i與i++哪個(gè)執(zhí)行效率高?有什么區(qū)別?

文章出處:【微信號(hào):gh_c472c2199c88,微信公眾號(hào):嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    優(yōu)秀實(shí)踐:I3C共享總線(xiàn)上的I2C器件

    電子發(fā)燒友網(wǎng)站提供《優(yōu)秀實(shí)踐:I3C共享總線(xiàn)上的I2C器件.pdf》資料免費(fèi)下載
    發(fā)表于 09-06 09:49 ?0次下載
    優(yōu)秀實(shí)踐:<b class='flag-5'>I3C</b>共享總線(xiàn)上的<b class='flag-5'>I2C</b>器件

    I3C接口通信基礎(chǔ)

    I 2 CI3C之間顯著的變化是時(shí)鐘速度。I 2 C 通常為 100 kHz、400 kHz 或 1 MHz,而
    發(fā)表于 05-03 10:22 ?2967次閱讀
    <b class='flag-5'>I3C</b>接口通信基礎(chǔ)

    STM32F103系列做PMBus的主機(jī)和I2C再配置上有什么區(qū)別嗎?

    有沒(méi)有人用STM32F103系列做過(guò)PMBus的主機(jī)呀?和I2C再配置上有什么區(qū)別嗎?
    發(fā)表于 03-13 07:41

    什么是I2C協(xié)議 I2C總線(xiàn)的控制邏輯

    在實(shí)際使用過(guò)程中,I2C比較容易出現(xiàn)的一個(gè)問(wèn)題就是死鎖 ,死鎖在I2C中主要表現(xiàn)為:I2C死鎖時(shí)表現(xiàn)為SCL為,SDA一直為低。
    發(fā)表于 03-12 09:17 ?772次閱讀
    什么是<b class='flag-5'>I2C</b>協(xié)議 <b class='flag-5'>I2C</b>總線(xiàn)的控制邏輯

    什么是I3C接口 I3C和SPI接口什么區(qū)別

    I3C接口使用兩根通信線(xiàn),一根是數(shù)據(jù)線(xiàn)(SDL),另一根是時(shí)鐘線(xiàn)(SCL)。與I2C總線(xiàn)一樣,I3C接口也支持多主設(shè)備(Main Master)和從設(shè)備(Slave)之間的通信。
    的頭像 發(fā)表于 03-05 16:50 ?2428次閱讀
    什么是<b class='flag-5'>I3C</b>接口 <b class='flag-5'>I3C</b>和SPI接口<b class='flag-5'>有</b><b class='flag-5'>什么區(qū)別</b>

    type-c和usb-c什么區(qū)別

    type-c和usb-c什么區(qū)別? Type-C 和 USB-C 都是指同一個(gè)連接標(biāo)準(zhǔn),它使用
    的頭像 發(fā)表于 02-19 10:00 ?2401次閱讀

    TC397多核之間數(shù)據(jù)訪(fǎng)問(wèn)效率什么區(qū)別?本地和全局的效率什么區(qū)別?

    TC397多核之間數(shù)據(jù)訪(fǎng)問(wèn)效率什么區(qū)別,本地和全局的效率什么區(qū)別,可不可以將電機(jī)同步ADC采
    發(fā)表于 02-06 07:42

    c語(yǔ)言,c++,java,python區(qū)別

    C語(yǔ)言、C++、Java和Python是四種常見(jiàn)的編程語(yǔ)言,各有優(yōu)點(diǎn)和特點(diǎn)。 C語(yǔ)言: C語(yǔ)言是一種面向過(guò)程的編程語(yǔ)言。它具有底層的特性,能夠?qū)τ?jì)算機(jī)硬件進(jìn)行直接操作。
    的頭像 發(fā)表于 02-05 14:11 ?1657次閱讀

    vb語(yǔ)言和c++語(yǔ)言的區(qū)別

    VB語(yǔ)言和C++語(yǔ)言是兩種不同的編程語(yǔ)言,雖然它們都屬于高級(jí)編程語(yǔ)言,但在設(shè)計(jì)和用途上有很多區(qū)別。下面將詳細(xì)比較VB語(yǔ)言和C++語(yǔ)言的區(qū)別。 設(shè)計(jì)目標(biāo): VB語(yǔ)言(Visual Bas
    的頭像 發(fā)表于 02-01 10:20 ?1793次閱讀

    藍(lán)牙串口通訊總線(xiàn)——I2C/SPI/UART

    Sensor、Touch、藍(lán)牙模塊、WIFI模塊等也都兼容這三種通信協(xié)議的1種以上。那么這三條通信總線(xiàn)什么區(qū)別呢?工業(yè)物聯(lián)網(wǎng)無(wú)線(xiàn)通信專(zhuān)業(yè)廠(chǎng)商為您一一細(xì)說(shuō)—— I2C:半雙工、同步、串行傳輸,物理上只有兩根線(xiàn)(SDA/SCL);
    的頭像 發(fā)表于 01-22 09:41 ?1242次閱讀
    藍(lán)牙串口通訊總線(xiàn)——<b class='flag-5'>I2C</b>/SPI/UART

    i2c接口和spi接口區(qū)別

    重要的區(qū)別I2C接口和SPI接口都屬于串行通信接口標(biāo)準(zhǔn),廣泛應(yīng)用在嵌入式系統(tǒng)和電子設(shè)備中。它們之間的主要區(qū)別在于通信協(xié)議、電氣特性、速度和適用場(chǎng)景等方面。 I2C接口使用兩根線(xiàn)進(jìn)行
    的頭像 發(fā)表于 12-25 15:02 ?3385次閱讀

    for循環(huán)中i++與++i區(qū)別

    i++和++i都是增加變量i的值的運(yùn)算符,但它們之間有著一些微小的區(qū)別。在這篇文章中,我將詳盡、詳實(shí)、細(xì)致地解釋i++和++
    的頭像 發(fā)表于 11-26 09:20 ?7987次閱讀

    “T”型NPC的效率怎么會(huì)比“I”型NPC的效率高呢?

    網(wǎng)上查找三電平相關(guān)的資料,特別是兩種三電平結(jié)構(gòu)的差異,經(jīng)常能看到這樣的一個(gè)結(jié)論。那就是,當(dāng)開(kāi)關(guān)頻率小于16kHz的時(shí)候,“T”型NPC的效率比“I”型NPC的效率高;當(dāng)開(kāi)關(guān)頻率高于16kHz的時(shí)候,“
    的頭像 發(fā)表于 11-09 14:29 ?2942次閱讀
    “T”型NPC的<b class='flag-5'>效率</b>怎么會(huì)比“<b class='flag-5'>I</b>”型NPC的<b class='flag-5'>效率高</b>呢?

    在STM32中的func1和func2函數(shù),哪個(gè)效率高?

    在STM32單片機(jī)中,上面的func1和func2函數(shù),哪個(gè)效率高?這是一個(gè)非常有趣的問(wèn)題。
    的頭像 發(fā)表于 11-08 09:20 ?657次閱讀

    twi和i2c總線(xiàn)什么區(qū)別

    twi和i2c總線(xiàn)什么區(qū)別,他們誰(shuí)是不是兼容誰(shuí)
    發(fā)表于 11-08 07:45