最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺談C/C++ 語(yǔ)言中的表達(dá)式求值

 更新時(shí)間:2016年07月20日 10:59:42   投稿:jingxian  
下面小編就為大家?guī)?lái)一篇淺談C/C++ 語(yǔ)言中的表達(dá)式求值。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

經(jīng)??梢栽谝恍┯懻摻M里看到下面的提問(wèn):“誰(shuí)知道下面C語(yǔ)句給n賦什么值?”

m = 1; n = m+++m++;

最近有位不相識(shí)的朋友發(fā)email給我,問(wèn)為什么在某個(gè)C++系統(tǒng)里,下面表達(dá)式打印出兩個(gè)4,而不是4和5:

a = 4; cout << a++ << a;

C++ 不是規(guī)定 << 操作左結(jié)合嗎?是C++ 書(shū)上寫(xiě)錯(cuò)了,還是這個(gè)系統(tǒng)的實(shí)現(xiàn)有問(wèn)題?

注:運(yùn)行a = 4; cout << a++ << a;

如在Visual c++ 6.0中,得到的是4和4;在Visual Studio中,得到的是4和5.

到底哪個(gè)是對(duì)的呢?請(qǐng)?jiān)斂春竺娴姆治觯?/span>

 要弄清這些,需要理解的一個(gè)問(wèn)題是:如果程序里某處修改了一個(gè)變量(通過(guò)賦值、增量/減量操作等),什么時(shí)候從該變量能夠取到新值?有人可能說(shuō),“這算什么問(wèn)題!我修改了變量,再?gòu)倪@個(gè)變量取值,取到的當(dāng)然是修改后的值!”其實(shí)事情并不這么簡(jiǎn)單。

C/C++ 語(yǔ)言是“基于表達(dá)式的語(yǔ)言”,所有計(jì)算(包括賦值)都在表達(dá)式里完成。“x = 1;”就是表達(dá)式 “x = 1”后加表示語(yǔ)句結(jié)束的分號(hào)。要弄清程序的意義,首先要理解表達(dá)式的意義,也就是:1)表達(dá)式所確定的計(jì)算過(guò)程;2)它對(duì)環(huán)境(可以把環(huán)境看作 當(dāng)時(shí)可用的所有變量)的影響。如果一個(gè)表達(dá)式(或子表達(dá)式)只計(jì)算出值而不改變環(huán)境,我們就說(shuō)它是引用透明的,這種表達(dá)式早算晚算對(duì)其他計(jì)算沒(méi)有影響(不 改變計(jì)算的環(huán)境。當(dāng)然,它的值可能受到其他計(jì)算的影響)。如果一個(gè)表達(dá)式不僅算出一個(gè)值,還修改了環(huán)境,就說(shuō)這個(gè)表達(dá)式有副作用(因?yàn)樗嘧隽祟~外的事)。a++ 就是有副作用的表達(dá)式。這些說(shuō)法也適用于其他語(yǔ)言里的類(lèi)似問(wèn)題。

現(xiàn)在問(wèn)題變成:如果C/C++ 程序里的某個(gè)表達(dá)式(部分)有副作用,這種副作用何時(shí)才能實(shí)際體現(xiàn)到使用中?為使問(wèn)題更清楚,我們假定程序里有代碼片段 “...a[i]++ ... a[j] ...”,假定當(dāng)時(shí)i與j的值恰好相等(a[i] 和a[j] 正好引用同一數(shù)組元素);假定a[i]++ 確 實(shí)在a[j] 之前計(jì)算;再假定其間沒(méi)有其他修改a[i] 的動(dòng)作。在這些假定下,a[i]++ 對(duì) a[i] 的修改能反映到 a[j] 的求值中嗎? 注意:由于 i 與 j 相等的問(wèn)題無(wú)法靜態(tài)判定,在目標(biāo)代碼里,這兩個(gè)數(shù)組元素訪(fǎng)問(wèn)(對(duì)內(nèi)存的訪(fǎng)問(wèn))必然通過(guò)兩段獨(dú)立代碼完成?,F(xiàn)代計(jì)算機(jī)的計(jì)算都在寄 存器里做,問(wèn)題現(xiàn)在變成:在取 a[j] 值的代碼執(zhí)行之前,a[i] 更新的值是否已經(jīng)被(從寄存器)保存到內(nèi)存?如果了解語(yǔ)言在這方面的規(guī)定,這個(gè)問(wèn) 題的答案就清楚了。

