999精品在线视频,手机成人午夜在线视频,久久不卡国产精品无码,中日无码在线观看,成人av手机在线观看,日韩精品亚洲一区中文字幕,亚洲av无码人妻,四虎国产在线观看 ?

C 語言程序的理解與編譯優(yōu)化

2020-08-07 14:44:38吳元斌
現(xiàn)代計算機 2020年18期
關鍵詞:程序語言

吳元斌

(重慶三峽學院計算機科學與工程學院,重慶404000)

0 引言

在多年的C 語言教學實踐中發(fā)現(xiàn),不少初學者對C 語言中運算求值存在一些模糊認識。很多人認為C語言的算術(shù)表達式求值順序與數(shù)學中算術(shù)表達式的求值順序相同,即先乘除、后加減。C 語言標準規(guī)定了運算符間的優(yōu)先級及同級運算的結(jié)合性[1],也是乘除運算的優(yōu)先級高于加減運算,因此對于表達式a+b-c*d,其運算順序看起來是:*、+、-,但事實上并非如此。

一些習題包括一個變量多次自增(或自減)求和的表達式,如:(a++)+(a++)。C 語言并沒有規(guī)定這兩個自增運算與相加的求值順序。通常的理解是:先求左邊自增的值,再求另右邊自增的值,最后將兩個值相加,但實際上,有些編譯器進行了優(yōu)化:先進行兩次自增,然后再將兩個a 相加。還有其它依賴于編譯器的問題,出現(xiàn)在習題或思考題甚至考試題中。這種情況是應該避免的,因此程序的運行結(jié)果是依賴于編譯器的,在不同的編譯器下運行結(jié)果可能不同。

為了清楚的理解C 語言教學中存在的一些編譯相關的問題,使初學者編寫與不依賴于編譯器的C 語言程序,本文將列舉一些典型的C 語言示例程序,給出了它們在集成開發(fā)環(huán)境Eclipse + MinGW GCC、LCCWin32 以及在Visual Studio 2019 下的運行結(jié)果對比。由于多數(shù)示例程序的運行結(jié)果存在一些差異,進一步展示、對照和分析了源程序在開源編譯器MinGW GCC和LCC 下目標程序的反匯編程序,目標程序的反匯編程序是利用Eclipse+MinGW GCC、LCC-Win32 兩種集成開發(fā)環(huán)境調(diào)試程序環(huán)境下得到的。本文還對編譯器翻譯算術(shù)表達式的基本思想進行說明,并分析編譯器在表達式運算求值順序?qū)崿F(xiàn)中的具體差異。

1 示例源程序及其目標程序反匯編分析

1.1 算術(shù)表達式中運算的次序

算術(shù)表達式是用二元運算符+、-、*、/和圓括號連接起來的滿足語法和語義規(guī)則的式子,C 語言規(guī)定了其中運算符的優(yōu)先級和結(jié)合性,如圓括號的優(yōu)先級最高,之后是乘除,加減最低,同一優(yōu)先級的兩個算術(shù)運算符的結(jié)合性是從左到右,但C 語言算術(shù)表達式中運算的次序不等同于數(shù)學中運算的次序。通過分析一些C 語言教材[2-4]中給出的容易引起模糊認識的示例,用下列典型程序段進行說明:

照數(shù)學運算規(guī)則,賦值語句右邊的表達式運算順序依次是:*、/、*、+、-,運算過程和結(jié)果可以表示為:a* 4 + b/ 2- c * b →12 + b/ 2- c * b →12 + 2 - c *b →12+2-20 →14-20 →-6。雖然C 語言程序中表達式的運行結(jié)果值也是-6,但運算次序與上述數(shù)學運算次序是不同的。編譯程序是用下面的文法對算術(shù)表達式進行了嚴格的定義,文法指明了運算符的結(jié)合性和優(yōu)先級,算術(shù)表達式的文法為[5-6]:

其中非終結(jié)符E 表示一組以+號或-號分隔的項所組成的表達式;T 表示由一組以*號或/號分隔的因子所組成的項,F(xiàn) 表示因子,它是用括號括起來的表達式或標識符id。變量和常數(shù)被詞法分析程序歸類為標識符id。利用該文法生成上面表達式的語法樹是唯一的,如圖1 所示。

圖1 生成示例表達式的語法樹

其中,圖中的id 對應的變量或常量從左到右依次是a、4、b、2、c、b。通過語法制導翻譯(類似于語法樹自下而上或遞歸下降分析),上述表達式被翻譯成為每條指令最多一個運算符的指令序列[5]。將上述表達式計算的同一示例程序在兩種集成環(huán)境中調(diào)試視圖截圖(源程序及反匯編)如圖2 所示,左圖是Eclipse+Min-GW GCC,右圖LCC-Win32,兩個子圖的上面部分都是源程序,下面都是目標程序的反匯編程序。左、右兩圖放在一起是便于對比分析(下面示例都采用這種方式)。

