1. 程式人生 > >C語言深度解析(二)

C語言深度解析(二)

 4.變數命名規則
        (1)一般規則:
             a:命名應當直觀且可以拼讀,可望文知意,便於記憶和閱讀。
             b:命名的長度應當符合“min-length && max-information”原則。
             c:當標示符由多個片語成時,每個詞的第一個字母大寫,其餘全部小寫。
             d:儘量避免名字中出現數字編號,如Value1,Value2等,除非邏輯上的確需要編號,比如驅動開發是為引腳命名,飛編號名字反而不好。
             e:對在多個檔案之間共同使用的全域性變數或函式要加範圍限定符(建議使用模組名(縮寫)作為範圍限定符),比如GUI_等。
        (2)識別符號的命名規則
             a:識別符號名分為兩部分:規範識別符號字首(字尾)+ 含義標識。非全域性變數可以不使用範圍限定符字首。
               識別符號名的組成如下:
        模組名縮寫 -- 作用域字首 資料型別字首 【指標字首】 含義標識 陣列、結構字尾
             b:作用域字首命名規則
                
               序號               識別符號型別                  作用域字首
                1                Global Variable                  g
                2                File Static Variable             n
                3                Function Static Variable         f
                4                Auto Variable                    a
                5                Global Function                  g
                6                Static Function                  n
             c:資料型別字首命名規則
               略
             d:含義標識命名規則,變數命名使用名詞性片語,函式命名使用動詞性片語
               變數含義識別符號構成:目標詞+動詞(過去分詞)+【狀語】+【目的地】
              函式含義識別符號構成:動詞(一般現在時)+目標詞+【狀語】+【目的地】
             e:程式中不得出現僅靠大小寫區分的相似的識別符號。
             f:一個函式名禁止被用於其他之處。
             g:所有巨集定義、列舉常數、只讀變數全用大寫字母命名,用下劃線分割單詞。
               const int MAX_LENGTH = 100;//這不是常量,而是一個只讀變數
             h:考慮到習慣性問題,區域性變數中可採用通用的命名方式,但僅限於n、i、j等作為迴圈變數使用。
               一般來說習慣上用n,m,j,i,k等表示int型別的變數;c,ch等表示字元型別變數;a等表示陣列;p等表示指標。
             i:結構體被定義時必須有明確的結構體名。另外,所有結構和聯合的型別在轉換單元的結尾應該是完整的。
             j:定義變數的同時千萬別忘了初始化。定義變數時編譯器並不一定清空了這塊記憶體,它的值可能是無效的資料。
             k:不同型別資料之間的運算要注意精度擴充套件問題,一般低精度資料向高精度資料擴充套件。
             l:禁止使用八進位制的常數(0除外,因為嚴格意義上來講0也是八進位制數)和八進位制數的轉義字元。
        (3)signed,unsigned             
             a:單純的char型別應該只用於字元值的儲存和使用‘有符號和無符號的“char”型變數只能用於數值的儲存和使用。===》
              char有三種不同的型別:單純char、signed char 及 unsigned char.
              signed char 和 unsigned char 型別是用來宣告數值的;單純char型別是真正的字元型別,是用來宣告字元的。對於單純char型別,唯一允許的操作是賦值和相同運算子(=              ,==,!=)。
             b:所有無符號型常量都應該帶有字母U字尾
         (4)if.else組合
          @1.bool變數與“零值”進行比較
          寫法:   if(bTestFlag)     if(!bTestFlag)
          大家都知道if語句是靠其後面括號裡的表示式的值來進行分支跳轉。表示式如果為真,則執行if語句後面緊跟的程式碼;否則不執行。那顯然,本組的寫法很好,既不會引起誤會,也不會由於TRUE 或FALSE 的不同定義值而出錯。記住:以後寫程式碼就得這樣寫。
          @2.float變數與“零值”進行比較
          寫法:   if((flestVal >= -EPSINON) && (flestVal <= EPSINON))//EPSINON 為定義好的精度
              a:使用浮點數應遵循已經定義好的浮點數標準。
                    在表示浮點數的各個位元組中,究竟用多少為表示小數部分,多少位表示指數部分,標準C中無具體定義。
                    ANSI/IEEE標準的基本規定如下所述:
                        *兩種基本浮點格式:單精度和雙精度。
                        *兩種擴充套件浮點格式:單精度擴充套件和雙精度擴充套件。
                        *浮點運算的準確度要求:加,減,乘,除,平方根,餘數,將浮點格式的數舍入為整數,在不同浮點格式之間轉換,在浮點和整數格式之間轉換以及比較。
                        *在十進位制字串和兩種基本浮點格式之一的二進位制浮點數之間進行轉換的準確度,單一性和一致性要求。
                        *五種型別的IEEE浮點異常,以及用於向用戶指示發生這些型別異常的條件。
                         五種型別的浮點異常是:無效運算,被零除,上溢,下溢和不精確。
                        *四種射入方向:
                             &向最接近的可表示的值;
                             &當有兩個最接近的可表示的值時,首選“偶數”值;
                             &向負無窮大(向下);
                             &向正無窮大(向上)以及向0(截斷)。 
          @3.指標變數與“零值”進行比較
          寫法:    if(NULL == p)    if(NULL != p)
          @4.else 到底與哪個 if 配對
          c語言有這樣的規定:else始終與同一括號內最近的未匹配的if語句結合。
                a:程式中的分界‘{’和‘}’對齊風格:
                      程式碼的縮排一般為4個字元,但不要使用Tab鍵,因為不同的編輯器Tab鍵定義的空格數量不一樣,別的編輯器開啟Tab鍵縮排的程式碼可能會一片混亂。
          @5.if語句後面的分號
      if(NULL != p);
           相當於
           if(NULL != p)
           {
                ;
      }
      建議在真正需要用空語句時寫成這樣:
            NULL;而不是單用一個分號。
          @6.使用if語句的其他注意事項
                 a:先處理正常情況,再處理異常情況。
                       因為,if語句總是需要做判斷,而正常情況一般比異常情況發生的概率更大。如果把執行概率更大的程式碼放到後面,也就意味著if語句將進行多次無所謂的比較。另外,非常重要的一點是,把正常情況的處理放在if後面,而不要放在else後面。當然這也符合把正常情況的處理放在前面的要求。
                 b:確保if和else子句沒有弄反。
                 c:賦值運算子不能使用在產生布爾值的表示式上。
                 d:所有的if-else if 結構應該由else 子句結束。
                       不管何時一條if語句後有一個或多個else if語句都要應用本規則;最後的else if必須跟有一條else 語句。而if語句之後就是else語句的簡單情況不在本規則之內。
                       對最後的else語句的要求是保護性程式設計。else語句或者要執行適當的動作,或者要包含合適的註釋以說明為何沒有執行動作。這與switch語句中要求具有最後一個default子句是一致的。
         (5)switch,case組合
          @1.if,else一般表示兩個分支或是巢狀比較少的分支,但如果分支很多的話,還是用switch,case組合吧,這樣可以提高效率。
                 a:每個case語句的結尾絕對不要忘了加break,否則將導致多個分支重疊(除非有意使多個分支重疊)。
                 b:最後必須使用default分支。即使程式真的不需要default處理,也應保留default語句。
                 c:在switch case組合中,禁止使用return語句。
                 d:switch表示式不應是有效的布林值。
          @2.case關鍵字後面的值的要求:
          case後面只能是整型或字元型的常量或常量表達式。
          @3.case語句的排列順序
                 a:按字母或者數字順序排列各條case語句。
                 b:把正常情況放在前面,而把異常情況放在後面。
                   如果有多個正常情況,把正常情況放在前面,並做好註釋;把異常情況放在後面,同樣要做好註釋。
                 c:按執行頻率排列case語句。
                   把最常執行的情況放在前面,而把嘴不常執行的情況放在後面。最常執行的程式碼可能也是除錯的時候要單步執行最多的程式碼。如果放在後面的話,找起來可能會比較困難,而放在前面的話,可以很快找到。
          @4.使用case語句的其他注意事項
                 a:簡化每種情況對應的操作。
                  case語句後面的程式碼越精煉,case語句的結果就會越清晰。如果某個case語句確實需要這麼多的程式碼來執行某個操作,那可以把這些操作寫成一個或幾個子程式,然後在case語句後面呼叫這些子程式就可以了。一般來說,case語句後面的程式碼儘量不要超過20行。
                 b:不要為了使用case語句而刻意製造一個變數。
                   case語句應該用於處理簡單,容易分類的資料。如果你的資料並不簡單,那麼會用if-else if的組合更好一些。如果為了使用case而刻意構造出來的是很容易把人搞糊塗的變數,那麼就應該避免這種變數。
                 c:將default子句只用於檢查真正的預設情況。
                   如果將最後一種情況用default來處理,這樣將失case語句的標號所提供的自說明功能,而且也喪失了使用default子句處理錯誤情況的能力。所以,要把每一種情況都用case語句來完成,而把真正預設情況的處理交給default子句。
          (6)do,while,for關鍵字
           @1.break,與continue的區別
           break關鍵字表示終止本次迴圈;
           continue關鍵字表示終止本次(本輪)迴圈,進入下一輪迴圈。
           while(1)也有寫成while(true),while(1==1),while((bool)1)等形式的,效果一樣。
           @2.迴圈語句的注意點
                 a:在多重迴圈中,如果有可能,應當將最長的迴圈放在最內層,最短的迴圈放在最外層,以減少CPU跨切迴圈層的次數。
                 b:建議for語句的迴圈控制變數的取值採用“半開半閉區間”寫法。(更直觀)
                 c:不能在for迴圈體內修改迴圈變數,防止迴圈失控。
                 d:迴圈要儘可能短,要使程式碼清晰,一目瞭然。
                   一般來說,迴圈內的程式碼不要超過20行。要不然,就將這些程式碼改寫成一個子函式,迴圈中只調用這個子函式即可。
                 e:把迴圈巢狀控制在三層以內。
                 f:for語句的控制表示式不能包含任何浮點型別的物件。
                   舍入誤差和擷取誤差會通過迴圈的迭代過程傳播,導致迴圈變數的顯著誤差,並且在進行檢測時很可能給出不可預期的結果。
           (7)goto關鍵字
                 a:禁用goto語句。
                   首先,由於goto語句可以靈活跳轉,如果不加限制,它的確會破壞結構化設計風格;
                   其次,goto語句經常帶來錯誤或隱患,他可能跳過了變數的初始化,重要的計算等語句。
           (8)void關鍵字
            @1.void a
            void 真正發揮的作用在於:對函式返回的限定;對函式引數的限定。
            void * ,任何型別的指標都可以直接賦值給它,無需進行強制型別轉換;
            但這並不意味著,void * 也可以無需進行強制型別轉換地賦值給其他型別的指標。
            @2.void修飾函式返回值和引數
                  a:如果函式沒有返回值,那麼應將其宣告為void型別。
                    在c語言中,凡不加返回值型別限定的函式,就會被編譯器作為返回整型值處理。加上void型別聲明後,可以發揮程式碼的“自注釋”作用。所謂程式碼的“自注釋”即程式碼能自己註釋自己。
                  b:如果函式無引數,那麼應宣告其引數為void。
                    在c語言中,可以給無引數的函式傳送任意型別的引數,但是在C++中,不能向無引數的函式傳送任何引數。
                    所以,無論在C還是C++中,若函式不接受任何引數,則一定要指明引數為void。
            @3.void指標
                  a:千萬小心地使用void指標型別。
                    按照ANSI標準,不能對void指標進行演算法操作。
                    ANSI標準之所以這樣認定,是因為它堅持:進行演算法操作的指標必須是確定知道其指向資料型別大小的,也就是說必須知道記憶體目的地址的確切值。
                  b:如果函式的引數可以是任意型別指標,那麼應宣告其引數為void*。
            @4.void不能代表一個真實的變數。
                  a:void不能代表一個真實的變數。因為定義變數時必須分配記憶體空間,定義void型別變數編譯器到底分配多大的記憶體,不知道。
            (8)return關鍵字
             return用來終止一個函式並返回其後面跟著的值。
                  a: re,因為該記憶體函式體結束時被自動銷燬。
            (9)const關鍵字也許該被替換為readonly//execl表格中
            (10)最易變的關鍵字--volatile
                  volatile是易變的,不穩定的意思。
                  volatile關鍵字和const一樣是一種型別修飾符。遇到這個關鍵字宣告的變數,編譯器對訪問該變數的程式碼就不在進行優化,從而可以提供對特殊地址的穩定訪問。
                  volatile int i= 10;
                  int j = i;
                  int k = i;
                  volatile關鍵字告訴編譯器,i是隨時可能發生變化的,每次使用它的時候必須從記憶體中取出i的值,因而編譯器生成的彙編程式碼會重新從i的地址處讀取資料放在k中。
            (11)extern
                  extern可以置於變數或函式前,以表明變數或函式的定義在別的檔案中,下面程式碼用到的這些變數或函式是外來的,不會死本檔案定義的,提示連結器遇到此變數和函式時在其他模組中解析/繫結此識別符號。
           (12)struct 關鍵字
                  struct 是個神奇的關鍵字,它將一些相關聯的資料打包成一個整體,方便使用。
              @1.空結構體多大
                 結構體所佔的記憶體的大小是其成員所佔記憶體之和。
                 編譯器理所當然地認為,你構造一個結構體資料型別是用來打包一些資料成員的,而最小的資料成員需要1位元組,編譯器為每個結構體型別資料至少預留1位元組的空間。所有,空結構體的大小就定為1位元組。
              @2.柔性陣列(flexible array)
                 結構中的最後一個元素允許是未知大小的陣列,著就叫做柔性陣列成員,但結構中的柔性陣列成員前面必須至少有一個其他成員。柔性陣列成員允許結構中包含一個大小可變的陣列。sizeof返回的這種結構大小不包括柔性陣列的記憶體。包含柔性陣列成員的結果用malloc()函式進行記憶體的動態分配,並且分配的記憶體應該大於結構的大小,以適應柔性陣列的預期大小。
                  typedef struct st_type
                  {
                      int i;
                      int a[];
                   }type_a;
                   
 type_a *p = (type_a *)malloc(sizeof(type_a) + 100*(sizeof(int));
             @3.struct與class的區別
                  struct的成員預設情況下的屬性是public,而class成員的卻是private.
             (13)union關鍵字
                 union維護足夠的空間來放置多個數據成員中的“一種”,而不是為每一個數據成員配置空間。在union中所有的資料成員共用一個空間,同一時間只能儲存其中的一個數據成員,所有的資料成員具有相同的起始地址。
                 一個union只配置一個足夠大的空間來容納最大長度的資料成員。
                在C++中,union的成員預設屬性為public。union主要用來壓縮空間。如果一些資料不可能在同一時間同時被用到,則可以使用union。
               @1.大小端模式對union型別資料的影響
                  大端模式:字資料的高位元組儲存在低地址中,而子資料的低位元組則存放在高地址中。
                  小端模式:字資料的高位元組儲存在高地址中,而字資料的低位元組則存放在低地址中。
                  union型資料所佔的空間等於其最大的成員所佔的空間。對union型成員的存取都從相對於該聯合體基地址的偏移量為0處開始,也就是聯合體的訪問不論對哪個變數的存取都是從union的首地址位置開始。
               @2.如何用程式確認當前系統的儲存模式
                    a:對於位域的使用和自定義的行為需要詳細說明,且在使用前需要用程式碼check當前系統的模式(大端或小端模式)。使用位域時,需要特別注意對齊方式是LSB或是MSB。???
                    b:使用帶符號的位域,至少需要兩位來表示一個完整的位域。第一位是符號位,0表示正數,1表示負數;其餘位數表示數值。
                      負數是按照補碼的方式來表示的。
           (14)enum關鍵字(列舉)
               @1.列舉型別的使用方法
                  實際上列舉變數型別是對一個變數取值範圍的限定,而花括號內是它的取值範圍,即列舉變數的變數只能取值為花括號內的任何一個值,如果賦給該型別變數的值不在列表中,則會報錯或者警告。
                  enum變數型別還可以給其中的常量符號賦值,如果不賦值則會從被賦初值的那個變數開始依次加1;如果都沒有賦值,它們的值從0開始依次遞增1.
               @2.列舉與#define巨集的區別
                  A:#define巨集常量是在預編譯階段進行簡單替換;列舉常量則是在編譯的時候確定其值。
                  B:一般在偵錯程式裡,可以除錯列舉常量,但是不能除錯巨集常量。
                  C:列舉可以一次定義大量相關的常量,而#define巨集一次只能定義一個。
             (15)typedef關鍵字
               @1. typedef的真正意思是給一個已經存在的資料型別(注意:是型別不是變數)取一個別名,而非定義一個新的資料型別。
                   在實際專案中,為了方便,可能很多資料型別(尤其是結構體之類的自定義資料型別)需要我們取一個適用於實際情況的別名。
                   a:用typedef重新命名基本的資料型別,以替代原始的資料型別。
            
         typedef                  char                   char_t
         typedef  signed          char                   int8_t?
         typedef  signed          short                  int16_t
         typedef  signed          int                    int32_t
         typedef  signed          long                   int64_t
         typedef  unsigned        char                   uint8_t?
         typedef  unsigned        short                  uint16_t
         typedef  unsigned        int                    uint32_t

         typedef  unsigned        long                   uint64_t          

          typedef                  float                 float32_t

         typedef                  double                float64_t
         typedef  long            double               float128_t
            
               @2.typedef與#define的區別