程序語(yǔ)言通常都規(guī)定了執(zhí)行中變量修改的最晚實(shí)現(xiàn)時(shí)刻(稱(chēng)為順序點(diǎn)、序點(diǎn)或執(zhí)行點(diǎn))。程序執(zhí)行中存在一系列順序點(diǎn)

(時(shí) 刻),語(yǔ)言保證一旦執(zhí)行到達(dá)一個(gè)順序點(diǎn),在此之前發(fā)生的所有修改(副作用)都必須實(shí)現(xiàn)(必須反應(yīng)到隨后對(duì)同一存儲(chǔ)位置的訪(fǎng)問(wèn)中),在此之后的所有修改都還 沒(méi)有發(fā)生。在順序點(diǎn)之間則沒(méi)有任何保證。對(duì)C/C++ 語(yǔ)言這類(lèi)允許表達(dá)式有副作用的語(yǔ)言,順序點(diǎn)的概念特別重要。

現(xiàn)在上面問(wèn)題的回答已經(jīng)很清楚了:如果在a[i]++ 和a[j] 之間存在一個(gè)順序點(diǎn),那么就能保證a[j] 將取得修改之后的值;否則就不能保證。

C/C++語(yǔ)言定義(語(yǔ)言的參考手冊(cè))明確定義了順序點(diǎn)的概念。順序點(diǎn)位于

1. 每個(gè)完整表達(dá)式結(jié)束時(shí)。完整表達(dá)式包括變量初始化表達(dá)式,表達(dá)式語(yǔ)句,return語(yǔ)句的表達(dá)式,以及條件、循環(huán)和switch語(yǔ)句的控制表達(dá)式(for頭部有三個(gè)控制表達(dá)式);

2. 運(yùn)算符 &&、||、?: 和逗號(hào)運(yùn)算符的第一個(gè)運(yùn)算對(duì)象計(jì)算之后;

3. 函數(shù)調(diào)用中對(duì)所有實(shí)際參數(shù)和函數(shù)名表達(dá)式(需要調(diào)用的函數(shù)也可能通過(guò)表達(dá)式描述)的求值完成之后(進(jìn)入函數(shù)體之前)。

假設(shè)時(shí)刻ti和ti+1是前后相繼的兩個(gè)順序點(diǎn),到了ti+1,任何C/C++ 系統(tǒng)(VC、BC等都是C/C++系統(tǒng))都必須實(shí)現(xiàn)ti之后發(fā)生的所有副 作用。當(dāng)然它們也可以不等到時(shí)刻ti+1,完全可以選擇在時(shí)段 [t, ti+1] 之間的任何時(shí)刻實(shí)現(xiàn)在此期間出現(xiàn)的副作用,因?yàn)镃/C++ 語(yǔ)言允許 這些選擇。

前面討論中假定了a[i]++ 在a[i] 之前做。在一個(gè)程序片段里a[i]++ 究竟是否先做,還與它所在的表達(dá)式確定的計(jì)算過(guò)程有關(guān)。我們都熟悉C/C++ 語(yǔ)言有關(guān)優(yōu)先級(jí)、結(jié)合性和括號(hào)的規(guī)定,而出現(xiàn)多個(gè)運(yùn)算對(duì)象時(shí)的計(jì)算順序卻常常被人們忽略??聪旅胬樱?br />

(a + b) * (c + d) fun(a++, b, a+5)

