C/C++編程筆記:C語言編程風(fēng)格個人總結(jié),初學(xué)小白可借鑒!
總結(jié)一下我個人的編程風(fēng)格及這樣做的原因吧,其實(shí)是為了給實(shí)驗(yàn)室寫一個統(tǒng)一的C語言編程規(guī)范才寫的。首先聲明,我下面提到的編程規(guī)范,是自己給自己定的,不是c語言里面規(guī)定的。

一件事情,做成和做好中間可能隔了十萬八千里。
同樣的,代碼的質(zhì)量也極大程度上反映了編程者的水平高低。為了讓大家從學(xué)習(xí)的開始就養(yǎng)成良好的編程習(xí)慣,創(chuàng)作出優(yōu)質(zhì)的代碼,實(shí)驗(yàn)室編輯這個文檔,作為大家編程的參考,同時也是對以后編程風(fēng)格的硬性規(guī)定。
對于一個團(tuán)隊(duì)來講,制定統(tǒng)一的編程規(guī)范,好處是顯而易見的。通常一個項(xiàng)目是由多個成員共同完成,在項(xiàng)目中,經(jīng)常互相調(diào)用組內(nèi)成員的代碼。如果兩個人的編程習(xí)慣和風(fēng)格差異顯著,那么將會浪費(fèi)大量時間在讀懂代碼上。相反,一致而良好的編程規(guī)范,會讓合作開發(fā)變得輕松而高效。
眾所周知,C語言是面向過程的語言。也就是說,程序員要對程序的每一步有精準(zhǔn)的把握,知道每一條程序語句的執(zhí)行內(nèi)容及其結(jié)果。因而,代碼的可讀性就顯得尤為重要。這里的可讀,不僅僅是對自己可讀,也要對其他人可讀。一段只有自己能讀懂的代碼,可以說價值很低,而且這樣的代碼隨著時間的推移往往自己也讀不懂。而可讀性強(qiáng)的代碼,不僅方便移植與交流,更給調(diào)試帶來了難以估量的便利。
讀一段好的代碼,會有一種讀英語文章的流暢感。盡管C語言提供了有限的32個關(guān)鍵字,但是變量、函數(shù)等的命名卻提供了較大的自由,這也是我們將代碼語句化的基礎(chǔ)。試想,如果一段代碼有了主謂賓結(jié)構(gòu),即使不懂編程的人,也能明白代碼的功能。而這正是我們代碼編輯者追求的目標(biāo)。
所以,寫好一段代碼,從把你的代碼讀者當(dāng)編程小白開始!

一、文件管理
每一個做技術(shù)的人,無論軟硬件,計(jì)算機(jī)里都應(yīng)該有一個純英文的盤符,注意我是說英文,而不是pinyin。在這個純英文的盤符里,當(dāng)然是存放各種技術(shù)相關(guān)的軟件、程序以及文檔。而這些內(nèi)容的命名也應(yīng)該是英文的,包括各個子文件夾。其他諸如即時通訊軟件、游戲文件等應(yīng)該放在其他盤符內(nèi)。一方面,這樣是對自己英文水平的鍛煉;另一方面,也能避免很多在使用國外軟件的時候出現(xiàn)的各種BUG。
每一個軟件,都應(yīng)該放在一個獨(dú)立的文件夾中。這樣既方便查找,又避免混亂。因?yàn)槲覀兌贾烂恳粋€軟件完成后,都不僅僅是一個exe文件那么簡單,通常還有各種后綴的文件,而這些我們都不能刪除。如果打開D盤時,映入眼簾的是幾萬個由不同軟件安裝時生成的各種文件,相信給誰都會一臉大寫的懵逼。因此,將不同的軟件放在單獨(dú)的文件夾下是非常有必要的。
不同IDE下編寫的程序,也都應(yīng)該存放在獨(dú)立的一個文件夾下。文件夾內(nèi),不同的工程也應(yīng)該分別建立文件夾,并合理而精準(zhǔn)命名。這樣為日后的查找?guī)順O大的便利。
很多IDE在編寫程序文件時,除了要建立Project(工程),還要建立Workspace,即工作空間。工作空間通常是指定一個空間(也就是文件夾),IDE啟動時,自動打開該空間下的各個Project。因此,一個Workspace可以存放多個Project。這樣我們就可以利用Workspace管理自己在該IDE下編寫的各個Project。前提是你建立了Workspace,而Project存放在這個Workspace下。
每一個獨(dú)立的項(xiàng)目都應(yīng)該是一個獨(dú)立的Project。例如,分別練習(xí)編寫流水燈和數(shù)碼管的程序時,要分別建立Project,而不能放在一個Project下,除非你的項(xiàng)目同時用到了流水燈和數(shù)碼管。這樣做的好處是你可以Project名稱上精確獲得其內(nèi)容信息,而不會出現(xiàn)程序?qū)懲赀^一段時間后無從查找的情況。

