<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          C語言函數(shù)調(diào)用:【錯誤碼】和【返回值】傳遞的小思考

          共 2865字,需瀏覽 6分鐘

           ·

          2022-01-18 19:44

          作 ?者:道哥,10+年嵌入式開發(fā)老兵,專注于:C/C++、嵌入式、Linux。

          關(guān)注下方公眾號,回復(fù)【書籍】,獲取 Linux、嵌入式領(lǐng)域經(jīng)典書籍;回復(fù)【PDF】,獲取所有原創(chuàng)文章( PDF 格式)。

          別人的經(jīng)驗,我們的階梯!

          目錄

          • 第一種:輸入、輸出結(jié)果和錯誤碼全部通過參數(shù)傳遞

          • 第二種:函數(shù)返回值表示錯誤碼

          • 第三種:函數(shù)返回值表示輸出結(jié)果

          • 小結(jié)

          • 如果函數(shù)輸出結(jié)果是結(jié)構(gòu)體呢?


          C 語言是一門面向過程的編程語言,通過一個又一個函數(shù),把計算、過程控制等邏輯,包裝成一個個獨立的處理單元。

          既然是函數(shù)調(diào)用,就一定會有參數(shù)和返回值的傳遞問題,因此也就產(chǎn)生了多種不同的編程范式,比如:

          1. Posix 風(fēng)格:函數(shù)返回值只用來表示成功(0)或失敗(非0),其他的輸出結(jié)果都使用參數(shù)來傳遞。

          2. Unix 風(fēng)格:函數(shù)返回值即包括錯誤代碼,也包括有用的輸出結(jié)果。

          3. GAI 風(fēng)格:與 Posix 有點類似,函數(shù)執(zhí)行成功時返回0,否則就返回非0。

          這篇文章就來輕松一下,聊一聊這些函數(shù)調(diào)用范式在開發(fā)過程中的一些小思考。

          我們假設(shè)有一個算法函數(shù),輸入兩個整型參數(shù),輸出一個整型結(jié)果,并且輸出一個錯誤代碼。

          第一種:輸入、輸出結(jié)果和錯誤碼全部通過參數(shù)傳遞

          既然所有的信息都是通過參數(shù)來傳遞的,那么函數(shù)定義就應(yīng)該是下面這樣:

          void func1(int a, int b, int *result, int *err_code)
          {
          int c = a + b;
          *result = c;
          err_code = 0; // 沿用 Linux 中的習(xí)慣,0 表示沒有發(fā)生錯誤。
          }

          因為不需要返回任何數(shù)據(jù),因此函數(shù)簽名的返回類型就是 void 。

          因為調(diào)用者需要獲取輸出結(jié)果和錯誤碼,因此在形參中, resulterr_code需要傳遞指針類型的變量。

          面對這樣的函數(shù)簽名,調(diào)用者就必須顯示的定義兩個變量resulterr_code,用來接收函數(shù)的輸出。

          // 調(diào)用者代碼

          int result, err_code;
          func(1, 2, &result, &err_code);
          if (0 == err_code)
          printf("Success. result = %d \n", result);
          else
          printf("Failed. err_code = %d \n", err_code);

          這種函數(shù)范式的優(yōu)點就是:在調(diào)用形式上統(tǒng)一,無論參數(shù)類型是什么(基礎(chǔ)類型、結(jié)構(gòu)體等待),都是整齊劃一的函數(shù)調(diào)用寫法。

          缺點就是有點累贅。

          面對任何一個函數(shù),調(diào)用者都必須定義一個err_code變量傳遞進去。

          如果一個函數(shù)是過程控制類型的,壓根就不會產(chǎn)生什么錯誤碼,這樣的函數(shù)調(diào)用就顯得很臃腫,因為調(diào)用者壓根就不需要檢查錯誤碼。

          第二種:函數(shù)返回值表示錯誤碼

          也就是把第一種方式中的err_code參數(shù),通過函數(shù)返回值賦值給調(diào)用者。

          這種函數(shù)編程范式還是比較常見的,返回值只表示錯誤碼,其他的輸出結(jié)果都通過參數(shù)引用(指針)來傳遞。

          int func2(int a, int b, int *result)
          {
          int c = a + b;
          *result = c;
          return 0; // 返回錯誤碼
          }

          這樣的函數(shù)范式跟POSIX風(fēng)格很像了。

          面對這樣的函數(shù),調(diào)用者的寫法就會變成這樣:

          // 調(diào)用者代碼

          int result, err_code;
          err_code = func2(1, 2, &result);
          if (0 == err_code)
          printf("Success. result = %d \n", result);
          else
          printf("Failed. err_code = %d \n", err_code);

          看起來好像跟第一種方式?jīng)]有什么本質(zhì)區(qū)別,但是再看一下下面這樣的寫法呢:

          // 調(diào)用者代碼

          int result;
          if (0 == func2(1, 2, &result))
          printf("Success. result = %d \n", result);
          else
          printf("Failed.\n");

          這樣的代碼風(fēng)格,在Linux中是不是很常見?當(dāng)不需要處理錯誤碼時,這樣的編程方式會更方便一些。

          第三種:函數(shù)返回值表示輸出結(jié)果

          也就是把第一種方式中的result參數(shù),通過函數(shù)返回值賦值給調(diào)用者。

          int func3(int a, int b, int *err_code)
          {
          int c = a + b;
          err_code = 0;
          return c;
          }

          這有點類似Unix中的風(fēng)格:

          1. 返回結(jié)果中包括了有用的數(shù)據(jù),但是它有一個局限:返回結(jié)果必須與錯誤碼的類型一致。

          2. 另外還有一個問題:如果 int 型的返回結(jié)果也可能是負數(shù), 所以 Unix 中還必須使用另一個全局變量 errno 來單獨存儲錯誤碼,存在線程安全問題(可以使用線程局部存儲來解決)。

          面對這樣的函數(shù)簽名,調(diào)用者的調(diào)用方式如下:

          // 調(diào)用者代碼

          int result, err_code;
          result = func3(1, 2, &err_code))

          if (0 == err_code)
          printf("Success. result = %d \n", result);
          else
          printf("Failed.\n");

          這種方式的缺點與第一種一樣:必須定義一個變量 err_code,來接收錯誤碼。

          在不必要檢查錯誤碼的場合中,顯得有點多此一舉。

          小結(jié)

          以上的這三種函數(shù)調(diào)用方式,沒有好壞之分,只與每一位開發(fā)者的編碼習(xí)慣有關(guān)系。

          而且在實際的項目代碼中,這三種方式都能看得到。

          如果函數(shù)輸出結(jié)果是結(jié)構(gòu)體呢?

          剛才討論的三種方式中,函數(shù)輸出結(jié)果reuslt是一個整型,如果它是一個結(jié)構(gòu)體類型的變量,那么哪一種方式相對比較好呢?

          這就要注意另外兩點了:

          1. 結(jié)構(gòu)體的賦值是需要時間開銷的;

          2. 結(jié)構(gòu)體賦值時,需要考慮深拷貝、淺拷貝的問題;


          ------ End ------

          當(dāng)看完以上幾個小思考時,是不是覺得特別簡單、不屑一顧?

          不妨繼續(xù)思考一步:在我們的實際編程過程中,是不是每次能夠注意、遵守這些小細節(jié)問題呢?

          如果團隊中沒有強制的代碼規(guī)范,同事之間不會code review,我們是不是都會選擇偷懶、放過自己呢?我就是^-^

          推薦閱讀

          【1】《Linux 從頭學(xué)》系列文章

          【2】C語言指針-從底層原理到花式技巧,用圖文和代碼幫你講解透徹

          【3】原來gdb的底層調(diào)試原理這么簡單

          【4】內(nèi)聯(lián)匯編很可怕嗎?看完這篇文章,終結(jié)它!

          其他系列專輯:精選文章、應(yīng)用程序設(shè)計、物聯(lián)網(wǎng)C語言。


          星標公眾號,第一時間看文章!


          瀏覽 69
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产美女被强躁到呻吟红视频 | 自拍偷拍五月天 | 亚洲视频网站在线观看 | 毛片入口| 少妇 高潮喷水 |