這里“*”的兩個(gè)運(yùn)算對(duì)象中哪個(gè)先算?fun及其三個(gè)參數(shù)按什么順序計(jì)算?對(duì)第一個(gè)表達(dá)式,采用任何計(jì)算順序都沒(méi)關(guān)系,因?yàn)槠渲械淖颖磉_(dá)式都是引用透明的。 第二個(gè)例子里的實(shí)參表達(dá)式出現(xiàn)了副作用,計(jì)算順序就非常重要了。少數(shù)語(yǔ)言明確規(guī)定了運(yùn)算對(duì)象的計(jì)算順序(Java規(guī)定從左到右),C/C++ 則有意不予 規(guī)定,既沒(méi)有規(guī)定大多數(shù)二元運(yùn)算的兩個(gè)對(duì)象的計(jì)算順序(除了&&、|| 和 ,),也沒(méi)有規(guī)定函數(shù)參數(shù)和被調(diào)函數(shù)的計(jì)算順序。在計(jì)算第二 個(gè)表達(dá)式時(shí),首先按照某種順序算fun、a++、b和a+5,之后是順序點(diǎn),而后進(jìn)入函數(shù)執(zhí)行。

不少書(shū)籍在這些問(wèn)題上有錯(cuò)(包括一些很流行的書(shū))。例如說(shuō)C/C++ 先算左邊(或右邊),或者說(shuō)某個(gè)C/C++ 系統(tǒng)先計(jì)算某一邊。這些說(shuō)法都是錯(cuò)誤 的!一個(gè)C/C++ 系統(tǒng)可以永遠(yuǎn)先算左邊或永遠(yuǎn)先算右邊,也可以有時(shí)先算左邊有時(shí)先算右邊,或在同一表達(dá)式里有時(shí)先算左邊有時(shí)先算右邊。不同系統(tǒng)可能采 用不同的順序(因?yàn)槎挤险Z(yǔ)言標(biāo)準(zhǔn));同一系統(tǒng)的不同版本完全可以采用不同方式;同一版本在不同優(yōu)化方式下,在不同位置都可能采用不同順序。因?yàn)檫@些做法 都符合語(yǔ)言規(guī)范。在這里還要注意順序點(diǎn)的問(wèn)題:即使某一邊的表達(dá)式先算了,其副作用也可能沒(méi)有反映到內(nèi)存,因此對(duì)另一邊的計(jì)算沒(méi)有影響。

回到前面的例子:“誰(shuí)知道下面C語(yǔ)句給n賦什么值?”

m = 1; n = m++ +m++;

正確回答是:不知道!語(yǔ)言沒(méi)有規(guī)定它應(yīng)該算出什么,結(jié)果完全依賴(lài)具體系統(tǒng)在具體上下文中的具體處理。其中牽涉到運(yùn)算對(duì)象的求值順序和變量修改的實(shí)現(xiàn)時(shí)刻問(wèn)題。對(duì)于:

cout << a++ << a;

我們知道它是

(cout.operator <<(a++)).operator << (a);

的簡(jiǎn)寫(xiě)。先看外層函數(shù)調(diào)用,這里需要算出所用函數(shù),還需要計(jì)算a的值。語(yǔ)言沒(méi)有規(guī)定哪個(gè)先算。如果真的先算函數(shù),這一計(jì)算中出現(xiàn)了另一次函數(shù)調(diào)用,在被調(diào) 函數(shù)體執(zhí)行前有一個(gè)順序點(diǎn),那時(shí)a++的副作用就會(huì)實(shí)現(xiàn)。如果是先算參數(shù),求出a的值

4,而后計(jì)算函數(shù)時(shí)的副作用當(dāng)然不會(huì)改變它(這種情況下輸出兩個(gè) 4)。當(dāng)然,這些只是假設(shè),實(shí)際應(yīng)該說(shuō)的是:這種東西根本不該寫(xiě),討論其效果沒(méi)有意義。

有人可能說(shuō),為什么人們?cè)O(shè)計(jì) C/C++時(shí)不把順序規(guī)定清楚,免去這些麻煩?C/C++ 語(yǔ)言的做法完全是有意而為,其目的就是允

