關於G++ 首先更正一個概念,C++是一門電腦編程語言,G++不是語言,是一款編譯器中編譯C++程式的命令而已。那麼他們之間的區別是什麼? 在提交題目中的語言選項里,G++和C++都代表編譯的方式。準確地說,選擇C++的話,意味著你將使用的是最標準的編譯方式,也就是ANSI C++編譯。如果你使用 ...
關於G++
首先更正一個概念,C++是一門電腦編程語言,G++不是語言,是一款編譯器中編譯C++程式的命令而已。那麼他們之間的區別是什麼?
在提交題目中的語言選項里,G++和C++都代表編譯的方式。準確地說,選擇C++的話,意味著你將使用的是最標準的編譯方式,也就是ANSI C++編譯。如果你使用的是G++的話,意味著你將使用GNU項目中最平凡適用人群最多的編譯器(其實也就是我們熟悉的Code::Blocks的自帶的編譯器,Windows環境里一般是MinGW下的gcc,Linux中的gcc和前者基本是一個東西)進行編譯。類似的還有選擇C和GCC,前者是標準C編譯器編譯,後者同樣是用gcc來編譯。
編譯器的差別——編譯器的優化
當然,很多時候我們有的代碼用C++提交通過了,但是G++卻失敗了呢?眾所周知,不同的編譯器,會對代碼做出一些不同的優化。舉一個最簡單的例子。針對單個語句(註意,是單個語句,不是包含在語句中的那種前++和後++):
1 2 | a: a++; b: ++a; |
一般的講,我們都知道,這兩條語句的最終結果是一樣的,就是a自己增加了1。但是,兩者的差距還是有的。如果從標準C的角度去理解。a++這個語句等同於
1 | a: a = a +1 |
也就是說,我是先調用,再自增。在調用過程中,會申請一個新的數據地址,用於存放臨時的變數a’,然後在把a’加1,之後在把a’賦值給a。但是++a這個語句不需要這麼麻煩。因為他是先自增,後調用,也就是省去了申請新地址的功夫。所以理論上,二者的時間消耗是有差異的,如果你是使用標準C的編譯方式,就可以發現這個差異。畢竟,申請臨時記憶體這個操作耗費的時間,遠遠比令已知記憶體的數據進行一個改變要長的多。但是編譯器的優化就體現在了這種本身結果相同卻耗時有差異的地方。如果你使用gcc來編譯,結果你會發現前++與後++二者基本上沒有差異。這就是編譯器的優化中的冰山一角了。事實上還有很多優化的地方。
為什麼G++提交WA了?
好吧回到現實中來。我昨天在做poj 3122這道題的時候,再一次的遇到了G++WA;C++AC的尷尬局面。為什麼呢?其實這個也算是編譯器優化的一部分,那就是精度預設。
眾所周知,long long類型,作為一個在C/C++11才被確認為基本數據類型的一個數據類型,在不同的環境下,他的類型標識符是不同的。也就是我們津津樂道的%lld 和 %I64d了。同樣,double類型也是一個有趣的類型。double類型其實準確地說是雙精度型,他的記憶體長度一般是比float類型(單精度型)的多了一倍,有的時候很早的標準里是把double稱為long float的。所以說就有了為什麼float類型用%f,double用%lf。但是由於現在不是以前的那種一個記憶體條就幾兆,多開一個double就會超記憶體的年代了,所以double還有float在gcc中被自動優化。
在用scanf讀數據時,為了與float區分,使用%lf。
在用printf寫數據時,由於實質上,double和float是同一個類型,只不過記憶體占用有差異而已,他們的標識符都是%f,註意,這個和標準C不同,這裡的都是%f。
當然對於另外一個特殊的類型long double雖然不常用,但是編譯器依舊在支持,這裡有個插曲,理論上long double應該是兩倍的double(類似long long和int的關係,因為long和int其實是一個東西)。
但是實際上,long double很奇怪的是一個10位元組的怪物,他有兩個空餘位元組,是怎麼改動都不會發生變化的。輸入輸出的標識符都是%Lf,大寫的L。
但是這裡又有問題了,為什麼我在本地用%f會WA,在OJ上用%f會AC?
因為我們本機如果使用的是Windows下的Code::Blocks這款IDE的話,編譯器也就是MinGW這個東西。事實上,為了儘量保持gcc的跨平臺性,MinGW在某些地方是直接用了MSVC的東西的,而對我們影響最大的就是這個標識符的問題。簡單的說,如果你是要在本機測試,那麼最好,請使用標準C的那個標識符系統;如果你要提交代碼,那麼請改成gcc的那一套標識符系統。再有就是編譯器版本的問題,現在的MinGW版本已經到了4.8,但是POJ上仍然使4.4,所以低版本的編譯器同樣會有一些不尋常的問題。
當然還有更簡單的方法,就是直接用輸入輸出流在控制輸入輸出,這樣更省事,而且跨平臺性能更好,不會出現這種因為標識符而出錯的情況。
列個表格出來就是這個樣子的:
double f; | POJ G++提交 | POJ C++提交 | 本機測試(MinGW GCC 4.8) | 最安全的方法 |
輸入 | scanf(“%lf”, &f); | scanf(“%lf”, &f); | scanf(“%lf”, &f); | cin >> f; |
輸出 | printf(“%f“, f); | printf(“%lf”, f); | printf(“%lf“, f); | cout << f; |
大概就是這麼多了,希望大家避免這種錯誤的發生。
--------------------------------------------------------------以上內容為轉載-----------------------------------------------------------------------
補充一下...cin和cout在這一點上比較安全,但是讀取速度卻比scanf和printf慢很多,在ACM比賽中,用cin和cout當然有可能導致TLE啦、
事實上,cin慢是有原因的,其實預設的時候,cin與stdin總是保持同步的,也就是說這兩種方法可以混用,而不必擔心文件指針混亂,同時cout和stdout也一樣,兩者混用不會輸出順序錯亂。正因為這個相容性的特性,導致cin有許多額外的開銷,如何禁用這個特性呢?只需一個語句 std::ios::sync_with_stdio(false); ,這樣就可以取消cin於stdin的同步了。
std::ios::sync_with_stdio(false); std::cin >> n;
std::cout << n;
速度基本可以達到csanf和printf。
希望有所幫助~ 謝謝。