其中左圖中最后用紅色邊框包括的列為手工添加的代碼說明,右圖的反匯編用圓括號包含了變量名。兩種編譯器采用的指令不完全相同,左圖中除法比右圖的實現(xiàn)復雜,詳細分析可見文獻[7]??梢钥闯?,兩個編譯器處理表達式的運算次序相同的,它們的次序都是:*、/、+、*、-,顯然與數(shù)學運算規(guī)則不同。這個運算次序也就是圖1 中的語法樹按運算符從下到上、從左到右得到的運算次序。

實際上,C 語言算術(shù)表達式求值中在處理當前運算符時,要與其右邊相鄰的運算符進行比較,若當前運算符高于其相鄰右邊的運算符,或者它們的優(yōu)先級相同,且結(jié)合性是從左到右,則完成當前運算,否則先處理其右邊相鄰的運算,其右邊相鄰的運算也要處理方法也是相同的,如此等等,直到所有運算完成。編譯器自下而上的翻譯法在表達式最后添加了一個運算符$(優(yōu)先級最低)運算符[5-6],它就沒有右邊相鄰的運算符了。

圖2 兩種編譯器表達式運算次序相同

1.2 表達式運算次序的副作用

很多時候無論是按數(shù)學運算次序還是按編譯程序指定的次序運算,算術(shù)表達式的值是相同的,看起來不用區(qū)分C 語言表達式的運算次序,但并不總是這樣。當表達式中存在項與項之間的值存在依賴關系時,結(jié)果就可能不同,如將上面的程序段修改為:

所作的修改只是將上例的表達式最后一項改為(b=2),由于第2 項中的b 與最后一項的b 存在依賴關系,編譯器MinGW GCC 與LCC 對表達式的優(yōu)化處理不同,程序在兩種環(huán)境下的運行結(jié)果不同,分別為4和3,在Visual Studio 2019 下運行的結(jié)果也為3。

該程序在MinGW GCC 與LCC 編譯反匯編對比如圖3 所示,分析發(fā)現(xiàn)LCC 編譯器中b 賦值為2 的指令被提前,MinGW GCC 則沒有,兩條指令分別用紅色方框標出,表達式中其它的運算次序還是與上例相同??梢?,兩種編譯器只是對“(b=2)”的處理不同,在LCC環(huán)境下表達式中所有b 的值都是2,而在MinGW GCC環(huán)境下只是最后的b 的值為2,因此導致了程序的運行結(jié)果不同,所以該程序的運行結(jié)果完全依賴于編譯環(huán)境。

圖3 兩種編譯器對運算次序優(yōu)化存在差異

1.3 函數(shù)實參的求值順序處理

C 語言也沒有指定函數(shù)各參數(shù)的求值順序[1]。在函數(shù)調(diào)用時,有的編譯器是從左到右,有的則是從右到左。參考文獻[3]有一個類似于下面的程序段:

通過在MinGW GCC 與LCC 下運行程序后發(fā)現(xiàn),第1 個輸出語句的運行結(jié)果不同,分別為“3 3”和“3 0”,而第1 個輸出語句在Visual Studio 2019 下運行的結(jié)果為“3 3”。三種環(huán)境下第2 個輸出語句的結(jié)果為“3 3 3”,即變量a、b、c 的值最后都是3,這說明在三種環(huán)境下編譯器的求值順序都是從右到左。由于第1 個輸出存在差異,為了找出問題的原因,將該程序在Min-GW GCC 與LCC 編譯反匯編進行對比,如圖4 所示。

圖4 兩種編譯器對實參求值的處理不同

分析圖4 發(fā)現(xiàn),兩種編譯器都是先計算右邊的參數(shù)(左圖中第1 個加框的部分為第1 個參數(shù)的求值,右圖中為第1 個加框指令的前6 條指令),然后計算左邊的參數(shù)。但是它們處理值的方式不同,對MinGW GCC環(huán)境中,printf 輸出的是變量a 的最終值(左圖第2 個加框部分)。但在LCC 環(huán)境中是將兩個實參的值分別保存在寄存器rbx、rdi 中,計算一個保存一個(右圖中兩個加框指令),rbx 的值為0,rdi 的值為3??梢姳M管都是從右到左計算函數(shù)實參,但形參的值卻不同,程序的執(zhí)行結(jié)果也依賴于編譯程序,只有仔細分析它們的反匯編程序,才能搞清楚其中的原因。

1.4 自增自減運算的副作用

函數(shù)調(diào)用、嵌套賦值語句、自增與自減運算符都有可能產(chǎn)生“副作用”—在對表達式求值的同時,修改了某些變量的值[1]。一些C 語言教材的練習中常常包含類似于下面的程序段:

上述程序在MinGW GCC 和LCC 下下的運行結(jié)果分別為“11 14 7 7”和“11 13 7 7”,在Visual Studio 2019下的運行結(jié)果為“11 14 7 7”。可見輸出結(jié)果存在差異,為了找出問題的原因,將該程序在MinGW GCC 與LCC編譯反匯編進行對比,如圖5 所示。