許編譯器采用任何求值順序,使編譯器在優(yōu)化中可以根據(jù)需要調(diào)整實(shí)現(xiàn)表達(dá)式求值的指令序列,以得到效率更高的代碼。

像 Java那樣嚴(yán)格規(guī)定表達(dá)式的求值順序和效果,不僅限制了語(yǔ)言的實(shí)現(xiàn)方式,還要求更頻繁的內(nèi)存訪(fǎng)問(wèn)(以實(shí)現(xiàn)副作用),這些可能帶來(lái)可觀(guān)的效率損失。應(yīng)該 說(shuō),在這個(gè)問(wèn)題上,C/C++和Java的選擇都貫徹了它們各自的設(shè)計(jì)原則,各有所獲(C/C++ 潛在的效率,Java更清晰的程序行為),當(dāng)然也都有 所失。還應(yīng)該指出,大部分程序設(shè)計(jì)語(yǔ)言實(shí)際上都采用了類(lèi)似C/C++的規(guī)定。

討論了這么多,應(yīng)該得到什么結(jié)論呢?C/C++ 語(yǔ)言的規(guī)定告訴我們,任何依賴(lài)于特定計(jì)算順序、依賴(lài)于在順序點(diǎn)之間實(shí)現(xiàn)修改效果的表達(dá)式,其結(jié)果都沒(méi)有保證。程序設(shè)計(jì)中應(yīng)該貫徹的規(guī)則是:如果在任何“完整表達(dá)式”(形成一段由順序點(diǎn)結(jié)束的計(jì)算)里存在對(duì)同一“變量”的多個(gè)引用,那么表達(dá)式里就不應(yīng)該出現(xiàn)對(duì)這一“變量”的副作用。否則就不能保證得到預(yù)期結(jié)果。注意:這里的問(wèn)題不是在某個(gè)系統(tǒng)里試一試的問(wèn)題,因?yàn)槲覀儾豢赡茉囼?yàn)所有可能的表達(dá)式組合形式以及所有可能的上下文。這里討論的是語(yǔ)言,而不是某個(gè)實(shí)現(xiàn)。總而言之,絕不要寫(xiě)這種表達(dá)式,否則我們或早或晚會(huì)某種環(huán)境中遇到麻煩。

后記:去年參加一個(gè)學(xué)術(shù)會(huì)議,看到有同行寫(xiě)文章討論某個(gè)C系統(tǒng)里表達(dá)式究竟按什么順序求值,并總結(jié)出一些“規(guī)律”。從討論中了解到某“程序員水平考試”出 了這類(lèi)題目。這使我感到很不安。今年給一個(gè)教師學(xué)習(xí)班講課,發(fā)現(xiàn)許多專(zhuān)業(yè)課教師也對(duì)這一基本問(wèn)題也不甚明了,更覺(jué)得問(wèn)題確實(shí)嚴(yán)重。因此整理出這篇短文供大 家參考。

