庫代碼中是否應該檢查malloc的返回值?

假如要檢查,那麼怎麼處理NULL分支才好?

我個人感覺,作為一個庫,肯定不能直接退出進程。而返回錯誤碼又會使介面太過複雜,而且僅僅為了malloc失敗這一種情況,就對整個庫進行一套錯誤處理,太不划算了。

但是不檢查,如果malloc失敗,程序會因為訪問NULL指針而崩潰,這樣算bug嗎?

很糾結,所以,我想問問最佳實踐是什麼?


分場景來說。

1. 如果鏈路需要保障應用在限流薄弱的高壓下苟活,NULL導致的嚴重不可用盡量級聯檢查+附贈TLS錯誤碼走出去,別炸太早。

2. 如果C代碼作為跨語言callee存在,禮貌地crash loudly但考慮別sigsegv。是否abort視應用而定。

3. 和NULL關係不大但多提一句。某些場景(比如linux過提交)下malloc返回非空指針但存儲實際不可用的問題,可以通過限制進程本身避開,歸約到返回NULL上。

4. 庫盡量對用戶報錯,但這要求設計上不要有太深的強依賴,或者強依賴的部分經過仔細收口,否則級聯報錯級聯回滾很痛苦。

真的需要級聯回滾的話,看看編譯器有沒有defer類的擴展(比如attribute cleanup)。用起來會幹凈些。

總之原則就兩個:考慮單點可用性要求、考慮鏈路排查難度。


貼一七年前在C語言吧讀到的貼子:

[拍磚帖]是否應該驗證malloc的返回值不為NULL?【c語言吧】_百度貼吧

想起當年(2009-2011),也常混跡 C、C++ 吧,知道了牛羚、羊駝、帝球、炮姐……

----


最佳實踐就是自己封裝一層malloc/calloc/realloc,出錯了再調用自己的處理函數。包括sgi stl的alloctor也提供類似的oom機制。

代碼引用自redis/zmalloc:https://github.com/antirez/redis/blob/unstable/src/zmalloc.c#L100


C的話abort就好了,C++可以扔異常,不過C++也都用new了吧(new是扔異常的)

向os申請內存的時候,一般是申請到地址空間,並沒有實際分配內存,如果失敗一般是你進程已經用了很多了,或者malloc參數錯誤,這時候let it crash比較好,如果你不想弄很多繁瑣的錯誤返回的話


當然需要檢查,不檢查就是bug,不管你的調用者需不需要處理,你自己都是需要處理的。

你應該至少釋放所有已經分配的內部資源並且返回一個錯誤的值。如果有值得保存的數據,應該嘗試保存。

作為一個庫來說你絕對沒有權利自己去調用abort或者exit,除非調用者違反了你的文檔聲明傳遞了非法的參數。你的庫的調用者才是決定內存分配失敗該怎麼處理的人。在內存分配失敗的時候,調用者可能需要回滾當前事務或者保存當前文檔等等。

如果你想要你的代碼可移植,那麼你不能拋異常,因為異常的實現是編譯器自決的。即使是同一個牌子的編譯器,不同版本的編譯器的實現也可以不同。如果你不能控制調用者的編譯器版本,不要拋異常。


要檢查,NULL的話,如果你覺得程序還有救,那就救一下,否則就 printf("沒內存跑不下去了"); abort();


取決於你的業務邏輯。如果對於擼棒性沒有特別的需求,比如特別地需要內存跑完了也能接著跑,就直接死掉。

如果你是在設計庫,並且想讓用戶知道這種事情的發生,而不是自己默默地死掉,那麼C風格的庫可以設計成這樣:

  • 將庫的動態內存分配,集中在一些用戶操作的API上,比如my_system_create、my_system_init。
  • 此時如果內存分配掛掉了,那麼返回給用戶一個空指針、錯誤結果值,然後設置個errno什麼的。


程序可以不處理, 庫不行.

怎麼處理,參見stl中bad_alloc異常和 no exception版本.


if (p == NULL) abort();


理論上,既然一個函數可能返回空,當然需要檢查。

不過呢,由於內存不夠基本都是設計失誤,內存泄漏導致的,因此讓它崩掉,dump環境備查也是種不錯的處理方法。

內存分配是相當頻繁的,檢查帶了的開銷不可忽視,所以我推薦不檢查。


看了這麼多還是 @蔣晟 說的有道理,畢竟是專家。我只想說,malloc出錯為什麼一定系統錯誤?這個結論是錯誤的!任何錯誤的內存錯誤操作都會導致malloc報錯,比如內存泄露等。所以還是按專家說的做吧


