總是糾結於編程語言標準怎麼辦?

我是本科生,以前刷題寫項目沒太關心標準,後來漸漸覺得標準重要。然而研究了標準後更糾結,主要有:符合標準強迫症,標準混亂恐懼症。

比如C這麼簡潔的語言竟然有多個標準。現在不少穩定的開源項目是C89,那是否可以暫時忽視C99和C11?比如不用//注釋。

再比如GHC在快速迭代(即使Haskell2020還沒出),但它能暫時保證完全符合Haskell2010嗎?

Python分裂過一次。據說C++連100%標準的編譯器都沒有,不安心。我大概適合Java這種進化慢而穩定的語言吧。

看起來我有點本末倒置,但我只是想在選擇標準的時候留意一些,讓目前的軟體盡量穩定且可移植。不要出現標準過時導致大批代碼失效的情況,也不要出現用新標準寫的東西難以移植的情況(比如新標準可能總是普及不了)。


與很多人的嘲諷看法不同,我認為糾結標準並沒有錯,標準確實需要糾結,但這不是需要普通人類去糾結的東西,只要寫編譯器的人糾結就夠了。

你如果不寫編譯器,糾結標準就是對編譯器的不信任。因為,只要打開了相關選項,編譯器自然會把不符合標準的用法都給你揪出來。例如gcc似乎有個叫做pedantic的選項,能夠更嚴格的按照標準去檢查。

這個答案中的很多人把符合標準想像成天大的可笑事情,這其實根本沒有必要,pedantic檢查能通過比你想像的容易得多,換句話說,符合語言標準遠遠沒有很多人想像的那麼難,絕大多數程序都能做到,或者只需要進行非常有限的修改。如果你選擇支持標準,並不需要花太大代價,熟練之後,根本不會因此而糾結。

至於你為了糾結標準而去搞C89,個人覺得大可不必,畢竟C89距離現在已經28年,再有兩年就30年了。C99則是接近20年前的東西。那麼,你是覺得30年前的標準更容易過時,還是覺得20年前的標準更容易過時?30年前的標準跟20年前的標準誰可能先被淘汰?答案在你心中。

C++也是類似,第一個C++標準是C++98,距離今天也快20年了,能完整符合它的編譯器還是很多的,至於有人說沒有編譯器完全做到?我的建議:不信謠不傳謠。

--

來回答題主後面提出的問題。

標準過時導致大批代碼失效的情況,並不會經常發生,因為這是個嚴肅的問題,絕大多數情況下,標準的制定都會保持兼容,避免代碼大規模失效,做不到這一點的語言往往被人用腳投票。。。py3就是個典型例子,因為不兼容py2導致了十年過去都沒能取代py2(很多Linux發行版至今預設都依然還是py2),最後,py3成為了與py2並行的另外一種語言。

C++設計者原本是打算完全取代C的,所以在設計時才盡最大可能保持與C的兼容性,然而它依然不能做到完全取代,最後,也就成為了與C完全不同的語言,以兩種不同語言的身份並行發展了這麼多年。

所以題主,完全沒有必要擔心語言標準更新導致的代碼失效,首先這種不兼容一般不會發生,其次如果不兼容真發生了,新的語言標準往往會發展成一種新語言,平行存在,無法取代舊語言。


我只是想讓軟體穩定下來,不要出現標準過時導致大批代碼失效的情況

對於 C/C++ 來說,其實並不用擔心,大多數編譯器都是允許你選擇語言標準的版本的。只要設置好了目標標準,就不用擔心編譯器升級導致代碼失效,因為編譯器作者其實比你更擔心兼容問題。比如 gcc / clang 都可以用 -std=c++11 來指定使用 C++11 標準,不用考慮默認或者更高的標準。

而真正面對一個項目,通常也都存在一個確定的語言標準(無論是由人為定義還是被工具鏈環境所限),這是加入項目後首先需要搞清楚的,腦子裡一團漿糊顯然會出問題。(使用早期的 MSVC 更容易一團漿糊,原因是早期的 MSVC 只提供一種各個標準特性的大雜燴,不過最近的版本也步入了正軌,開始提供 /STD 選項用以指定標準版本了。)