以上這篇淺談C/C++ 語(yǔ)言中的表達(dá)式求值就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • 詳解C++ 桶排序(BucketSort)

    詳解C++ 桶排序(BucketSort)

    這篇文章主要介紹了C++桶排序,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之二叉樹(shù)詳解

    C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)之二叉樹(shù)詳解

    二叉樹(shù)(Binary tree)是樹(shù)形結(jié)構(gòu)的一個(gè)重要類(lèi)型。許多實(shí)際問(wèn)題抽象出來(lái)的數(shù)據(jù)結(jié)構(gòu)往往是二叉樹(shù)形式。本文將通過(guò)示例詳細(xì)講解一下二叉樹(shù),需要的可以參考一下
    2022-03-03
  • 用C語(yǔ)言實(shí)現(xiàn)貪吃蛇小游戲

    用C語(yǔ)言實(shí)現(xiàn)貪吃蛇小游戲

    這篇文章主要為大家詳細(xì)介紹了用C語(yǔ)言實(shí)現(xiàn)貪吃蛇小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • VisualStudio2022制作多項(xiàng)目模板及Vsix插件的實(shí)現(xiàn)

    VisualStudio2022制作多項(xiàng)目模板及Vsix插件的實(shí)現(xiàn)

    本文主要介紹了VisualStudio2022制作多項(xiàng)目模板及Vsix插件的實(shí)現(xiàn),文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-06-06
  • Qt添加MSVC2017編譯器的實(shí)現(xiàn)方法

    Qt添加MSVC2017編譯器的實(shí)現(xiàn)方法

    Qt添加MSVC2017編譯器是開(kāi)發(fā)者在Windows平臺(tái)上進(jìn)行Qt應(yīng)用程序開(kāi)發(fā)的重要步驟,本文詳細(xì)介紹了如何為Qt配置MSVC2017編譯器的具體步驟,感興趣的可以了解一下
    2023-09-09
  • Qt菜單QMenu和菜單欄QMenuBar及自定義菜單用法

    Qt菜單QMenu和菜單欄QMenuBar及自定義菜單用法

    本文主要介紹了Qt菜單QMenu和菜單欄QMenuBar及自定義菜單用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • C語(yǔ)言超全面define預(yù)處理指令的使用說(shuō)明

    C語(yǔ)言超全面define預(yù)處理指令的使用說(shuō)明

    C語(yǔ)言里可以用#define定義一個(gè)標(biāo)識(shí)符來(lái)表示一個(gè)常量。特點(diǎn)是:定義的標(biāo)識(shí)符不占內(nèi)存,只是一個(gè)臨時(shí)的符號(hào),預(yù)編譯后這個(gè)符號(hào)就不存在了,也不做類(lèi)型定義。預(yù)編譯又叫預(yù)處理
    2022-04-04
  • C++中constexpr與模板元編程的基礎(chǔ)、常見(jiàn)問(wèn)題、易錯(cuò)點(diǎn)及其規(guī)避策略

    C++中constexpr與模板元編程的基礎(chǔ)、常見(jiàn)問(wèn)題、易錯(cuò)點(diǎn)及其規(guī)避策略

    C++編譯時(shí)計(jì)算允許程序在編譯階段完成計(jì)算任務(wù),constexpr與模板元編程是C編譯時(shí)計(jì)算的兩把利劍,它們不僅能夠提升程序的性能,還能增強(qiáng)代碼的健壯性和可維護(hù)性,通過(guò)避開(kāi)本文闡述的易錯(cuò)點(diǎn),開(kāi)發(fā)者可以更加得心應(yīng)手地運(yùn)用這些特性,編寫(xiě)出既高效又優(yōu)雅的C代碼
    2024-06-06
  • C++中l(wèi)ist的用法實(shí)例講解

    C++中l(wèi)ist的用法實(shí)例講解

    list是順序容器的一種,list是一個(gè)雙向鏈表,使用list需要包含頭文件list,這篇文章主要給大家介紹了關(guān)于C++中l(wèi)ist的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-11-11
  • C++中二維map實(shí)現(xiàn)讀和寫(xiě)

    C++中二維map實(shí)現(xiàn)讀和寫(xiě)

    在C++中,可以使用std::map來(lái)實(shí)現(xiàn)二維映射,本文主要介紹了C++中二維map實(shí)現(xiàn)讀和寫(xiě),具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12

最新評(píng)論

江西省| 东兴市| 万年县| 重庆市| 喜德县| 永济市| 台南市| 涞源县| 巩留县| 新野县| 正定县| 来安县| 兴海县| 崇仁县| 晋城| 山阴县| 万州区| 广东省| 海晏县| 平江县| 开原市| 河南省| 华池县| 高密市| 开阳县| 普宁市| 乌拉特前旗| 家居| 东乡县| 达州市| 康乐县| 潼关县| 乌兰县| 老河口市| 年辖:市辖区| 饶平县| 兴业县| 新绛县| 许昌县| 吴堡县| 通道|