malloc是可以在鏈接時被替換為其他實現的,比如tcmalloc等,所以就算你內存足夠也有可能會因為對應庫里的邏輯拿到nullptr,不一定要虛內存地址不足。

總之還是自己定奪,很多時候拿到nullptr以後let it crash也不是壞事


我不會,我以後想找一個學這個的老公,嗯


自己的應用程序的話可以根據實際情況,庫的話,一般內存用盡了,得返回錯誤碼吧!或者你庫也做聲明,不處理內存用盡的情況,但是一般情況下,庫都會提供分配資源的 API !


我只想說,底層向上只提供機制不應提供策略


我認為不應該給每個malloc都檢查。首先你可以確定在現在的大內存時代你的程序不會跑到一點內存不剩(一般不應該在循環中做malloc,但是如果非要並且給了一個很大的循環計數,一般都是bug引起的,這種情況下導致內存耗盡時,表現為程序很卡,這時直接結束進程是最好的辦法),並且操作系統能夠保證為你分配一個可以使用的內存地址,這些都不用去操心。你唯一要操心的是自己寫的程序有沒有出現邏輯錯誤,導致被分配的內存指針指向了其他地方或者null,所以,正確的做法是在每次使用指針時檢查,這樣很麻煩但是很有必要,這種檢查就把malloc異常以及野指針的情況都包含進去了。由於這種檢查的存在,使得你在寫代碼時可以考慮設計在指針錯誤的情況下程序的運行線路,所以程序會正確運行下去而不是被粗暴地結束掉整個進程。但這種檢查也不必過於頻繁,比如指針作為入參的情況下,只在函數開始處檢查一下,寫的函數體保證不讓指針改變(或者用xxx const *更好)就可以了。


不需要檢查,內存都沒了還呆著幹嘛,趕緊洗洗睡,讓watch dog重新喚醒一下。


建議還是添加一個判斷並列印錯誤信息。因為這種malloc出問題的情況極少而且一般出問題基本上是系統有問題了。這種小概率且嚴重的問題在工程上來說是很重要的,比如一個程序跑個一兩年才會出malloc問題,你想復現或者排查問題的時候再等一年?

如果是一般的小程序。比如作業啥的不檢查問題也不大。但是請養成良好的編程習慣。如果你要做工程,做項目啥的。程序的健壯性還是很必要的。注意那些warning,並不是不必要的提示。


當然要。既然是個庫,那就返回個錯誤碼出來.


首先回答一下,null導致空指針異常是bug,可以認為是開發者考慮不周全。這個問題屬於演算法範疇。舉個例子吧,比如java中向虛擬機申請空間,但是為虛擬分配的空間不足,是會出現內存溢出,然後程序崩潰。但是虛擬機會自動保存error信息,告訴開發者崩潰的原因。

再比如說,vs要運行一個程序,但是比如說需要管理員許可權,會自動提示用戶是否重新啟動,然後以管理員身份運行。

因為程序的魯棒性是一個非常重要的衡量標準,一個給別人用的庫需要很高的穩定性,以及可調試可擴展性。如果malloc失敗,沒有任何處理,甚至沒有任何錯誤信息,這個庫是沒有辦法給別人使用的。可以說,這種做法是非常不負責任的,別人用了你的庫,因為庫內部原因,程序崩潰了,但是一點信息都不給別人,這不是坑人嗎。

從演算法角度,假如是內存足夠的計算機,malloc失敗的概率比較小,而且malloc失敗處理的代碼時間複雜度其實可以看做是一個常數,增加一個常數的時間複雜度並不會對整體效率有什麼影響。

而加了統一的異常處理,不僅使得庫的穩定性增加,而且留好了擴展的餘地,因為其他異常也可以用統一的異常處理來處理。

然後在演算法上面,函數返回值一般作為狀態指示,或者說要包含執行狀態的指示,也就是,如果別人調用你的函數,執行失敗了要有體現,習慣上用返回值,也可以用引用傳遞,c裡面沒有引用的話用指針間接訪問傳出執行狀態。


推薦閱讀:

python如何畫出這樣漂亮的地圖呢?
C++中編寫強異常安全的代碼真的有必要麼?
能不能對QVariant進行引用/指針讀寫數據?
eclipse為什麼不能做的好用一點?好看一點?
土豪程序員的設備都有啥?

TAG:編程 | C編程語言 | CC | 異常處理 |