二、命名規(guī)則
首先說一下總的命名規(guī)則:命名一定要用英文。并不是因?yàn)槠匆舨豢梢裕且驗(yàn)槲覀円c國際接軌,要養(yǎng)成良好的英文書寫習(xí)慣。其次,命名中除了“\/:*?”<>|“等系統(tǒng)不允許的字符外,也不能出現(xiàn)除英文字母、下劃線、數(shù)字外的其他字符。如果你想命名成flash LED.c,中間的空格要用下劃線”_“來代替,寫成flash_LED.c。另外,命名中可以出現(xiàn)必要的數(shù)字。
1、文件/文件夾命名
文件命名要精確,文件名要準(zhǔn)確反映文件內(nèi)容。寫的是
文件命名一律使用小寫字母,如keyboard.c。
如有縮寫單詞,則必須大寫,如flash_LED.c、UART.c。其中LED是Light-Emitting Diode(發(fā)光二極管)的縮寫,UART是Universal Asynchronous Receiver/Transmitter(通用異步收發(fā)器,也就是串口)的縮寫。對于有約定俗成縮寫的單詞,就使用縮寫詞匯。
文件名應(yīng)使用名詞,而不應(yīng)該使用動詞。如果文件內(nèi)容是數(shù)據(jù)采集,應(yīng)該命名為data_collection.c而非data_collect.c。
2、標(biāo)識符命名
???? C語言中,可以定義各種標(biāo)識符作為變量名、數(shù)組名、函數(shù)名、標(biāo)號及用戶定義對象的名稱。ANSI C規(guī)定標(biāo)識符必須由字母和下劃線開始,隨后可以出現(xiàn)字母、下劃線和數(shù)字。
1)變量命名
???? 變量命名一律小寫,縮寫詞匯用大寫,且全部使用名詞,可以使用形容詞修飾,用“_”表從屬關(guān)系。因?yàn)樽兞棵鳛橐粋€變量的名字,就應(yīng)該是一個名詞。
???? 局部循環(huán)體控制變量用i,j,k。如for(i=0;i<100;i++)。
???? 指針變量用“p_”開頭,后面接指向內(nèi)容。如指向高度變量的指針,命名為“*p_height”。請讀者自行區(qū)分指針和指針變量的區(qū)別。
???? 局部變量盡量用一個單詞表達(dá)清其含義。
全局變量命名時首先寫所屬模塊名稱。例如如一個傳感器文件sensor.c里面的一個全局變量要代表溫度,則命名為sensor_temperature。又例如LCD(液晶顯示屏)文件LCD.c中表示LCD狀態(tài)的全局變量命名為LCD_status。因?yàn)槿肿兞客缥募{(diào)用,如不寫清變量定義位置,當(dāng)程序龐大,而IDE又不支持一鍵定位時,查找起來很麻煩。即使IDE支持一鍵定位,一個清楚明白的命名,能讓人瞬間讀懂該變量的含義。
2)數(shù)組命名
???? 數(shù)組命名各單詞首字母大寫,其他同變量。
讀者可能會有疑問,數(shù)組名后面會有[]符號,與變量區(qū)別明顯,為什么要用首字母大寫的方式。實(shí)際上,在數(shù)組名作為實(shí)參傳遞數(shù)組首地址時,往往會省略[]符號,應(yīng)該數(shù)組名就是數(shù)組的首地址。例如:
unsigned char string[]=”abcdefg”;
? printf(“%s”,string);
在以上代碼中,string是一個8位數(shù)組(為什么是8位?),在使用printf()函數(shù)輸出時,只寫了數(shù)組名,顯然這種方式是被允許的。而此時就沒有寫[],在這種情況下,并不能瞬間知道string是變量還是數(shù)組,而需要參考前面的格式控制符“%s”。在其他函數(shù)中,或許沒有“%s”這樣的格式控制符幫助我們判斷string到底是數(shù)組還是變量,我們只有找到函數(shù)的聲明或定義才能知道答案,嚴(yán)重影響閱讀。因此有必要對數(shù)組和變量加以區(qū)分。
3)函數(shù)命名
函數(shù)命名各單詞首字母大寫,寫成主謂語形式,主語用名詞,謂語用動詞,縮寫詞匯用大寫,用“_”表從屬關(guān)系。主語通常為模塊名,而謂語是描述模塊的動作。因?yàn)楹瘮?shù)本身就是用來執(zhí)行一系列的動作的,
結(jié)合函數(shù)參數(shù),可以表達(dá)通順的語句。舉個簡單的例子:延時函數(shù)。定義一個ms級延時函數(shù)為:
void Delay(unsigned int ms);(這個其實(shí)是聲明,函數(shù)體不想寫了)
調(diào)用時寫:
Delay(500);
很顯然是延時了500ms。而如果再用個宏定義:
#define MS500?500
Delay(MS500);
是不是更一目了然呢?
另外還比如串口發(fā)送函數(shù)命名UART_TX( ),調(diào)用時寫成:
UART_TX(time); (通常發(fā)送數(shù)據(jù)Transmit Data簡寫為TXD)
顯然意思是串口發(fā)送時間數(shù)據(jù)。
再比如設(shè)置參考值的函數(shù)命名為REF_Set( ),調(diào)用時寫成:
REF_Set(current_voltage);(通常參考值Reference簡寫為REF)
顯然意思是將當(dāng)前的電壓設(shè)置為參考值。
主謂格式的命名大大增加了代碼的可能性。
當(dāng)然,函數(shù)命名中必要時可以出現(xiàn)賓語。這種情況多出現(xiàn)在函數(shù)沒有參數(shù)的情況下。如一個函數(shù)的功能是LCD顯示時間,而時間是全局變量,因此這個函數(shù)就不需要參數(shù),此時直接定義成void LCD_Display_Time(void)(其實(shí)是聲明,因?yàn)闆]寫函數(shù)體)。
命名時首字母大寫不會和數(shù)組混淆嗎?顯然不會,因?yàn)楹瘮?shù)不論是在定義、聲明還是調(diào)用的時候后面都必須跟著”( )”。?
4)標(biāo)號命名
由于在硬件編程中標(biāo)號可以用循環(huán)來代替,所以很少用到。我們規(guī)定標(biāo)號的命名格式基本同變量,使用全部小寫的名詞,但是只用一個單詞表示即可。因?yàn)闃?biāo)號時候的時候或者前面加了goto,或者后面加了“:”,很容易與變量區(qū)分開。況且只是一個定位標(biāo)志,所以一個單詞足夠了。
5)自定義類型命名
自定義類型命名主要指使用typedef定義的新類型名,以及結(jié)構(gòu)體類型、共用體類型的類型名(而非該類型的變量名)。
自定義的新類型名,只用一個單詞,首字母大寫。但是定義這種新類型的變量時,命名規(guī)則與變量命名規(guī)則完全相同。
請自行體會新類型名與新類型變量的區(qū)別。
6)宏定義命名
宏定義命名全部使用大寫字母,單詞數(shù)不限。可以加入數(shù)字和下劃線,但是數(shù)字不能開頭 。
由于宏定義的特殊性,對其使用名詞或動詞不作規(guī)定。因?yàn)楹甓x一個函數(shù)時,應(yīng)該是動詞性質(zhì),而宏定義一個常數(shù)時,應(yīng)該是名詞性質(zhì)。

