我聽說C編譯器有/無優化選項可能會生成不同的程序(編譯優化程序會導致它的行爲不同),但我從未遇到過這種情況。 任何人都可以舉個簡單的例子來證明這一點嗎?使用GCC -O2選項編譯生成不同的程序
回答
對於GCC 4.4.4,這不同於與-O0
和-O2
void foo(int i) {
foo(i+1);
}
main() {
foo(0);
}
有了這個優化循環,直到永遠。如果沒有優化,它崩潰(堆棧溢出!)
其它和更逼真的變體通常將依賴於定時,易受浮精確性的變化,或者取決於未定義行爲(未初始化的變量,堆/堆棧佈局)
酷!我從不期望編譯器優化可以做到這一點。 – user607722 2011-03-25 03:05:37
一個這個問題的答案可能是:
每ANSI C編譯器必須至少支持:
- 31個參數的函數定義
- 個31參數在函數調用中的源極線
- 509字符
- 在表達式32個水平嵌套的括號的
- 長整型的最大值不能被任何小於2147483647,(即,長整型 至少爲32位)。
來源:C專家編程 - 彼得·菩提樹
這可能是因爲編譯器支持的功能定義,也許31參數-O0和35 -O3,這是因爲沒有規範爲了這。我個人認爲這應該是一個缺陷設計,非常可以改進。但簡而言之:編譯器中的某些內容不受標準約束,並且可以在包含優化級別的實現中進行更改。
希望這可以幫助馬克Loeser說,你應該在你的問題更具體。
錯誤的猜想 – sehe 2011-03-30 21:55:10
我已經看到它在浮點精度極限附近做了很多數學計算。 在極限情況下,算術不是關聯的,所以如果以稍微不同的順序執行操作,則可以得到稍微不同的答案。另外,如果使用80位雙精度浮點芯片,但結果存儲在64位雙精度變量中,則信息可能會丟失,因此操作順序會影響結果。
你能找到一個例子嗎? – user607722 2011-03-25 11:54:57
如果你看一下這個代碼生成的彙編:
int main()
{
int i = 1;
while (i) ;
return 0;
}
Whitout的-02標誌:
.file "test.c"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl $1, -4(%ebp)
.L2:
cmpl $0, -4(%ebp)
jne .L2
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
隨着-02標誌:
.file "test.c"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
.L2:
jmp .L2
.size main, .-main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
隨着-O2標誌,i
和返回值的聲明是ommited,你只有一個跳轉標籤o這個相同的標籤構成無限循環。
沒有-02標誌,你可以清楚地看到堆棧(subl $16, %esp
)和初始化(movl $1, -4(%ebp)
)以及的,而條件(cmpl $0, -4(%ebp)
)評估和主要的返回值的i
空間的分配功能(movl $0, %eax
)。
well..儘管彙編代碼不同,但他們仍然在做同樣的事情:無限循環。 – user607722 2011-03-25 02:59:58
優化級別之間的差異通常源於未初始化的變量。例如:
#include <stdio.h>
int main()
{
int x;
printf("%d\n", x);
return 0;
}
當與-O0
編譯,輸出5895648
。用-O2
編譯時,每次運行它時輸出一個不同的數字;例如,-1077877612
。
區別可以更微妙;想象你有下面的代碼:
int x; // uninitialized
if (x % 10 == 8)
printf("Go east\n");
else
printf("Go west\n");
隨着-O0
,這將輸出Go east
,並與-O2
,(通常)Go west
。
well..uninitialized變量具有未定義的行爲...如果標準中定義了每行代碼的行爲,那麼優化會不會影響輸出? – user607722 2011-03-25 03:01:46
很少發現-O2不會產生與不使用優化不同的結果的情況。
unsigned int fun (unsigned int a)
{
return(a+73);
}
沒有優化:
fun:
str fp, [sp, #-4]!
.save {fp}
.setfp fp, sp, #0
add fp, sp, #0
.pad #12
sub sp, sp, #12
str r0, [fp, #-8]
ldr r3, [fp, #-8]
add r3, r3, #73
mov r0, r3
add sp, fp, #0
ldmfd sp!, {fp}
bx lr
與優化:
fun:
add r0, r0, #73
bx lr
即使這樣的功能:
void fun (void)
{
}
沒有優化:
fun:
str fp, [sp, #-4]!
.save {fp}
.setfp fp, sp, #0
add fp, sp, #0
add sp, fp, #0
ldmfd sp!, {fp}
bx lr
隨着優化:
fun:
bx lr
如果宣佈一切揮發,產生了對幀指針,其中未優化和優化是一樣的,你可能會接近一些。同樣,如果您編譯了一個可調試版本(不確定該開關是什麼),那麼它的行爲就好像一切都是不穩定的,這樣您就可以使用調試器來監視內存中的變量並單步執行。這也可能接近來自相同輸入的相同輸出。
另請注意,無論是否進行優化,預計都會看到來自不同編譯器的相同源代碼的不同輸出,甚至不同主要版本的gcc也會產生不同的結果。像上面這些平凡的功能通常會產生相同的結果,許多編譯器進行優化。但是更復雜的函數會帶來更多的變量,從編譯器到編譯器會產生不同的結果。
在錯誤提交報告中可以找到在不同優化級別上具有不同輸出的正確程序的示例,它們只能在特定版本的GCC上「工作」。
但是通過調用UB很容易實現。但是,它不再是一個正確的程序,並且可能會產生不同版本的GCC的不同輸出(其中,請參閱mythology)。
以下代碼輸出Here i am
,當編譯時沒有進行優化但沒有進行優化編譯時。
的想法是,該函數x()
被指定爲「純」(具有無副作用),所以編譯器可以優化出來(我的編譯器是GCC 4.1.2)。
#include <stdio.h>
int x() __attribute__ ((pure));
int x()
{
return printf("Here i am!\n");
}
int main()
{
int y = x();
return 0;
}
優化使用約
- 在某些情況下不存在指針別名的假設(這意味着它可以保持東西在寄存器無需經過另一參考擔心修改)
- 中的存儲器位置的非揮發性一般
正是因爲這一點,你可以像
也警告Type-punned pointers may break strict aliasing rules... (paraphrased)
像這樣的警告是爲了讓您免於頭痛,當您的代碼在編譯機智&優化時發展微妙的錯誤。
一般來說,在C和C++
- 非常肯定你知道自己在做
- 什麼從來沒有發揮它寬鬆(不投炭**直接爲char *等)
- 使用常量,易揮發,擲(),盡職盡責
- 信任你的編譯器供應商(或開發者),或建立-O0
我敢肯定,我錯過了史詩,但你的漂移。
鍵入我的HTC。對不起打字錯誤
哦,不要忘記:最具風險的優化(即那些使得每天程序員說法更容易錯過/最令人驚訝的細微假設)只能使用-O3或特定的-f .......選項 – sehe 2011-03-30 21:59:22
- 1. -O2選項生成文件
- 2. 在gcc中使用-g和-O2選項
- 3. 使用gcc編譯應用程序,它與使用不同gcc版本編譯的庫不同gigcc
- 4. GCC不編譯程序
- 5. 使用avr-gcc編譯彙編程序
- 6. 使用不同的GCC版本編譯
- 7. 使用gcc編譯C++程序
- 8. 如何編譯GCC生成的asm?
- 9. 在premake 3.7中,如何爲gcc生成-O0而不是-O2?
- 10. gcc - 編譯,生成與內聯源程序集而不調用objdump
- 11. VS2010每次編譯項目都會生成不同的程序集
- 12. 在Netbeans中編譯C程序,但不能用cmcc編譯gcc
- 13. 爲什麼在編譯期間不使用GCC選項-Os?
- 14. gcc與complex.h的編譯器選項
- 15. 在gcc c編譯器中生成cswtch
- 16. 編譯.exe程序時生成的.lib
- 17. 奇怪的gcc6.1 -O2編譯行爲
- 18. 編譯時不帶-g選項的調用堆棧(gcc編譯器)
- 19. NetBeans IDE gcc編譯器優化選項
- 20. Gcc編譯器命令和選項
- 21. CMake交叉編譯生成無效的鏈接器選項
- 22. 生成與編譯器選項d.ts文件生成角
- 23. 使用gcc編譯帶-D選項的多個文件
- 24. 如何避免使用gcc編譯器的-l選項?
- 25. 使用gcc在另一個程序中編譯程序
- 26. 爲什麼在-O2中編譯會導致我的應用程序崩潰,但-O2 -fno-default-inline不會?
- 27. 編譯選項時發生Gcc錯誤-fPIC
- 28. 使用交叉編譯器編譯原生GCC for
- 29. 使gcc編譯C使用gcc規則
- 30. GCC - 不編譯未使用的代碼
這裏有助於更具體的瞭解。你的意思是輸入是代碼,輸出是實際的二進制對象嗎?或者通過優化編譯程序使其行爲有所不同?後者通常是編譯器中的一個錯誤,或者您濫用未定義的行爲。 – 2011-03-24 17:14:31