導航:首頁 > 源碼編譯 > 編譯器使用順序

編譯器使用順序

發布時間:2022-12-13 06:25:11

A. C語言程序的運行順序

1、這個涉及到函數的調用約定
運行結果跟編譯器有一定的關系,不同的編譯器參數的入棧的順序不同
一般的編譯器是從右到左
如fun(a,b)這個函數調用,是先計算參數b,入棧,再計算參數a,入棧
2、printf("%d
%d",
a++,++a);
//先計算++a,先自增,a的值變為2,將2入棧
再來計算a++,將a的值2入棧,再使a自增,a的值變為3
printf("
%d\n",a);
//a的值已經變為3了
3、printf(%d
%d",
++a.a++);//先計算a++,將a的值1入棧,再使a自增,a的值變為2,再來計算++a,先自增,a的值為3,將3入棧,輸出3
1
printf("
%d\n",a);
//輸出3
4、三種調用約定:
__stdcall調用約定。兩者實質上是一致的,即函數的參數自右向左通過棧傳遞,被調用的函數在返回前清理傳送參數的內存棧,但不同的是函數名的修飾部分(關於函數名的修飾部分在後面將詳細說明)。
C調用約定(即用__cdecl關鍵字說明)和__stdcall調用約定有所不同,雖然參數傳送方面是一樣的,但對於傳送參數的內存棧卻是由調用者來維護的(也正因為如此,實現可變參數的函數只能使用該調用約定),另外,在函數名修飾約定方面也有所不同。
__fastcall調用約定是「人」如其名,它的主要特點就是快,因為它是通過寄存器來傳送參數的(實際上,它用CX和EDX傳送前兩個雙字或更小的參數,剩下的參數仍舊自右向左壓棧傳送,被調用的函數在返回前清理傳送參數的內存棧),在函數名修飾約定方面,它和前兩者均不同。

B. 編譯器生成的匯編語句執行順序為什麼與C代碼順序不同

不影響語義的前提下編譯器可以任意重排代碼順序;
在亂序執行(Out-of-Order)的CPU里,機器碼的執行也可以不按照你在「匯編」層面上看到的順序執行,只要不影響語義。
所以說這些中間步驟的順序,作為底層細節平時不需要那麼在意——它們多半跟原始源碼的順序是不一樣的。

現代優化編譯器優化的思路之一是「基於依賴的優化」(dependence-based optimization)。題主引用的CSAPP的例子:

int arith(int x, int y, int z) {
int t1 = x + y;
int t2 = z * 48;
int t3 = t1 & 0xFFFF;
int t4 = t2 * t3;
return t4;
}

所有涉及運算的值都是局部標量變數(local scalar variable),這是最便於編譯器做分析的情況,所有依賴都可以顯式分析。
由於整個函數沒有分支,這里也不需要討論控制依賴(control dependence),只要討論數據依賴(data dependence)就好。
把數據依賴圖畫出來是個DAG(這里正好是棵樹,特例了):

x y z 48
\ / \ /
t1 0xFFFF t2
\ / /
t3 /
\ /
t4

優化必須要滿足的約束是:每個節點求值之前,其子節點(依賴的數據源)必須要先求了值。
顯然,t1和t2之間沒有依賴關系,它們的相對求值順序怎樣重排都沒關系。

有本我很喜歡的書,裡面講的是各種基於依賴的優化:Optimizing Compilers for Modern Architectures - A Dependence-based Approach

以上是理論部分。

================================================================

下面來看例子。

我們可以用一個實際編譯器來看看CSAPP的例子編譯出來的結果:

.text
# -- Begin arith
.p2align 4,,15
.globl arith
.type arith, @function
arith:
.p2align 4,,7
/*.L0:*/ /* Block BB[54:2] preds: none, freq: 1.000 */
movl 8(%esp), %edx /* ia32_Load T[139:10] -:1:22 */
addl 4(%esp), %edx /* ia32_Add Iu[141:12] -:2:14 */
movzwl %dx, %edx /* ia32_Conv_I2I Iu[142:13] -:4:15 */
imull 12(%esp), %edx /* ia32_IMul Iu[143:14] -:5:15 */
leal (%edx,%edx,2), %eax /* ia32_Lea Iu[144:15] -:5:15 */
shll $0x4, %eax /* ia32_Shl Iu[146:17] -:5:15 */
ret /* ia32_Return X[152:23] -:6:3 */
.size arith, .-arith
# -- End arith

這里用的是libFirm。可見它跟CSAPP書里所說的匯編的順序又有所不同。這也是完全合理的。
這個編譯結果的順序是:

edx = y;
edx += x;
edx = zeroextend dx; // edx = edx & 0xFFFF
edx *= z;
eax = edx * 3;
eax <<= 4; // eax = eax * 16

也是完全符合依賴關系的約束的一種順序。
之所以用libFirm舉例是因為它的中間表示(Intermediate Representation)是一種程序依賴圖(Program Dependence Graph),可以很方便的看出控制與數據依賴。把CSAPP那裡例子對應的libFirm IR畫出來,是這個樣子的:
(這張圖跟我前面畫的數據依賴圖正好是左右翻轉的,不過意思一樣。(這張圖跟我前面畫的數據依賴圖正好是左右翻轉的,不過意思一樣。
Arg 0、1、2分別代表x、y、z。白色方塊是普通數據節點,黃色方塊是常量節點,藍色方塊是內存相關節點,紅色方塊是控制流節點,粉紅色方塊是特殊的開始/結束節點。)

某版LLVM生成的代碼:

; MoleID = '/tmp/webcompile/_16355_0.bc'
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-ellcc-linux"

; Function Attrs: nounwind readnone
define i32 @arith(i32 %x, i32 %y, i32 %z) #0 {
entry:
%add = add nsw i32 %y, %x
%mul = mul nsw i32 %z, 48
%and = and i32 %add, 65535
%mul1 = mul nsw i32 %mul, %and
ret i32 %mul1
}

attributes #0 = { nounwind readnone "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.ident = !{!0}

!0 = !{!"ecc 0.1.10 based on clang version 3.7.0 (trunk) (based on LLVM 3.7.0svn)"}

最終生成的x86匯編:

.text
.file "/tmp/webcompile/_15964_0.c"
.globl arith
.align 16, 0x90
.type arith,@function
arith: # @arith
# BB#0: # %entry
movl 8(%esp), %eax
addl 4(%esp), %eax
movzwl %ax, %eax
imull 12(%esp), %eax
shll $4, %eax
leal (%eax,%eax,2), %eax
retl
.Ltmp0:
.size arith, .Ltmp0-arith

.ident "ecc 0.1.10 based on clang version 3.7.0 (trunk) (based on LLVM 3.7.0svn)"
.section ".note.GNU-stack","",@progbits

GCC 4.9.2 x86-64:

arith(int, int, int):
leal (%rdx,%rdx,2), %eax
addl %edi, %esi
movzwl %si, %esi
sall $4, %eax
imull %esi, %eax
ret

Zing VM Server Compiler x86-64:

# edi: x
# esi: y
# edx: z
movl %edx, %eax
shll $0x4, %eax
leal (%rsi, %rdi, 1), %ecx
shll $0x5, %edx
addl %edx, $eax
movzwl %ecx, %edx
imull %edx, %eax

C. 編譯器生成的匯編語句執行順序為什麼與C代碼順序不同

你知道「C代碼」執行順序?C源碼文件只有書寫順序,而且書寫順序與形成可執行文件的順序本來就不是完全同的。隨便舉個例子,如for(i=0;i<N;i++){printf("%d ",i);printf("%d ",i+3);}中的i++在哪裡執行?它可不是在i<N後就執行,而是在兩個printf後的}處才執行,想與}分開都不可能!再如,在main前自定義一個函數,在main中調用這個函數時,流程又跑到哪裡去了?是不是應該理解為從後面跑到前面去執行了?……所以,談論C代碼的執行順序基本是一個偽命題!