其實不同版本的標準,如果當作是不同的語言看待,其實一切問題都很好理解。


經常引用標準的人常常會被嘲諷為「語言律師」。不過對於標準本身,似乎只有在你想搞清「X 是不是真的」這類問題時候才有摳字眼的必要,畢竟只有標準才是權威的信息來源。而正常情況下,搞清楚你中意的特性是哪個版本引入或者廢棄的、了解某個函數不同版本之間行為的區別就足夠了。

當然,與標準互相補完的,其實還有各個編譯器的具體「實現」。各個編譯器極有可能並不是 100% 實現(或者說兼容)某個標準版本,不過這通常都會明確寫在編譯器的手冊(說明文檔)中。比如 clang 的說明裡就明確說明 C++98 除了 export 都已支持,而其它更高的標準版本里則精確到了具體特性,給出了要使用特性所需的最低 clang 版本。

你完全可以把「標準」當作是「C++ 這門語言」的介面文檔,而「標準」覆蓋上「實現」則是「你所用的環境中的 C++」的介面文檔。讀文檔是程序員的本分,而很多人懶得看文檔、怎麼簡單怎麼寫、不考慮小概率事件、掩耳盜鈴地寫程序,因此而遇到問題不肯承認自己的錯誤,才造就了各式各樣的「坑」。


