【導讀】:程式碼註釋的作用,不需要對程式設計師解釋了。有時在檢視他人程式碼,能看到一些令人不禁大笑的註釋。比如:
或者:
1 2 |
// 寫這段程式碼的時候,只有上帝和我知道它是幹嘛的 // 現在只有上帝知道 |
最近在 Quora 上看到一個帖子,號召程式設計師分享自己見過最有趣的程式碼註釋。看到了各種有趣的註釋,打算分多篇摘編分享給大家。
1. Bill Poucher 的分享(他是一位電腦科學教授)
我見過的最佳註釋是以 HTML 格式寫在原始碼裡的,任何想要閱讀的人都能看得見。我管它叫“Cerny效應”。
曾經有一位很有天賦的捷克研究生 Tomas Cerny,在 Baylor 大學 ICPC(國際大學生程式設計競賽)技術研發部主任 Jeff Donahoo 博士的領導下,負責將另外一位很聰明的研究生的設計原型轉換成實際產品。
有一天,Jeff 到我的辦公室跟我說,在他們ICPC實驗室,冷戰的格局正在形成。因為有人在原始碼的註釋裡寫了一些話,冒犯到了其他人。(為了看看情況,)我就隨他一起去找了Tomas。
Jeff去了以後就開門見山地問:“Tomas,你是不是在Joel的程式碼上加了註釋,說他的程式碼是愚蠢(retarded)的?”Tomas倒是很坦白地說:“是的。”Jeff又問:“你憑什麼這麼寫呢?”Tomas回答說:“因為(他的程式碼)確實愚蠢(retarded)啊!”
我就站在一邊看著,Tomas一臉懵逼,Jeff強壓怒火,場面真是大寫的尷尬。接著,Tomas拿出了他的《捷克語-英語詞典》,開啟,上面寫著,詞義:“開發中(under developed):retarded”(譯註:其實retarded這個詞有兩個意思,既有“弱智,愚蠢”的意思,也有專案未完成,正在開發中的意思,這也是造成這個誤會的原因。)
是的,開發確實還沒完成……後來,Tomas就把註釋修改為了“建設中(Under construction)”。然後我和Jeff都對Tomas擴充英語能力的熱情捧腹大笑。我至今不知道當年這個誤會是不是真的解決了。
跟你們說,我和Jeff都很愛講這個段子,後來每當我們把Tomas介紹給ICPC新成員的時候,就一定會講這個段子。Tomas現在已經是布拉格捷克技術大學的電腦科學系教授了,他還是學校ICPC技術部的奠基人,也是我非常好的朋友。
Tomas不僅在Baylor大學獲得了碩士學位,而且他在這裡找到了他的伴侶,一位音樂家,同時也是奧運會級別的田徑選手。當然,這是另外一個關於奧林匹克的愛情故事了。
ICPI-ACM國際大學生程式設計競賽,由IBM贊助。
2. Anirudha Bose 的分享:
謝爾蓋.布林(Google的聯合創始人之一)在史丹佛大學念電腦科學博士學位的時候,他的簡歷裡並不含任何”待遇要求“(Objective)的字眼。但當你去檢視他的簡歷的HTML原始碼的時候,你會看到(他在簡歷HTML原始檔裡明確寫了“待遇要求”,只是用註釋注掉了,在瀏覽器頁面上不顯示。):
(其“待遇要求“的內容是:辦公室要大,掙錢要多,幹活要少。如果能經常去奇妙的地方旅行而且還能給報銷的話,那就更好了。)
布林簡歷的原始連結在這裡。
3. Abhinav Upadhyay 的分享
1 2 |
/* You are not expected to understand this */ /* 我們並不指望你能看懂這段話 */ |
這段註釋並不是我親眼所見,但是它在網上傳得很厲害。這段註釋是出自於貝爾實驗室的Unix系統第六發行版,並在《Lions’ Commentary on UNIX 6th Edition, with Source Code》這本書中標註出來的。
程式碼和標註的細節如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/* * 切換到新程式棧,並設定其段暫存器 */ retu(rp->p_addr); sureg(); /* * 如果新程式因為被換出而暫停,則設定其棧級別為last call,並傳至sayu(u_ssay). * 這樣做的目的是確保aretu方法被呼叫後立即返回的值實質上是上一次呼叫sayu的方法的返回值。 * * You are not expected to understand this. * 我們並不指望你能看懂這段話 */ if(rp->p_flag&SSWAP) { rp->p_flag =& ~SSWAP; aretu(u.u_ssav); } /* * The value returned here has many subtle implications. * See the newproc comments. */ return(1); |
4. Kalpesh Singh 的分享:
我有個壞習慣,每當我看到做得不錯的網站,我就回去控制檯看它的原始碼。我想很多前端工程師都喜歡這麼幹吧。
我訂購了Box8服務,並在他們的console裡看到了如下資訊。(夥計們,他們居然在console/原始碼裡面打招聘廣告。我對廣告什麼的早就受夠了,你們就不能搞點兒新花樣?)
你們可以看看Box8.in的console。
不過這樣真的是挺有趣的。
還有一個比較有意思的註釋是target.com程式設計遊戲網站的原始碼
可檢視:Code With Target via The Geekiest Contact Form (BETA)
好好享受樂趣吧!
5. Liu Wei 的分享[譯註:這明顯是一位來自中國的工程師]:
我在一週前在社交網站上看到很多人在討論這個網站,網站的原始碼包含了這些註釋。
有人說,這家公司應該加強對程式碼的稽核機制。有人則懷疑這家公司可能沒有足夠的人力資源來做程式碼稽核,因為至少需要兩個程式設計師才能完成這項工作。
6. Edwin Romero 的分享:
我不確認有多少人熟悉站點內的Robots.txt這個檔案。其實這個檔案不是執行必要的程式碼,但是它宣告瞭爬蟲/搜尋引擎能爬到/搜到站點的哪些內容。
我在Nike網站上發現的Robots.txt檔案非常有意思,如下:
如果你讀一下檔案頭部的內容,你就會發現它是這麼寫的:
“…just crawl it.”
這種寫法和Nike品牌著名的廣告標語“Just Do It”不謀而合。
**更新**
Nike最近修改了他們的robots檔案,並在裡面加入了一個有趣的圖案:
感謝Chris Shepherd 告訴我這個訊息!
7. Soham Bhowmik 的分享:
好吧,這個回答其實並不完全是關於註釋,而是關於程式碼。
我的第一個專案,一個為印度非常流行的新聞頻道做的iOS應用,要求在交付之前制定規則。這件事情已經過去很久了,大約6年左右。當時iOS的最新版本還只是3.2。無論公司什麼時候把應用交付給客戶,應用都會被設定在5天之後過期,然後使用者就不得不回頭來找我們解決應用的問題。
程式碼是用Objective-C寫的:
1 2 3 4 5 6 7 |
BOOL appExpired =........some code....; if(appExpired ) { [ NSString hahahahhaha ]; //will purposely crash now, will not use exit(1) //這裡故意讓程式崩潰,而不使用exit(1) } |
當然,如果現在還這麼寫程式碼編譯器就會報錯,但是當時編譯器只會給出警告資訊,然後程式在執行的時候會因為呼叫未定義的方法而崩潰。
8. Ray Mullins 的分享:
曾經用過IBM的VSAM系統(包括z/OS和z/VSE以及其後續版本)的人,都應該體驗過這個系統的逗位元性。
我曾經在一家德國的軟體公司任職,負責對一個事務處理監測程式進行技術運維和研發,這個監測程式是美國開發的,有一個針對VSAM檔案的介面,那麼當然程式就需要一個控制模組負責對檔案進行存取操作。明顯寫這個控制模組的人那天過得不太如意,因為在控制模組獲取通用錯誤方法地址的那段程式碼裡,寫著這樣的註釋:”VSAM又SB了(FUCKED UP AGAIN), 到這裡來。“ 更有趣的是,這些控制模組的程式碼被正式印到操作手冊上(包括帶有FUCK的那段文字),所以在差不多20年的實踐裡,這個註釋就這麼被寫在那裡然後發給客戶。然後直到某一天有個客戶想對控制模組進行替換,然後才發現有這麼個註釋,然後才告訴我們。
接下來的這個不是註釋,而是來自於我的實際工作。我們當時有一套內部標準和方法文件。我們的系統程式設計師需要為一個什麼渣渣的4GL產品的延遲方法編寫文件,並進行演示。他的文件草稿第一版草稿大概是這麼寫的:
1 2 3 4 |
IF TIMEDIFF >=5 SAY "THE SYSTEM IS FUCKED, PLEASE BEAR WITH US" //如果系統SB了,請和我們一起忍受 SLEEP(10) ENDIF |
草稿寫完了以後他就去忙東忙西,根本沒時間搞文件,接著突然發現文件的提交期限就在晚上了。於是他就直接把草稿發給文件管理人員了,根本都沒看。一週以後,上百份文件就這麼被列印出來分發出去了。並且,內容就是他草稿上寫的那些,一個字都沒改。
9. Terry Lambert 的分享:
我最喜歡的註釋有兩個,都是Bill Paul寫的。這傢伙為FreeBSD做了大量的工作,現在受僱於Wind River System,聽說這個公司最近要被Intel收購了。Bill是一個非常有才華的程式設計師,但是他對愚蠢的容忍度也出奇的低,並且他的幽默感也很不同尋常。
下面是我喜歡的第一條註釋,這是從RelTek 8129/8139 PCI NIC 驅動程式裡找到的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
/* * The RealTek 8139 PCI NIC redefines the meaning of 'low end.' RealTek 8139 PCI NIC重刷了low逼的下限 * 這可能是史上寫得最爛的PCI乙太網控制器驅動 * with the possible exception of the FEAST chip made by SMC. * The 8139 supports bus-master DMA, but it has a terrible * interface that nullifies any performance gains that * bus-master DMA usually offers. * * For transmission, the chip offers a series of four TX * descriptor registers. Each transmit frame must be in a * contiguous buffer, aligned on a longword (32-bit) boundary. * This means we almost always have to do mbuf copies in order * to transmit a frame, except in the unlikely case where a) * the packet fits into a single mbuf, and b) the packet is * 32-bit aligned within the mbuf's data area. The presence of * only four descriptor registers means that we can never have * more than four packets queued for transmission at any one * time. * * Reception is not much better. The driver has to allocate a * single large buffer area (up to 64K in size) into which the * chip will DMA received frames. Because we don't know where * within this region received packets will begin or end, we * have no choice but to copy data from the buffer area into * mbufs in order to pass the packets up to the higher * protocol levels. * * 要讓這麼爛的設計去達到100Mbps的速度簡直就是天方夜譚 * 除非你有一臺CPU強勁的電腦去驅動 * 400Mhz PII or some equally overmuscled CPU to drive it. * * On the bright side, the 8139 does have a built-in PHY, * although rather than using an MDIO serial interface like * most other NICs, the PHY registers are directly accessible * through the 8139's register space. The 8139 supports * autonegotiation, as well as a 64-bit multicast filter. * |
這絕對是一個很爽的註釋。傳說為了讓 Bill 能把這段註釋刪掉/修訂/修改/更新等等,廠家用了各種條件去誘惑他,但是他都拒絕了。
第二段註釋是寫在一個修改版的BSD許可證的“限制傷害”條款裡的,Bill在他的程式碼裡引用了這個許可證協議。其實它並沒有對原先的協議做大的修改,所以很多人看到這個協議以後,一看跟模板差不多,然後就跳過了,幾乎沒什麼人仔細去看整個文字。
1 2 3 |
IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
怎麼樣,你沒見過這條吧。其實很容易就看掉了。有趣的地方正好在這裡:
「Bill Paul以及他頭腦中的想法絕不會直接,間接,偶然,特殊,典型或實質性地造成任何損害。」
總之,這哥們兒是個天才。
10. Boris Zamoruev 的分享
我曾經做過一個高效能分散式鍵/值儲存的專案。這是一個設計很精巧的軟體,API非常簡潔。如果你要獲取一個數值,那麼你就用命令:GETN(get, 數值)即可。如果你要存一個數值,那就用命令:PUTN(put, 數值)即可。其他的命令也很簡單,比如MGETN(get multiple, 數值),MPUTN(put multiple, 數值),INCR(增量), MINCR(多個增量),(基本上命令都可以自解釋)。
所有的命令都會被送到一個dispatcher函式去進行解析,辨明邏輯,然後去呼叫相應的處理函式。處理函式基本上也是自解釋風格的,因此程式碼裡面也不需要太多註釋,例如:
1 2 3 |
int Server::handle_getn(…) {…} int Server::handle_mgetn(…) {…} |
不過有一天,有人讓我review一下下面這段程式碼:
1 2 3 4 |
// In Soviet Russia, Putn handles you! // 在前蘇聯,Putn 就會搞定你!【譯註:注意,Putn 和普京的英文拼寫(Putin)非常接近。普京曾經是蘇聯的克格勃。】 int Server::handle_putn(…) { } |
我當即就選擇了通過,然後合併到程式碼庫並且釋出了。據我所知,現在這段程式碼還在程式碼庫裡。
11. Nikunj Madhogaria 的分享
1 2 |
//drunk, fix later //喝大了,一會兒再改 |
1 2 3 |
Catch (Exception e) { //who cares? // ← 這不翻譯了,估計都能看懂吧 } |
我最喜歡的一個註釋是:
1 |
long long ago; /* in a galaxy far far away */ |
讀者如果不明白什麼意思,請看下圖:
12. Sasha Krassovsky 的分享
曾經有一次, 我從學生交給我的程式碼裡隨便挑了一份來看,然後發現了這麼一條註釋:
1 |
/* 不要刪除這段註釋 */ |
當然,我就是要試一下如果把註釋刪除了到底會怎麼樣。所以我就刪除了,然後重新編譯。結果程式真的就不能執行了。然後我把註釋重新加回去,結果又好了。
刪掉註釋以後會報錯 LINK1000,根據連結器錯誤文件的說明,錯誤的原因直接就是:“未知錯誤;請參考文件或尋求技術支援。”
為什麼這個註釋不能去掉呢?我估計這個問題對我來說一直就是個謎。
13. Wojtek Swiatek 的分享
我看過一些資料分析的程式碼,然後就被下面的註釋震驚了:
1 2 3 4 5 6 |
# 不要再用 anal 做變數名了 # [譯註:寫註釋的人是想用 anal 這個縮寫來表示analyze(分析),可是 anal 這個單詞的意思是“肛門”] # 我特麼在哪都能看到 anal 這個詞! # 請不要再這麼做了! # 你們要用就用analyze,或者xbvvzr,要不然用什麼其他的都可以。就是別寫成 anal_insert 或者 anal_check了 #【譯註:insert是插入的意思,check是檢查的意思,其他的你們自行腦補吧】 |
14. Michael Dehmlow 的分享
我新入職了一個公司,然後發現了一段三週之前寫的註釋,這段註釋是專案之前的研發團隊寫的,寫的日期就是我來公司面試和正式入職的這段期間。
我有幸在原來負責這個專案的先生們被炒魷魚的前一天被派來參與這個專案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* 這坨程式碼寫得像屎一樣,寫程式碼的SB做了很蠢的事情 * 他們讓itvinserter.dll依賴egg_end470.dll。 * 然後又讓egg_en470.dll依賴itinserter.dll. 簡直是白痴!不過, * 是有一個合理的辦法能解決這個問題,而我正好打算做點兒深入研究去解決。 * 不過,真正唯一能解決問題的辦法是,找到這個當初寫程式碼的狗孃養的, * 然後衝他的兩腿之間狠狠踢上一腳。雖然這樣解決不了問題,但是這樣 * 能伸張正義。 * * * 真TM是個SB * * 呵呵 * * The EEGEN470::TapeDeck is an abstract class for controlling tape * decks....ah, fuck, I can't do it. I can't even fucking document the hack. * Just figure out, you're a smart fellow. * */ |
15. Ryan Jentzsch 的分享
好多年前我在一個公司工作,我現在都還保留著那時的磁碟,上面有我當時寫的程式程式碼和我對前任CEO留下的註釋。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
/* 這個公司的CEO(也就是我的前任老闆)告訴我可以隨意寫註釋,而且我確實也該這麼做。 我把前任CEO稱為蘸屎(Dip Shit),簡稱DS. DS覺得他自己是一個軟體開發的天才。很遺憾對我來說(我是程式的維護者),我的 主要任務就是擦他沒擦乾淨的屎。 DS是一個想一出是一出的開發者。就像Evel Khievel在他的程式碼做的一樣, 大量使用GOTO語句,而且貿然直接從一個模組 跳到了另外一個模組的IF...THEN語言中間,況且這個模組 和第一個模組根本就沒關係。 說到IF結構,DS真的很喜歡用。他根本就不知道還有 CASE或者SWITCH塊這種東西。然後我就只能去當一個程式碼 考古學家,在深深的IF-ELSE巢狀層次(而且還是沒有縮排的那種)中苦苦挖掘 DS認為他自己很聰明,並且這在他給變數命名的時候就有體現: global i=0; global dateMyWife = 1; datesAreAFruit = '12/12/98'; giveDatesToMyWife = datesAreAFruit + dateMyWife; if (someStuid) { giveDatesToMyWife = false; } // Ryan. Why does this sometimes crash? if (giveDatesToMyWife) goto :hotTub // 好吧DS,因為你修改了giveDatesToMyWife資料型別 (不僅如此,你還把整型和字串型別相加——編譯器已經 警告你了)...總之在弱型別的語言裡你獲得了 腦殘的能力,你時不時修改資料型別,把字串改成布林型,所以當 if(giveDatesToMyWife) 執行時,裡面的變數是一個字串, 程式肯定會崩潰——因為字串不是一個 可邏輯判斷的型別。還有,因為someStuid是全域性的,所以 只有你擁有超能力,才能知道這些全域性變數在哪裡定義, 或者是在哪裡被修改過。 DS只會用全域性變數,而不會用區域性變數。引用他的原話:“ 我用區域性變數,或者私有成員變數的時候,我的程式老是報錯, 說變數未找到什麼的。所以我乾脆就把所有東西都搞成全域性的好了。” 我還有更多的例子,其實如果你也被這種天馬行空的程式碼虐過的話,你就 能體會我是什麼感受了。 */ |
(本系列剛開始,還有後續,等著瞧~ )
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!