程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 個人c語言編程風格總結,c語言編程風格

個人c語言編程風格總結,c語言編程風格

編輯:關於C語言

個人c語言編程風格總結,c語言編程風格


  總結一下我個人的編程風格及這樣做的原因吧,其實是為了給實驗室寫一個統一的C語言編程規范才寫的。首先聲明,我下面提到的編程規范,是自己給自己定的,不是c語言裡面規定的。

  一件事情,做成和做好中間可能隔了十萬八千裡。

  同樣的,代碼的質量也極大程度上反映了編程者的水平高低。為了讓大家從學習的開始就養成良好的編程習慣,創作出優質的代碼,實驗室編輯這個文檔,作為大家編程的參考,同時也是對以後編程風格的硬性規定。

  對於一個團隊來講,制定統一的編程規范,好處是顯而易見的。通常一個項目是由多個成員共同完成,在項目中,經常互相調用組內成員的代碼。如果兩個人的編程習慣和風格差異顯著,那麼將會浪費大量時間在讀懂代碼上。相反,一致而良好的編程規范,會讓合作開發變得輕松而高效。

  眾所周知,C語言是面向過程的語言。也就是說,程序員要對程序的每一步有精准的把握,知道每一條程序語句的執行內容及其結果。因而,代碼的可讀性就顯得尤為重要。這裡的可讀,不僅僅是對自己可讀,也要對其他人可讀。一段只有自己能讀懂的代碼,可以說價值很低,而且這樣的代碼隨著時間的推移往往自己也讀不懂。而可讀性強的代碼,不僅方便移植與交流,更給調試帶來了難以估量的便利。

  讀一段好的代碼,會有一種讀英語文章的流暢感。盡管C語言提供了有限的32個關鍵字,但是變量、函數等的命名卻提供了較大的自由,這也是我們將代碼語句化的基礎。試想,如果一段代碼有了主謂賓結構,即使不懂編程的人,也能明白代碼的功能。而這正是我們代碼編輯者追求的目標。

  所以,寫好一段代碼,從把你的代碼讀者當編程小白開始!

 

一、文件管理

 

  每一個做技術的人,無論軟硬件,計算機裡都應該有一個純英文的盤符,注意我是說英文,而不是pinyin。在這個純英文的盤符裡,當然是存放各種技術相關的軟件、程序以及文檔。而這些內容的命名也應該是英文的,包括各個子文件夾。其他諸如即時通訊軟件、游戲文件等應該放在其他盤符內。一方面,這樣是對自己英文水平的鍛煉;另一方面,也能避免很多在使用國外軟件的時候出現的各種BUG。

  每一個軟件,都應該放在一個獨立的文件夾中。這樣既方便查找,又避免混亂。因為我們都知道每一個軟件完成後,都不僅僅是一個exe文件那麼簡單,通常還有各種後綴的文件,而這些我們都不能刪除。如果打開D盤時,映入眼簾的是幾萬個由不同軟件安裝時生成的各種文件,相信給誰都會一臉大寫的懵逼。因此,將不同的軟件放在單獨的文件夾下是非常有必要的。

  不同IDE下編寫的程序,也都應該存放在獨立的一個文件夾下。文件夾內,不同的工程也應該分別建立文件夾,並合理而精准命名。這樣為日後的查找帶來極大的便利。

  很多IDE在編寫程序文件時,除了要建立Project(工程),還要建立Workspace,即工作空間。工作空間通常是指定一個空間(也就是文件夾),IDE啟動時,自動打開該空間下的各個Project。因此,一個Workspace可以存放多個Project。這樣我們就可以利用Workspace管理自己在該IDE下編寫的各個Project。前提是你建立了Workspace,而Project存放在這個Workspace下。

  每一個獨立的項目都應該是一個獨立的Project。例如,分別練習編寫流水燈和數碼管的程序時,要分別建立Project,而不能放在一個Project下,除非你的項目同時用到了流水燈和數碼管。這樣做的好處是你可以Project名稱上精確獲得其內容信息,而不會出現程序寫完過一段時間後無從查找的情況。