單說 Haskell 的例子,GHC 的確有 Haskell 2010 或者 Haskell 98 的兼容模式,問題是不開一堆 GHC 擴展的 Haskell 那還能算 Haskell 么(逃

我覺得不論是從對抗 bit-rotting 的角度還是從保證跨平台兼容性的角度,語言標準基本上都是個可有可無的東西。還不如多配幾個不同的 CI 環境,一些常用平台/編譯器版本的 matrix 下,你的代碼都能編譯/測試過,就已經足夠了。


別糾結了,默默的寫代碼吧。這個世界哪有那麼多完美的東西,編譯器是人寫的,Bug肯定很多,插幾個內聯彙編,搞幾個特殊的lambda,模板,右值與operator轉換的混用,開上O3,編譯器都可能掛掉,這還是編譯器號稱支持的呢。但是,這些都是少數情況下出現的,大多數你都碰不到這樣的問題,如果為了移植,你也需要實際去測編譯器,如果發現有問題,改改代碼繞過去好了。


據我觀察,只有 C/C++ 程序員才會糾結「標準」,別的語言的程序員(C#/Java/PHP/Go/Python/Ruby/JavaScript/Objective-C 等等)似乎不會張口閉口「標準怎麼怎麼說」,也沒有那麼多令人討厭的語言律師。我認為一般的 C/C++ 程序員不需要閱讀(通讀)標準。標準是給寫編譯器和標準庫的人看的,非常晦澀(通俗地說,就是不說人話),一般人閱讀標準的回報率太低,不值得,除非你想成為語言律師。

我的建議是,不用太在意標準,因為只用 C/C++ 標準提供的東西,寫不出什麼有用的程序來,主要是 IO 方面太弱了。標準只提供了文件和 stdin/stdout,而目錄、網路、圖形、音頻都沒有。只用 C/C++ 標準連「清屏」都做不到。如果你真的要考慮編譯器之間的可移植性,搞定這些方面的平台差異比搞定語言標準的差異難多了。要說可移植性,我想 JavaScript 程序員更有發言權。

等你工作了就會發現,其實 C++ 沒多少可移植性需要考慮。我在 Linux 下寫了 10 年 C++ 伺服器,一直只需要兼容當前生產環境的操作系統提供的那個 gcc 版本。頭 5 年在摩根,一路從 gcc 3.2、3.4、4.1 用到 4.4,因為 RHEL 3/4/5/6 自帶的就是這幾個版本的 gcc。從可維護性的角度,我們在生產環境下只部署最新兩個版本的 RHEL。

再說了,沒有哪個主流 C++98/03 編譯器實現了 export 關鍵字,那難道為此你就不寫 C++ 程序了?gcc 5 之前的 std::string 採用的是 COW 實現,不符合 C++11 標準,難道你在用上 gcc 5 之前,就不能寫 C++11 程序了?要知道 Ubuntu 14.04/Debian 8/CentOS 7 都還停留在 gcc 4.x,去年發布的 Ubuntu 16.04 才用上 gcc 5。

其實,有能力修改編譯器的大廠也不太在意標準。我推測,既然 Visual C++ 是微軟的,如果微軟內部項目需要什麼 C++ 特性,跟 Visual Studio 團隊說一聲估計就能做出來。Visual C++ 也不敢輕易去掉那些不符合標準的擴展(例如 non-const reference 綁定到臨時對象)。Google 對 gcc 和 clang 也都有一定的發言權,在 Google 內部,我們這些寫 C++ backend 的平時也不會特別在意「符合 C++ 標準」,只要程序在生產環境下表現正確就足夠了,反正我們的編譯器團隊不會沒理由地破壞程序的行為。

你完全不用擔心「標準過時導致大批代碼失效的情況」。你要明白,C/C++ 標準委員會並不僱傭 C/C++ 編譯器實現者,實際上他們是求著 C/C++ 編譯器作者們實現他們定的標準,實現難度太大的功能會被編譯器作者廢置(以前的 export 就是一例,我看現在的 GC 也很可能重蹈覆轍)。而僱傭 C/C++ 編譯器作者的是 Microsoft/RedHat/Apple/Google/Intel/IBM 這樣的大公司(C++98 里就有 IBM 加進去的私貨:trigraphs,C++17 終於把它清除了),他們是不會讓自己已有的代碼無故失效的——由此產生的開發成本誰來承擔呢?

C/C++ 標準都是追述性質的(C 語言誕生於 70 年代,1989 年標準化;C++ 誕生於 80 年代,1998 年標準化),制定這種標準有一條重要原則:

Existing code is important, existing implementations are not. --- http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf

我看完全不必杞人憂天。

同理,我也不太看重 POSIX 標準。Linux 的 epoll 不在任何標準里,難道就不能用了?商用 Unix 已經死得差不多了(你對比一下 APUE 第 1/2/3 版舉例用的系統就知道了,今年 Solaris 團隊也被解散了),就剩 Linux 一家活得好好的。對於我們這些寫 C++ server 的人來說,POSIX (可移植操作系統介面)存在的意義已經不大了,反正也沒指望移植到別的系統上去。倒是 Windows 10 主動提供了 Linux 介面,把 muduo 往 Windows 移植的活兒由微軟幫我做了,我很欣慰。


產生這個問題的主要原因是你還抱有代碼千古的幻想,事實上用不了幾年你回頭看你寫的東西的時候只會覺得,這shit一樣的代碼竟然是我寫的……

然後用一個優美得多方法把它重寫了……


這麼說吧,tex當年選擇了現在基本死掉的語言pascal。然並卵。社區需要維護tex,於是搞了個叫web2c的玩意把它強行翻譯成了C然後編譯和加功能。

再找個例子好了。webkit。眾所周知這個東西的前身是kde下的khtml,所以是qt/c++寫的。然而蘋果fork了一份,並且好像改成了不用qt的c++,又加了一個objective C的介面。後來google也要做瀏覽器……於是也拿來用了,但是顯然又不用這個objective C的部分了。

然而這兩個項目都活得好好的。


標準有什麼用?老闆按照標準給工資?

穩定一些是值得追求的,但為什麼要讓軟體可移植,可升級?你以為編譯器升級了的時候,你的軟體還在么,你的項目還在沒被砍掉?你的公司還沒破產,老闆還沒跑路?你以為你的軟體真的需要跨平台?騙自己吧,其實你的軟體根本就沒人用,以後也不會有人用,一個平台都不需要,別說跨平台了!

有的時候確實開發工具升級很快的,比如這些年眼睜睜大家看著的cocos2dx從入門到崩潰。但是這個時候標準一直在變啊,你跟的上嗎?項目都還沒做完還沒砍掉公司還沒破產呢,開發工具就已經砍掉關門了,是一種什麼感受?


標準過時從來都不是代碼失效的原因。沒人維護才是。

與其通過各種標準強迫症來保證代碼有效,不如考慮讓代碼變得很有用,那樣只要還有用它就不會失效。如果能做到很有用,那甚至有人會來把它升級到最新的標準。

君不見,那麼些符合C89標準的、曾經一度緊跟時代潮流的、預計21世紀前都不會失效的代碼,終究還是失效了嗎…


done is better than perfect. 其實 你自己是清楚的, 知道有點本末顛倒, 只是呢,有時候人都是有點自己的追求的, 有句話叫做 千金難買我樂意, 我就樂意更讓它 perfect一點. 只是這些東西 一旦遇到 工程 產品 團隊,多少需要收斂點.


應該從編程語言的版本角度來考慮,分三方面:

1. 工作:用團隊在用的版本,這是必須。

2. 個人:如果是個人項目,往往就是主流版本。

3. 學習:學習新版本的知識點,也許現在用不到,但估計一兩年就流行開來。


符合標準沒有什麼大問題;你想想 cpp 曾經的多個編譯器下不一定表現相同和 js 的瀏覽器差異


過日子,老想著憲法怎麼怎麼說,上帝怎麼怎麼說,主怎麼說, 這生活也不免太扭曲了吧,,動不動就想著這樣這樣不符合「上帝標準」, 這算是所謂的「原教旨主義」嗎?

還是考慮把你所在地區的地方法律法規遵守好吧。。不應該因為所謂的標準,約束了你作為一個工程師的創意。 法無禁止皆可為之,真的。

別胡亂給自己帶上什麼「100%qingzheng」的這種道德衛士頭銜,或者用這個頭銜去嘲笑或者瞧不起別人,「好臟啊」,"你UB"。


習慣就好了,我覺得成長過程中都是起起伏伏的,這是你現階段看到的問題,你還沒看到更高層的問題。

我最開始寫代碼寫得可快了,啥都不管。

當開始注意編程格式和命名之後就慢了。

後來這個熟悉了,又變快了。

後來開始注意設計風格和擴展性,又慢了。

這個熟悉了,又快了。

開始注意性能與介面友好,慢了。

熟悉了,快了。

注意安全性和健壯性,慢了。

熟悉了,快了。

注意架構與數據一致性,慢了。

熟悉了,快了。

注意團隊整體發展與規劃,慢了。

正在加快中。


少年,這是因為你沒有生產目標啊。

如果有緊迫的生產目標,誰還在乎標準呢。

比如你看這個:一張鈔票:寫代碼過程中最忌諱的是什麼?總感覺最近太過於急於求成?


標準要看,約定也要注意,慢慢形成自己的一個語言子集。


我教你一招,用代碼潔癖來取代標準標準強迫症,對自己對項目都更好。


以你用的編譯器的「事實標準」為準


在有餘力的前提下,盡量按照標準來吧

比如某些C++編譯器的實現是long=64bit,但標準只規定了long≥32bit,這時候應該盡量使用typedef i64 std::int64_t的方式,來使得你的程序更加標準


(我覺得別人大概會覺得我是個C++語言律師)

考慮到編譯期支持度的問題,純按標準肯定是死讀書了。

知道標準再寫代碼的主要目的是明確知道你的每一行代碼到底會做什麼,以此判斷是否符合預期,而不是符合標準就一定可以用,不符合就一定不行。

搞OI的估計沒有幾個沒被undefined behavior坑到過的,這是好好看標準可以避免的,大部分是假定了「從左到右掃描,能求值部分求值」導致的,就是沒搞清楚代碼到底在做什麼。

加入你的早期C編譯器支持//注釋,而且你只用它編譯,那行為當然是符合預期的,可以隨便用。

至於開源項目,得按統一標準去搞,不同部分的代碼肯定得互相兼容,具體用什麼版本倒關係不大。

至於標準導致代碼失效,只要有編譯器能編譯你的代碼,那肯定不會出事。再說大部分語言標準更新是向下兼容的。


推薦閱讀:

用易語言寫過的最大的項目是什麼?
編寫一個小windows桌面程序用什麼語言和工具?
當我們在學習一門編程語言的時候,我們究竟在學習什麼?
寫一個具有model based design功能的Simulink需要哪些技術?難度如何?
內聯函數可以是虛函數嗎?

TAG:軟體開發 | 編程語言 | 軟體工程 | 軟體測試 | CC |