圖5 兩種編譯器對自增運算處理不同

在圖5 中,分別對兩種環(huán)境下相關指令功能進行了說明(紅色框線中)??梢钥闯?,兩種環(huán)境下對后綴的自增運算處理相同,即先累加,再自增,再累加,再自增,累加的結(jié)果都是11。但對于前綴的自增運算處理則不同,在MinGW GCC 下前綴的自增運算被優(yōu)化(在加法運算前進行),而在LCC 下前綴的自增運算則不同,并沒有優(yōu)化,所以前綴的自增運算相加的結(jié)果不同,分別為14 和13。因此一個變量多次自增(或自減)求和表達式的值也依賴于編譯器。

2 結(jié)語

本文討論了C 語言教學中一些容易引起初學者產(chǎn)生模糊認識的典型問題,通過不同編譯環(huán)境對目標程序的反匯編對照與分析,能夠清楚地看到這些程序的運行結(jié)果依賴于編譯器,不同編譯器可能產(chǎn)生不同的結(jié)果。這樣的問題還很多,雖然初學者也不一定能夠理解這些分析及編譯原理的具體細節(jié),但教學中應該讓他們知道編寫依賴于編譯器的程序是不好的習慣。在任何一種編程語言中,如果代碼的執(zhí)行結(jié)果與求值順序有關,則都是不好的程序設計風格。很自然,有必要了解哪些問題需要避免,但是,如果不知道這些問題在各種機器上是如何解決的,就最好不要嘗試運用某種特殊的實現(xiàn)方式[1]。因此,C 語言教學一個很重要的工作是讓學生學會正確的編程方法,培養(yǎng)良好的編程習慣[2],避免編寫執(zhí)行結(jié)果依賴于編譯器的C 語言程序。

猜你喜歡
程序語言
語言是刀
文苑(2020年4期)2020-05-30 12:35:30
試論我國未決羈押程序的立法完善
人大建設(2019年12期)2019-05-21 02:55:44
讓語言描寫搖曳多姿
失能的信仰——走向衰亡的民事訴訟程序
“程序猿”的生活什么樣
多向度交往對語言磨蝕的補正之道
英國與歐盟正式啟動“離婚”程序程序
累積動態(tài)分析下的同聲傳譯語言壓縮
創(chuàng)衛(wèi)暗訪程序有待改進
我有我語言
主站蜘蛛池模板: 呦女精品网站| 久久6免费视频| 国产成人亚洲精品色欲AV| 色亚洲成人| 欧美在线中文字幕| 国产一级毛片网站| 国产亚洲欧美在线专区| 午夜欧美理论2019理论| 88国产经典欧美一区二区三区| 99视频只有精品| 久视频免费精品6| 免费一级α片在线观看| 伊人无码视屏| 青青国产成人免费精品视频| 天天综合天天综合| 五月丁香在线视频| 九九精品在线观看| 人妻丰满熟妇AV无码区| 伊伊人成亚洲综合人网7777| 97久久超碰极品视觉盛宴| 免费看黄片一区二区三区| 国产成人凹凸视频在线| 无码中文字幕乱码免费2| 在线观看国产小视频| 成年A级毛片| 一本久道久综合久久鬼色| 激情爆乳一区二区| 亚洲性影院| 最新国产麻豆aⅴ精品无| 毛片免费高清免费| 日韩无码一二三区| 欧美亚洲欧美区| 在线观看视频99| 亚洲无线一二三四区男男| 亚洲国产成人麻豆精品| 国产精品免费电影| 欧美精品色视频| 999精品在线视频| 久久久精品久久久久三级| 欧美精品成人一区二区视频一| av一区二区无码在线| 中文字幕欧美日韩| 国产成人一区| 高清欧美性猛交XXXX黑人猛交| 国产无吗一区二区三区在线欢| 亚洲天堂2014| 国产精品视频导航| 国产在线精彩视频二区| 精品成人免费自拍视频| 99在线视频网站| 欧美福利在线播放| 亚洲 欧美 偷自乱 图片| 亚洲综合第一页| 欧美成人精品一级在线观看| 91日本在线观看亚洲精品| 国产黄色片在线看| 国产亚洲欧美日韩在线一区| 亚洲AV无码精品无码久久蜜桃| 无码高潮喷水专区久久| 欧美日韩资源| 色婷婷综合在线| 国产永久免费视频m3u8| 久久久无码人妻精品无码| 亚洲第一福利视频导航| 日本www色视频| 久久久久国产精品熟女影院| 国产一区二区三区视频| 亚洲三级色| 久久女人网| 国产特级毛片| 人妻出轨无码中文一区二区| 国产成人狂喷潮在线观看2345| 欧美日韩91| 亚洲毛片在线看| 青青青国产在线播放| 欧美激情网址| 一边摸一边做爽的视频17国产 | 91午夜福利在线观看| 亚洲久悠悠色悠在线播放| 亚洲AV无码乱码在线观看代蜜桃| 欧美日韩成人在线观看 | 亚洲第一黄色网址|