二、命名規則

 

  首先說一下總的命名規則:命名一定要用英文。並不是因為拼音不可以,而是因為我們要與國際接軌,要養成良好的英文書寫習慣。其次,命名中除了“\/:*?”<>|“等系統不允許的字符外,也不能出現除英文字母、下劃線、數字外的其他字符。如果你想命名成flash LED.c,中間的空格要用下劃線”_“來代替,寫成flash_LED.c。另外,命名中可以出現必要的數字。

1、文件/文件夾命名

  文件命名要精確,文件名要准確反映文件內容。寫的是

  文件命名一律使用小寫字母,如keyboard.c。

  如有縮寫單詞,則必須大寫,如flash_LED.c、UART.c。其中LED是Light-Emitting Diode(發光二極管)的縮寫,UART是Universal Asynchronous Receiver/Transmitter(通用異步收發器,也就是串口)的縮寫。對於有約定俗成縮寫的單詞,就使用縮寫詞匯。

  文件名應使用名詞,而不應該使用動詞。如果文件內容是數據采集,應該命名為data_collection.c而非data_collect.c。

2、標識符命名

     C語言中,可以定義各種標識符作為變量名、數組名、函數名、標號及用戶定義對象的名稱。ANSI C規定標識符必須由字母和下劃線開始,隨後可以出現字母、下劃線和數字。

1)變量命名

     變量命名一律小寫,縮寫詞匯用大寫,且全部使用名詞,可以使用形容詞修飾,用“_”表從屬關系。因為變量名作為一個變量的名字,就應該是一個名詞。

     局部循環體控制變量用i,j,k。如for(i=0;i<100;i++)。

     指針變量用“p_”開頭,後面接指向內容。如指向高度變量的指針,命名為“*p_height”。請讀者自行區分指針和指針變量的區別。

     局部變量盡量用一個單詞表達清其含義。

     全局變量命名時首先寫所屬模塊名稱。例如如一個傳感器文件sensor.c裡面的一個全局變量要代表溫度,則命名為sensor_temperature。又例如LCD(液晶顯示屏)文件LCD.c中表示LCD狀態的全局變量命名為LCD_status。因為全局變量往往跨文件調用,如不寫清變量定義位置,當程序龐大,而IDE又不支持一鍵定位時,查找起來很麻煩。即使IDE支持一鍵定位,一個清楚明白的命名,能讓人瞬間讀懂該變量的含義。

2)數組命名

     數組命名各單詞首字母大寫,其他同變量。

讀者可能會有疑問,數組名後面會有[]符號,與變量區別明顯,為什麼要用首字母大寫的方式。實際上,在數組名作為實參傳遞數組首地址時,往往會省略[]符號,應該數組名就是數組的首地址。例如:

  unsigned char string[]=”abcdefg”;

   printf(“%s”,string);

  在以上代碼中,string是一個8位數組(為什麼是8位?),在使用printf()函數輸出時,只寫了數組名,顯然這種方式是被允許的。而此時就沒有寫[],在這種情況下,並不能瞬間知道string是變量還是數組,而需要參考前面的格式控制符“%s”。在其他函數中,或許沒有“%s”這樣的格式控制符幫助我們判斷string到底是數組還是變量,我們只有找到函數的聲明或定義才能知道答案,嚴重影響閱讀。因此有必要對數組和變量加以區分。

3)函數命名

  函數命名各單詞首字母大寫,寫成主謂語形式,主語用名詞,謂語用動詞,縮寫詞匯用大寫,用“_”表從屬關系。主語通常為模塊名,而謂語是描述模塊的動作。因為函數本身就是用來執行一系列的動作的, 結合函數參數,可以表達通順的語句。舉個簡單的例子:延時函數。定義一個ms級延時函數為:

  void Delay(unsigned int ms);(這個其實是聲明,函數體不想寫了)

  調用時寫:

  Delay(500);

  很顯然是延時了500ms。而如果再用個宏定義:

  #define MS500 500

  Delay(MS500);

  是不是更一目了然呢?

  另外還比如串口發送函數命名UART_TX( ),調用時寫成:

  UART_TX(time); (通常發送數據Transmit Data簡寫為TXD)

  顯然意思是串口發送時間數據。

  再比如設置參考值的函數命名為REF_Set( ),調用時寫成:

  REF_Set(current_voltage);(通常參考值Reference簡寫為REF)

  顯然意思是將當前的電壓設置為參考值。

  主謂格式的命名大大增加了代碼的可能性。

  當然,函數命名中必要時可以出現賓語。這種情況多出現在函數沒有參數的情況下。如一個函數的功能是LCD顯示時間,而時間是全局變量,因此這個函數就不需要參數,此時直接定義成void LCD_Display_Time(void)(其實是聲明,因為沒寫函數體)。

  命名時首字母大寫不會和數組混淆嗎?顯然不會,因為函數不論是在定義、聲明還是調用的時候後面都必須跟著”( )”。 