D. c++編譯器以何種順序編譯文件的,先cpp文件,還是.h文件

1. 編譯階段 (頭文件 .h)
d工程中在頭文件中對導出內容(function, class, type, object, variable)進行定義.

2. 鏈接階段 (庫文件 .lib)
d工程在link階段會生成.lib
用戶link時需要 這個.lib 解決link時的代碼定位.
3. 運行階段
.exe

E. C語言程序的運行順序

for多重循環的執行順序是先執行內部循環,再執行外部循環

通過對代碼進行等價轉換,按照上述原理即可知道代碼的執行順序

對於上面代碼:

for(i=0;i<8;i++)
for(j=0;j<10;j++)
scanf("%d,%d",&i,&j);

它等價於下面

for(i=0;i<8;i++){
for(j=0;j<10;j++){
scanf("%d,%d",&i,&j);
}
}

從上面可以看出,每執行完一次j循環,i的值才增加1

但是注意,該代碼有一個陷阱:scanf函數會改變i和j的值

實際執行順序會受用戶輸入的i、j值的不同而不同

對於下面的代碼

for(i=0;i<8;)
min_m(i++)=i
for(j=0;j<8;j++)

它等價於下面的代碼:

for(i=0;i<8;){
min_m(i++)=i
}
for(j=0;j<8;j++){}

會先執行min_m函數8次 然後再執行j循環

注意,該代碼有一個問題,就是min_m函數的返回值必須是一個左值。

