linux環境C編程的血淚教訓

來源:互聯網
上載者:User

     初學linux平台上的C編程時間不長,這次正好有一個商務專案需要用到隊列,研究和對比了一下市面上的相關產品,總體而言不是太複雜就是效能達不到要求,所以最後還是決定自己寫一個。這次用C完完全全由自己實現只是第二次,以前都是下個開源軟體改一下,一般來說linux下的軟體只要是C開發的,效能都可以接受。但是為了……,還是自己決定寫一下。在整個開發過程中,碰到的血淚教訓太多了,這裡先記錄一下,第一:警示自己,以後不要再犯了;第二:給有用的人分享一下,別人跳過的坑盡量避免自己再跳(好像我經常會跳??嘻嘻)!

    1.記憶體流失;這是一個老問題了,這次開發的mq使用到了tc伺服器介面,本著小心的態度,我幾乎每個malloc的對象都會儘快釋放,並且寫到malloc的時候就緊張,一定要在這個函數中查看一下,是否把malloc的對象都free了。但是tc還是把我害死了,因為用到了從tc中取值的get函數,結果這個函數返回的值需要有開發人員自己free掉的,結果……,後來導致記憶體錯亂才發現了錯誤。引起這個問題的原因是沒有仔細看tc的文檔,咳,杯具。

   2.指標參數:這個問題也可以歸結為記憶體流失。對於malloc的變數,free之後一定要置為NULL,這樣我們就可以通過判斷這個自增是否為NULL來編程。所以為了簡單和不至於忘記最後的一個置NULL操作,我把這個過程寫成了函數:

     void mem_free(void *ptr)

     {

          if(NULL != ptr)

         {

               free(ptr);

               ptr = NULL;

        }

     }

不仔細看沒發現問題吧,把ptr的指標free掉,然後NULL操作,但是問題來了,當我為char *buff 執行mem_free(buff)函數後,發現第二次運行mem_free(buff)發現NULL != ptr竟然為true,鬱悶了吧?這個問題搞了我半天時間,後來查看相關書籍才發現,當第一次mem_free的時候,free確實把記憶體給清除了,但是壞就壞在ptr = NULL;上,注意這個時候ptr只是一個指向buff指標的副本,也就是這個時候運行時態的指標可以理解成這樣ptr->buff->heap,free是因為沒有改變ptr的指向,只是free掉了值,所以heap中的值被清除了,但是ptr = NULL,其實是切斷了ptr –> buff的這根鏈,那麼,buff ->heap這個鏈沒有斷開,所以其實buff還是指向這heap這個記憶體,雖然heap中已經不存在任何有用的資料了。但是我們的本意是要斷開buff –> heap這個鏈,所以這個函數應該寫成傳遞二級指標:

     void mem_free(void **ptr)

     {

          if(NULL != *ptr)

         {

               free(*ptr);

               *ptr = NULL;

        }

     }

這個問題可以總結為:改變指標指向的內容不需要傳遞指標地址,改變指標的指向,一定要傳遞指標的地址。

   3.字串(或者是字元數組,一下稱字串)結束符。對於字串結束符很多人都沒有搞明白,包括之前的我。每個字串都會在最後加上一個’\0’結束符,以示這個字串結束了,如果人為手工沒補齊,那麼自動補齊。我的經驗就是不管什麼時候(聲明char *buff = “i am chinese.”這種時候除外),特別是執行了strcpy或者memcpy後,如果你copy的是字串,那麼一定要手工上去補齊一下,當然,如果你memcpy的是二進位記憶體,那句不需要補齊了。

    4.記憶體補齊。這個問題困惑了我2天。比如有一個結構體:

     typedef  struct header_type

     {

         char protocol;

         int state;

         int64_t bodylen;

     } header_t;

    那麼 sizeof(header_t)和sizeof(char) + sizeof(int)+sizeof(int64_t)這兩者的值一樣嗎?答案是不一樣的。不要說自己看錯了,我確定是不一樣的,至少在我機器上是不一樣的。前者在我機器上的值為16,後者加起來為13.原因就是記憶體補齊。對於c而言,在申請的記憶體的時候會啟用記憶體補齊,補齊規則有的定死了,有的可以調整,具體需要看cpu和編譯器。在我的機器上是2^n規則,1,2,4,8,16…….所以長度為13的補齊後就是16了。那麼如果在同一機器上,這點可以忽略,但是如果你要通過網路傳遞這個結構體,那你就不能忽略這個了。原因有二:如果你直接傳遞這個結構體的變數,那麼需要考慮網路位元組序和記憶體大小端,這個太麻煩,放棄,第二:不考慮原因一的變通方法是全部使用char *傳遞。那麼如果你使用sizeof(header_t)申請記憶體,這個時候用戶端接收到buff傳遞的值是16位的,最後的3位其實是無用的,但是用戶端並不知道,所以會引起記憶體混亂。解決辦法就是申請char *buff記憶體的時候,長度要是真正的記憶體大小,不要把記憶體對齊的空隙加入進去。但是你使用malloc給headet_t的對象申請記憶體時要使用sizeof(header_t),因為不使用sizeof的話就會出現記憶體溢出。

  5.recv的時候不管是不是non-block模式,都需要判斷是否逾時。我就碰到這個問題,設定了timeout後,沒有判斷逾時,用戶端接收到伺服器返回的結果非常的不穩定,一會兒好的(在逾時時間內返回),一會兒又都是亂碼。後來發現原來recv返回-1了,並且errno置為11了。解決了這個問題後就不會出現接收不到資料的情況了。

   6.位元組長度:因為問題5的出現,導致了我懷疑伺服器端的資料轉送問題,所以就增加了列印header_t的buff的想法,但是這個buff是一個二進位,直接列印二進位不行,只能轉換到16進位,然後列印。所以加入了以下代碼;

   char header_buff[14];

    bin2hex(buff,13,header_buff);

    log(header_buff);

   結果程式每次運行到這裡的時候就會引發系統abort的訊號。開始找不到原來。後來經人指點,發現header_buff申請的空間錯了。header_t的二進位長度為13,但是每個位元組16進位表示需要2個位元組,所以13個二進位位元組需要26個位元組(有點繞口),再加上一個字串的結束符,所以應該申請的是27個字元。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.