4)標號命名

  由於在硬件編程中標號可以用循環來代替,所以很少用到。我們規定標號的命名格式基本同變量,使用全部小寫的名詞,但是只用一個單詞表示即可。因為標號時候的時候或者前面加了goto,或者後面加了“:”,很容易與變量區分開。況且只是一個定位標志,所以一個單詞足夠了。

5)自定義類型命名

  自定義類型命名主要指使用typedef定義的新類型名,以及結構體類型、共用體類型的類型名(而非該類型的變量名)。

  自定義的新類型名,只用一個單詞,首字母大寫。但是定義這種新類型的變量時,命名規則與變量命名規則完全相同。

  請自行體會新類型名與新類型變量的區別。

6)宏定義命名

  宏定義命名全部使用大寫字母,單詞數不限。可以加入數字和下劃線,但是數字不能開頭 。

  由於宏定義的特殊性,對其使用名詞或動詞不作規定。因為宏定義一個函數時,應該是動詞性質,而宏定義一個常數時,應該是名詞性質。

 

三、表達式書寫

 

  表達式書寫時,最重要的是意義明確。由於C語言不同運算符有著不同的結合順序和優先級,因此很容易造成歧義,即實際運算順序與設想運算順序不同。除了完全理解並熟記結合順序與優先級,最簡單的方法就是用括號來明確運算順序——在表達式中,括號的優先級是最高的。

  另外,運算符與其操作數之間要空格。如:

  a=a+b;

  應寫成:

  a  =  a  +  b;

  這樣做可以讓表達式顯得不那麼擁擠而增加可讀性,但這不是重點。這樣做的重點是幫我們避免很多不易識別的錯誤。如:

  a=a/*b;

  我們的本意是a除以指針變量b指向的內容,然後將商賦給a。然而殘酷的現實是,編譯器發現了連起來的“/*”,沒錯,這是注釋符。所以,後面的內容都會被注釋掉,直到找到最近的“*/”。

  所以我們應該寫成:

  a  =  a  /  *b;       //指針運算符*應該緊跟指針變量b

  或者:

  a=a/(*b);        //不過即便這樣寫也應該加入空格,便於閱讀

  有人會說,現在的IDE會用不同的顏色提示注釋內容,所以這樣的錯誤應該不會出現。但是我想說的是,作為一個立志做合格的工程師的你,會允許自己有不嚴謹的習慣嗎?況且本身我們的文檔是為了在C語言語法、詞法基礎上,制定一個編程規范。

  另外,有些老版本的C編譯器允許用=+來代替+=的含義,即復合賦值號的兩個符號順序可以是反的。這樣的話,如果寫出:

  a=-1;

  本意是將-1賦給a,但是編譯器卻會理解成:

  a  =  a  -  1;

  顯然意義完全變了。

  有人又會說了,你不是說老版本的C編譯器嘛,我不用不就行了嗎。然而,我們要考慮代碼的可移植性,就絕不應該允許這樣的想法。

  因此,在書寫表達式的時候,不要吝惜你的空格和括號。

  還有一點值得說明的是,復合賦值運算符的兩個運算符不能分開。如“+=”不能寫成“+  =”。

 

四、文件編寫

 