這意味著某些變數的值發生了更改,可能會影響到循環。

F. 編譯器生成的匯編語句執行順序為什麼與C代碼順序不同

這是開啟了編譯器優化是AT&T匯編,其實這里也沒做多少功能或者並非說多麼智能。只是把同一個變數的操作匯聚在一起了,不用來回的重復訪問。與之類似的是處理器優化有用到Cache line。就是一次從主存取盡可能多的數據以提高緩存命中率。舉個俗的例子,就比如你去接個水再順便上個廁所而往往不會先接完水回來又跑去上廁所再回來。

G. stm32cubeide設置文件編譯順序

對一個加法函數的庫項目的建立和編譯整個過程如下:
1、這里就要選擇ARMMCU的GCC編譯器了。
2、選擇和MCU型號的綁定關系了,畢竟編譯的庫是給MCU項目所用,這里選擇STM32F401CCU6的晶元。
2、添加源文件編寫加法函數,編寫加法函數進行編譯。
3、編譯成功,在工程目錄里就可以找到編譯好的庫文件了,庫文件的調用,配置一個對應MCU的基本工程,建立一個STM32F401CCU6的工程,並配置USART2作為串口輸出。
4、保存後產生基本工程代碼,重載printf函數方便列印輸出,參考STM32UART串口printf函數應用及浮點列印代碼空間節省。
5、在工程的core/inc/目錄新建一個和庫文件同名的頭文件,將庫文件libLib_C_Demo.a放置到工程的源文件目錄core/src/,則在工程目錄樹立可以看到。
6、因為編譯器默認只是識別C語言源代碼(.c文件),還需要進行庫文件的指定,這樣編譯器才會對二進制庫文件(.a)進行識別。
7、先打開屬性菜單,進行庫文件所在目錄和庫文件名的添加指定,注意庫文件名前需要加冒號,ApplyandClose後,就可以在工程文件里對庫文件進行調用,首先要引入庫文件的頭文件。
8、在while循環里進行列印輸出,列印數據為庫函數調用的加法和,編譯下載到STM32F401CCU6晶元後運行,通過串口工具觀察列印結果,輸出列印結果正確,庫函數正常調用成功。

H. java編譯順序的問題

存在父類時對象的實例化先編譯父類;
如果子類沒有實現父類的介面,屬於語法錯誤,無法通過編譯;
子類沒有實現父類的介面是子類的錯,父類沒有錯,即使不存在子類,父類也是可以編譯得過的。
你的答案應該選B:
但是,實際編譯是這樣:
如果你直接javac Square.java,語法檢查未通過,編譯器將不工作;而正確編譯時將得到父類與子類的class文件。
如果你先javac Shape.java,將得到Shape.class,再執行javac Square.java(未實現介面的)時,你仍將只會看到Shape.class。

I. 如何讓編譯器按代碼順序執行(((急急急)))

問題是你do的值就是c.me提供的,想要優化那就要考慮把do(c.me())這整個邏輯進行優化

J. 編譯器生成的匯編語句執行順序為什麼與C代碼順序不同

編譯器不僅是無序的,而且還展開、合並、刪除代碼(如果沒有發現代碼),,,C語言是好的,和C++編譯器編譯,經常連上帝也不知道結果。


暫停現代CPU為了彌補CPU和內存之間的速度差距的接入方案,以所謂的緩存(L1,L2,L3;在CPU晶元的速度比內存快得多,但容量很小),CPU緩存中常見的事情似乎保持鏡子,需要在緩存中直接讀取使用的數據是好的,比內存快幾倍的速度。

注意名字的區別。名稱風險是兩個沒有數據流的指令,數據冒險是兩個指令之間的數據流。

閱讀全文

與編譯器使用順序相關的資料

熱點內容
怎麼指定定向流量app的免流 瀏覽:900
華為雲伺服器有啥軟體 瀏覽:654
禮記正義pdf 瀏覽:988
CorePDF 瀏覽:733
python多文件調用 瀏覽:329
linux如何用python 瀏覽:188
超易學的python 瀏覽:159
控制面板命令行 瀏覽:51
為什麼空氣難壓縮是因為斥力嗎 瀏覽:643
郭天祥單片機實驗板 瀏覽:601
伺服器有什麼危害 瀏覽:258
飢荒怎麼開新的獨立伺服器 瀏覽:753
文件夾變成了 瀏覽:560
linuxpython綠色版 瀏覽:431
怎麼下載小愛同學音箱app 瀏覽:554
python佔位符作用 瀏覽:76
javajdbcpdf 瀏覽:543
php網頁模板下載 瀏覽:192
python試講課pygame 瀏覽:409
安居客的文件夾名稱 瀏覽:677