三、表達(dá)式書寫
表達(dá)式書寫時,最重要的是意義明確。由于C語言不同運(yùn)算符有著不同的結(jié)合順序和優(yōu)先級,因此很容易造成歧義,即實(shí)際運(yùn)算順序與設(shè)想運(yùn)算順序不同。除了完全理解并熟記結(jié)合順序與優(yōu)先級,最簡單的方法就是用括號來明確運(yùn)算順序——在表達(dá)式中,括號的優(yōu)先級是最高的。
另外,運(yùn)算符與其操作數(shù)之間要空格。如:
a=a+b;
應(yīng)寫成:
a ?= ?a ?+ ?b;
這樣做可以讓表達(dá)式顯得不那么擁擠而增加可讀性,但這不是重點(diǎn)。這樣做的重點(diǎn)是幫我們避免很多不易識別的錯誤。如:
a=a/*b;
我們的本意是a除以指針變量b指向的內(nèi)容,然后將商賦給a。然而殘酷的現(xiàn)實(shí)是,編譯器發(fā)現(xiàn)了連起來的“/*”,沒錯,這是注釋符。所以,后面的內(nèi)容都會被注釋掉,直到找到最近的“*/”。
所以我們應(yīng)該寫成:
a? =? a? /? *b;?????? //指針運(yùn)算符*應(yīng)該緊跟指針變量b
或者:
a=a/(*b);??????? //不過即便這樣寫也應(yīng)該加入空格,便于閱讀
有人會說,現(xiàn)在的IDE會用不同的顏色提示注釋內(nèi)容,所以這樣的錯誤應(yīng)該不會出現(xiàn)。但是我想說的是,作為一個立志做合格的工程師的你,會允許自己有不嚴(yán)謹(jǐn)?shù)牧?xí)慣嗎?況且本身我們的文檔是為了在C語言語法、詞法基礎(chǔ)上,制定一個編程規(guī)范。
另外,有些老版本的C編譯器允許用=+來代替+=的含義,即復(fù)合賦值號的兩個符號順序可以是反的。這樣的話,如果寫出:
a=-1;
本意是將-1賦給a,但是編譯器卻會理解成:
a? =? a? -? 1;
顯然意義完全變了。
有人又會說了,你不是說老版本的C編譯器嘛,我不用不就行了嗎。然而,我們要考慮代碼的可移植性,就絕不應(yīng)該允許這樣的想法。
因此,在書寫表達(dá)式的時候,不要吝惜你的空格和括號。
還有一點(diǎn)值得說明的是,復(fù)合賦值運(yùn)算符的兩個運(yùn)算符不能分開。如“+=”不能寫成“+? =”。

四、文件編寫
1、文件劃分
一個簡單的程序,只有幾行到幾十行,放在一個文件內(nèi)一目了然。但是一個較大的項(xiàng)目中可能會有成千上萬行代碼,更有大型程序代碼數(shù)以百萬行計(jì)。這樣規(guī)模的代碼,存放在一個文件內(nèi),其恐怖程度請自行想象。
當(dāng)一個函數(shù)的代碼量超過幾十行時,就應(yīng)該考慮有沒有可能把其中某些代碼提取出來打包成另一個函數(shù)然后調(diào)用。同樣的,當(dāng)一個文件的代碼量超過幾百行時,就應(yīng)該考慮有沒有可能把一些函數(shù)分出來放到別的文件中去。這樣做都是為了程序的可讀性和方便調(diào)試,畢竟一個較短的函數(shù)功能測試要比一個長函數(shù)容易得多。
然而,一個更好的劃分文件的依據(jù)應(yīng)該是按模塊劃分。當(dāng)然,相應(yīng)的劃分函數(shù)的依據(jù)應(yīng)該是按功能劃分。也就是說,一個文件存放一個模塊的內(nèi)容,一個函數(shù)完成單一的功能。
2、文件內(nèi)容
在C語言編程時,有兩種文件。一種是源文件(source file,后綴為.c),另一種是頭文件(head file,后綴為.h)。
C語言的編譯是以c文件為單位的,因此只有h文件時是無法編譯的。根據(jù)項(xiàng)目規(guī)模大小,一個項(xiàng)目可以由單個c文件構(gòu)成,也可以有多個c文件和h文件共同構(gòu)成。
C語言編譯器在編譯時,通常經(jīng)歷以下步驟:
預(yù)處理→語法、詞法分析→編譯→匯編→鏈接。
預(yù)處理階段,將根據(jù)預(yù)處理指令來修改c文件內(nèi)容。其中,預(yù)處理指令包括宏定義(#define)、條件編譯指令(#ifdef、#ifndef、#endif等)、頭文件包含指令(#include)、特殊符號(LINE、FILE等)。對于頭文件包含指令來講,其作用是將所包含h文件中的內(nèi)容替換到包含指令處,當(dāng)然如果內(nèi)容中有其他預(yù)處理指令,也會做相應(yīng)處理。
因此,h文件在編譯時將插入到c文件中。由此可見,h文件可以出現(xiàn)任何符合c語言語法的內(nèi)容,但是在實(shí)際編程中,我們顯然不會這樣做,因?yàn)檫@樣做就失去了區(qū)分c文件和h文件的意義。
h文件最大的意義是作為對外接口使用,在發(fā)布庫文件時作用更是明顯。也就是說,h文件的內(nèi)容用來提供供其他文件或函數(shù)調(diào)用的函數(shù)原型、變量等內(nèi)容。下面具體來規(guī)定c文件和h文件中應(yīng)該出現(xiàn)的內(nèi)容:

由上表可以看出,h文件內(nèi)存放的都是對外可見的變量、函數(shù)數(shù)組等的聲明,宏定義則是對內(nèi)對外都可以使用,放在這里主要為了修改方便。在定義外部變量、數(shù)組和函數(shù)時,不需要寫extern,因?yàn)槿笔r默認(rèn)extern。而聲明外部變量、數(shù)組和函數(shù)時,必須用extern顯式聲明,這樣是為了讓代碼更直觀。
函數(shù)說明是必須要寫的,寫清函數(shù)的入口、出口參數(shù)及其功能,以及其它說明,對于代碼維護(hù)和改寫能帶來極大的方便。
通常,如果h文件中全部是對外接口,而對應(yīng)c文件中各函數(shù)均不調(diào)用本文件中的其他內(nèi)容(變量、函數(shù)等),也可以不用包含自身的h文件。
另外,程序編寫時,縮進(jìn)要規(guī)范,要能表達(dá)所屬層次關(guān)系。每次縮進(jìn)4個字符,不能隨意縮進(jìn)。
關(guān)于函數(shù)體或組合語句使用{}的格式,常見的有兩種格式:
int main( ){
}
或者:
int main( )
{
}
本人比較偏向第一種,因?yàn)榭梢怨?jié)省行數(shù),讓程序緊湊。但是這個問題見仁見智,有人覺得第一種不如第二種對齊方式層次分明。所以這個就讓兩種方式并存吧。因?yàn)槠渌麊栴}不涉及審美習(xí)慣,只要規(guī)定好大家執(zhí)行就好了,這個畢竟涉及到每個人的審美不同。
h文件中必須在開頭和末尾寫條件編譯:
#ifndef __全大寫文件名_H__?? (或者寫成:全大寫文件名_H__)
#ifndef __全大寫文件名_H__
…(文件內(nèi)容)
#enif
這樣做是為了防止多次包含,保證在編譯時前面已經(jīng)替換過該頭文件,后面將不再替換,否則有些內(nèi)容可能重復(fù)定義。
下面用代碼示例:



這兩個文件都是從編者曾經(jīng)寫的代碼中截取出來的,有些部分是為了演示內(nèi)容現(xiàn)在添加進(jìn)去的,源代碼中不存在,請大家不必在意細(xì)節(jié),關(guān)鍵領(lǐng)會兩個文件中應(yīng)該出現(xiàn)的內(nèi)容,均在后面用注釋的方式作了說明。
注意:
本文中出現(xiàn)的很多字符,為了美觀和直觀,中英文輸入法混用,或者加多個空格。大家在編程時,切記使用英文半角輸入法,而且不管你加多少空格或制表符,編譯器都按一個處理。

學(xué)習(xí)C/C++編程知識,提升自身的C/C++編程能力,歡迎關(guān)注UP一起成長!
UP主頁上傳了一些學(xué)習(xí)C/C++編程的視頻教程,正在學(xué)習(xí)或者想要學(xué)習(xí)C/C++編程的小伙伴可以去看一下,希望對你有幫助喲~
歡迎評論、點(diǎn)贊、收藏、投幣、轉(zhuǎn)發(fā)