1、文件劃分

  一個簡單的程序,只有幾行到幾十行,放在一個文件內一目了然。但是一個較大的項目中可能會有成千上萬行代碼,更有大型程序代碼數以百萬行計。這樣規模的代碼,存放在一個文件內,其恐怖程度請自行想象。

  當一個函數的代碼量超過幾十行時,就應該考慮有沒有可能把其中某些代碼提取出來打包成另一個函數然後調用。同樣的,當一個文件的代碼量超過幾百行時,就應該考慮有沒有可能把一些函數分出來放到別的文件中去。這樣做都是為了程序的可讀性和方便調試,畢竟一個較短的函數功能測試要比一個長函數容易得多。

  然而,一個更好的劃分文件的依據應該是按模塊劃分。當然,相應的劃分函數的依據應該是按功能劃分。也就是說,一個文件存放一個模塊的內容,一個函數完成單一的功能。

2、文件內容

  在C語言編程時,有兩種文件。一種是源文件(source file,後綴為.c),另一種是頭文件(head file,後綴為.h)。

  C語言的編譯是以c文件為單位的,因此只有h文件時是無法編譯的。根據項目規模大小,一個項目可以由單個c文件構成,也可以有多個c文件和h文件共同構成。

  C語言編譯器在編譯時,通常經歷以下步驟:

  預處理→語法、詞法分析→編譯→匯編→鏈接。

  預處理階段,將根據預處理指令來修改c文件內容。其中,預處理指令包括宏定義(#define)、條件編譯指令(#ifdef、#ifndef、#endif等)、頭文件包含指令(#include)、特殊符號(LINE、FILE等)。對於頭文件包含指令來講,其作用是將所包含h文件中的內容替換到包含指令處,當然如果內容中有其他預處理指令,也會做相應處理。

  因此,h文件在編譯時將插入到c文件中。由此可見,h文件可以出現任何符合c語言語法的內容,但是在實際編程中,我們顯然不會這樣做,因為這樣做就失去了區分c文件和h文件的意義。

  h文件最大的意義是作為對外接口使用,在發布庫文件時作用更是明顯。也就是說,h文件的內容用來提供供其他文件或函數調用的函數原型、變量等內容。下面具體來規定c文件和h文件中應該出現的內容:

源文件(.c)

頭文件(.h)

頭文件包含指令(#include)

頭文件包含指令(#include)

 

宏定義(#define)

所有函數定義(必須有函數體,即{ })

內部函數聲明(static,不能有函數體)

外部函數聲明(extern,不能有函數體)

外部變量定義(必須賦初值)

靜態外部變量定義(static,必須賦初值)

外部變量聲明(extern,不能賦值)

 

 

自定義類型(typedef)

外部數組定義

靜態內部數組定義(static)

外部數組聲明(const)

條件編譯

條件編譯

 

  由上表可以看出,h文件內存放的都是對外可見的變量、函數數組等的聲明,宏定義則是對內對外都可以使用,放在這裡主要為了修改方便。在定義外部變量、數組和函數時,不需要寫extern,因為缺省時默認extern。而聲明外部變量、數組和函數時,必須用extern顯式聲明,這樣是為了讓代碼更直觀。

  函數說明是必須要寫的,寫清函數的入口、出口參數及其功能,以及其它說明,對於代碼維護和改寫能帶來極大的方便。

  通常,如果h文件中全部是對外接口,而對應c文件中各函數均不調用本文件中的其他內容(變量、函數等),也可以不用包含自身的h文件。

  另外,程序編寫時,縮進要規范,要能表達所屬層次關系。每次縮進4個字符,不能隨意縮進。

  關於函數體或組合語句使用{}的格式,常見的有兩種格式:

  int main( ){

  }

  或者:

  int main( )

  {

  }

  本人比較偏向第一種,因為可以節省行數,讓程序緊湊。但是這個問題見仁見智,有人覺得第一種不如第二種對齊方式層次分明。所以這個就讓兩種方式並存吧。因為其他問題不涉及審美習慣,只要規定好大家執行就好了,這個畢竟涉及到每個人的審美不同。

  h文件中必須在開頭和末尾寫條件編譯:

  #ifndef __全大寫文件名_H__   (或者寫成:全大寫文件名_H__)

  #ifndef __全大寫文件名_H__

  …(文件內容)

  #enif

  這樣做是為了防止多次包含,保證在編譯時前面已經替換過該頭文件,後面將不再替換,否則有些內容可能重復定義。

  下面用代碼示例:

<protocol.h>:(每一個h文件中必須有標注的內容)

  #include <msp430x14x.h>        //頭文件包含

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved