中文字幕理论片,69视频免费在线观看,亚洲成人app,国产1级毛片,刘涛最大尺度戏视频,欧美亚洲美女视频,2021韩国美女仙女屋vip视频

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
嵌入式程序設(shè)計(jì)的相關(guān)編寫規(guī)范
原創(chuàng)嵌入式小蕭2021-02-28 16:03:24

1.概述

無(wú)規(guī)則不成方圓,規(guī)范可以使開發(fā)項(xiàng)目階段清晰、要求明確、任務(wù)具體、編寫的代碼規(guī)范,使之規(guī)范化、系統(tǒng)化和工程化,規(guī)范的存在有利于開發(fā)過程的控制和管理,提高所開發(fā)軟件系統(tǒng)的質(zhì)量,縮短開發(fā)時(shí)間,減少開發(fā)和維護(hù)費(fèi)用,以保證項(xiàng)目高質(zhì)量、順利進(jìn)行。

本文的相關(guān)規(guī)范內(nèi)容參考某大公司的相關(guān)規(guī)范,內(nèi)容有助于改善自己在編寫程序時(shí)的一些不好的習(xí)慣,從而使自己編寫的代碼更加的專業(yè)化。

2.代碼管理

開發(fā)代碼做好備份(可以在完成一個(gè)重大功能之后,或者按時(shí)間周期性進(jìn)行備份),以免由于不可抗力導(dǎo)致代碼不可修復(fù)。

在每次重大修改之后要做好記錄(改動(dòng)的具體細(xì)節(jié)),修改前的版本要及時(shí)備份,可以方便隨時(shí)還原系統(tǒng)。

3.設(shè)計(jì)規(guī)范

3.1 文件結(jié)構(gòu)

每個(gè)C /C程序通常分為兩個(gè)文件。一個(gè)文件用于保存程序的聲明(declaration),稱為頭文件。另一個(gè)文件用于保存程序的實(shí)現(xiàn)(implementation),稱為定義(definition)文件。

C /C程序的頭文件以'.h'為后綴,C程序的定義文件以'.c'為后綴,C 程序的定義文件通常以'.cpp'為后綴(也有一些系統(tǒng)以'.cc'或'.cxx'為后綴)。

3.1.1 文件信息聲明

文件信息聲明位于頭文件和定義文件的開頭(參見示例3-1),主要內(nèi)容有:

(1) 版權(quán)信息;

(2) 文件名稱,項(xiàng)目代碼,摘要,參考文獻(xiàn);

(3) 當(dāng)前版本號(hào),作者/修改者,完成日期;

(4) 版本歷史信息;

(5) 主要函數(shù)描述。

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Copyright (c) 2015, DeLianSoftCompany YanTai// All rights reserved.//// Filename :filename.h// Project Code :The project code about this file// Abstract :Describe the content of this file summarily// Reference :......//// Version :1.1// Author :the name of author(mender)// Accomplished date : September 2, 2004//// Replaced version : 1.0// Original Author : the name of original author(mender)// Accomplished date : September 10, 2003//// Main functions ://Function 1 Return code Function name(Parameter Explain)//Function 2 Return code Function name(Parameter Explain)//...//Function n Return code Function name(Parameter Explain)////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

【規(guī)則3.1-1】文件信息聲明以兩行斜杠開始,以兩行斜杠結(jié)束,每一行都以兩個(gè)斜杠開始;

【規(guī)則3.1-2】文件信息聲明包含五個(gè)部分,各部分之間以一空行間隔;

【規(guī)則3.1-3】在主要函數(shù)部分描述了文件所包含的主要函數(shù)的聲明信息,如果是頭文件,這一部分是可以省略的。

3.1.2 頭文件的結(jié)構(gòu)

頭文件由三部分內(nèi)容組成:

(1) 頭文件開頭處的文件信息聲明(參見示例3-1);

(2) 預(yù)處理塊;

(3) 函數(shù)和類結(jié)構(gòu)聲明等。

假設(shè)頭文件名稱為 filesystem.h,頭文件的結(jié)構(gòu)參見示例3-2。

【規(guī)則3.2-1】為了防止頭文件被重復(fù)引用,應(yīng)當(dāng)用ifndef/define/endif結(jié)構(gòu)產(chǎn)生預(yù)處理塊;'#ifndef'或者'#define'后以TAB鍵代替SPACE鍵做空格;如果頭文件名稱是由多個(gè)單詞組成,則各單詞間以下劃線'_'連接,例如有頭文件名稱為'filesystem.h',則定義如下:'#ifndef_FILE_SYSTEM_H_';

【規(guī)則3.2-2】用 #include< filename.h> 格式來(lái)引用標(biāo)準(zhǔn)庫(kù)的頭文件(編譯器將從標(biāo)準(zhǔn)庫(kù)目錄開始搜索);

【規(guī)則3.2-3】用 #include 'filename.h' 格式來(lái)引用非標(biāo)準(zhǔn)庫(kù)的頭文件(編譯器將從用戶的工作目錄開始搜索);

【建議3.2-1】頭文件中只存放'聲明'而不存放'定義';

【建議3.2-1】頭文件中應(yīng)包含所有定義文件所定義的函數(shù)聲明,如果一個(gè)頭文件對(duì)應(yīng)多個(gè)定義文件,則不同定義文件內(nèi)實(shí)現(xiàn)的函數(shù)要分開聲明,并作注釋以解釋所聲明的函數(shù)從屬于那一個(gè)定義文件;

【建議3.2-3】宏定義和函數(shù)聲明分離,在兩個(gè)頭文件中定義,如果沒有類成員函數(shù),可以將類和結(jié)構(gòu)的定義與函數(shù)聲明分離,也就是說(shuō)一個(gè)頭文件專用于宏定義,一個(gè)頭文件專用于類和結(jié)構(gòu)的定義,一個(gè)頭文件專用于函數(shù)聲明;

【建議3.2-4】在C 語(yǔ)法中,類的成員函數(shù)可以在聲明的同時(shí)被定義,并且自動(dòng)成為內(nèi)聯(lián)函數(shù)。這雖然會(huì)帶來(lái)書寫上的方便,但卻造成了風(fēng)格不一致,弊大于利。建議將成員函數(shù)的定義與聲明分開,不論該函數(shù)體有多么小。

頭文件的結(jié)構(gòu)如下:

//文件信息聲明見示例3-1,此處省略。#ifndef_FILE_SYSTEM_H_//avoid referencing the file filesystem.h repeat#define_FILE_SYSTEM_H_#include <math.h>//reference standard head file…#include 'myheader.h' //reference non-standard head file…void Function1(…);//global function declare…class CBox //class structure decalre{…};#endif

3.1.3 定義文件的結(jié)構(gòu)

定義文件有三部分內(nèi)容:

(1) 定義文件開頭處的文件信息聲明(參見示例3-1);

(2) 對(duì)一些頭文件的引用;

(3) 程序的實(shí)現(xiàn)體(包括數(shù)據(jù)和代碼)。

假設(shè)定義文件的名稱為 filesystem.c,定義文件的結(jié)構(gòu)參見示例3-3。

//文件信息聲明見示例3-1,此處省略。#include 'filesystem.h'//reference a head file…//global function realizationvoid Function1(…){…}//class member function realizationvoid CBox::Draw(…){…}

示例3-3 C /C定義文件的結(jié)構(gòu)

3.1.4 頭文件的作用

早期的編程語(yǔ)言如Basic、Fortran沒有頭文件的概念,C /C語(yǔ)言的初學(xué)者雖然會(huì)用使用頭文件,但常常不明其理。這里對(duì)頭文件的作用略作解釋:

(1) 通過頭文件來(lái)調(diào)用庫(kù)功能。在很多場(chǎng)合,源代碼不便(或不準(zhǔn))向用戶公布,只要向用戶提供頭文件和二進(jìn)制的庫(kù)即可。用戶只需要按照頭文件中的接口聲明來(lái)調(diào)用庫(kù)功能,而不必關(guān)心接口怎么實(shí)現(xiàn)的。編譯器會(huì)從庫(kù)中提取相應(yīng)的代碼;

(2) 頭文件能加強(qiáng)類型安全檢查。如果某個(gè)接口被實(shí)現(xiàn)或被使用時(shí),其方式與頭文件中的聲明不一致,編譯器就會(huì)指出錯(cuò)誤,這一簡(jiǎn)單的規(guī)則能大大減輕程序員調(diào)試、改錯(cuò)的負(fù)擔(dān)。

3.1.5 目錄結(jié)構(gòu)

如果一個(gè)軟件的頭文件數(shù)目比較多(如超過十個(gè)),通常應(yīng)將頭文件和定義文件分別保存于不同的目錄,以便于維護(hù)。

例如可將頭文件保存于include目錄,將定義文件保存于source目錄(可以是多級(jí)目錄)。

如果某些頭文件是私有的,它不會(huì)被用戶的程序直接引用,則沒有必要公開其'聲明'。為了加強(qiáng)信息隱藏,這些私有的頭文件可以和定義文件存放于同一個(gè)目錄。

3.2 命名規(guī)則

比較著名的命名規(guī)則當(dāng)推'匈牙利' 命名法,該命名規(guī)則的主要思想是'在變量和函數(shù)名中加入前綴以增進(jìn)人們對(duì)程序的理解'。例如所有的字符變量均以ch為前綴,若是指針變量則追加前綴p。如果一個(gè)變量由ppch開頭,則表明它是指向字符指針的指針。

'匈牙利'法最大的缺點(diǎn)是繁瑣,例如

int i, j, k;

float x, y, z;

倘若采用'匈牙利'命名規(guī)則,則應(yīng)當(dāng)寫成

int iI, iJ, ik; // 前綴 i表示int類型

float fX, fY, fZ; // 前綴 f表示float類型

如此繁瑣的程序會(huì)讓絕大多數(shù)程序員無(wú)法忍受。

總的說(shuō)來(lái),沒有一種命名規(guī)則可以讓所有的程序員贊同,且命名規(guī)則對(duì)軟件產(chǎn)品而言并不是'成敗有關(guān)'的事,而且在不同的平臺(tái)和不同的環(huán)境下編寫的程序所應(yīng)遵循的規(guī)則也不盡相同,所以我們只是追求制定一種令大多數(shù)項(xiàng)目成員滿意的命名規(guī)則,并在項(xiàng)目中貫徹實(shí)施。

3.2.1 共性原則

本節(jié)論述的共性規(guī)則是被大多數(shù)程序員采納的,我們應(yīng)當(dāng)在遵循這些共性規(guī)則的前提下,再擴(kuò)充特定的規(guī)則,如3.2.2節(jié)

【規(guī)則3.2.1-1】標(biāo)識(shí)符應(yīng)當(dāng)直觀且可以拼讀,可望文知意,不必進(jìn)行'解碼';

【規(guī)則3.2.1-2】標(biāo)識(shí)符的長(zhǎng)度應(yīng)當(dāng)符合'min-length && max-information'原則;

【規(guī)則3.2.1-3】命名規(guī)則盡量與所采用的操作系統(tǒng)或開發(fā)工具的風(fēng)格保持一致;

【規(guī)則3.2.1-4】程序中不要出現(xiàn)僅靠大小寫區(qū)分的相似的標(biāo)識(shí)符。

【規(guī)則3.2.1-5】程序中不要出現(xiàn)標(biāo)識(shí)符完全相同的局部變量和全局變量,盡管兩者的作用域不同而不會(huì)發(fā)生語(yǔ)法錯(cuò)誤,但會(huì)使人誤解;

【規(guī)則3.2.1-6】變量的名字應(yīng)當(dāng)使用'名詞'或者'形容詞+名詞';

【規(guī)則3.2.1-7】全局函數(shù)的名字應(yīng)當(dāng)使用'動(dòng)詞'或者'動(dòng)詞+名詞'(動(dòng)賓詞組);

【規(guī)則3.2.1-8】用正確的反義詞組命名具有互斥意義的變量或相反動(dòng)作的函數(shù)等;

【建議3.2.1-9】盡量避免名字中出現(xiàn)數(shù)字編號(hào),如Value1,Value2等,除非邏輯上的確需要編號(hào);

注:

3.2.1 標(biāo)識(shí)符最好采用英文單詞或其組合,便于記憶和閱讀,切忌使用漢語(yǔ)拼音來(lái)命名,程序中的英文單詞一般不要太復(fù)雜,用詞應(yīng)當(dāng)準(zhǔn)確,例如不要把CurrentValue寫成NowValue;

3.2.2 標(biāo)示符的長(zhǎng)度應(yīng)當(dāng)以最小的長(zhǎng)度實(shí)現(xiàn)最多信息,一般來(lái)說(shuō),長(zhǎng)名字能更好地表達(dá)含義,但并非長(zhǎng)的變量名就一定要比短的變量名要好,此外單字符的名字也是有用的,常見的如i,j,k,m,n,x,y,z等,它們通常可用作函數(shù)內(nèi)的局部變量;

3.2.3 不同的操作系統(tǒng)的程序設(shè)計(jì)風(fēng)格是不一樣的,例如Windows應(yīng)用程序的標(biāo)識(shí)符通常采用'大小寫'混排的方式,如AddChild,而Unix應(yīng)用程序的標(biāo)識(shí)符通常采用'小寫加下劃線'的方式,如add_child,別把這兩類風(fēng)格混在一起使用;

3.2.2 Windows變量命名規(guī)則

【規(guī)則3.2.2-1變量的命名規(guī)則要求采用'匈牙利法則',即開頭字母用變量的類型,其余部分用變量的英文意思或其英文意思的縮寫,盡量避免采用中文拼音,要求單詞的第一個(gè)字母大寫;

即:變量名=變量類型+變量英文意思(或縮寫)

變量類型請(qǐng)參見附表1-變量類型表;

【規(guī)則3.2.2-2類名和函數(shù)名用大寫字母開頭的單詞組合而成;對(duì)struct、union、class變量的命名要求定義的類型用大寫,結(jié)構(gòu)采用S開頭,聯(lián)合體采用U開頭,類采用C開頭;

例如:

struct SPoint{intm_nX;intm_nY;};union URecordLen{BYTEm_byRecordNum;BYTEm_byRecordLen;}class CNode{//類成員變量或成員函數(shù)};

【規(guī)則3.2.2-3】指針變量命名的基本原則為:

一重指針變量的基本原則為:

變量名= 'p'+變量類型前綴+命名

對(duì)多重指針變量的基本原則為:

二重指針:

變量名='pp'+變量類型前綴+命名

三重指針:

變量名='ppp'+變量類型前綴+命名

......

例如一個(gè)short*型的變量應(yīng)該表示為pnStart;

【規(guī)則3.2.2-4】全局變量用g_開頭;例如一個(gè)全局的長(zhǎng)型變量定義為g_lFileNum,

即:變量名=g_+變量類型+變量的英文意思(或縮寫);

【規(guī)則3.2.2-5】靜態(tài)變量采用s_開頭;例如一個(gè)靜態(tài)的指針變量定義為s_plPrevInst,

即:變量名=s_+變量類型+變量的英文意思(或縮寫);

【規(guī)則3.2.2-6】類成員變量采用m_開頭;例如一個(gè)長(zhǎng)型成員變量定義為m_lCount,

即:變量名=m_+變量類型+變量的英文意思(或縮寫);

【規(guī)則3.2.2-7】對(duì)const的變量要求在變量的命名規(guī)則前加入c_(若作為函數(shù)的輸入?yún)?shù),可以不加),

即:變量名=c_+變量命名規(guī)則,例如:

const char* c_szFileName;

【規(guī)則3.2.2-8】對(duì)枚舉類型(enum)中的變量,要求用枚舉變量或其縮寫做前綴,且用下劃線隔離變量名,所有枚舉類型都要用大寫,例如:

enum EMDAYS{EMDAYS_MONDAY;EMDAYS_TUESDAY;......};

【規(guī)則3.2.2-9】對(duì)常量(包括錯(cuò)誤的編碼)命名,要求常量名用大寫,常量名用英文意思表示其意思,用下劃線分割單詞,例如:

#defineCM_7816_OK0x9000;

【規(guī)則3.2.2-10】為了防止某一軟件庫(kù)中的一些標(biāo)識(shí)符和其它軟件庫(kù)中的沖突,可以為各種標(biāo)識(shí)符加上能反映軟件性質(zhì)的前綴。例如三維圖形標(biāo)準(zhǔn)OpenGL的所有庫(kù)函數(shù)均以gl開頭,所有常量(或宏定義)均以GL開頭。

3.3 程序風(fēng)格

程序風(fēng)格雖然不會(huì)影響程序的功能,但會(huì)影響程序的可讀性,追求清晰、美觀,是程序風(fēng)格的重要構(gòu)成因素。

3.3.1 空行

空行起著分隔程序段落的作用??招械皿w(不過多也不過少)將使程序的布局更加清晰??招胁粫?huì)浪費(fèi)內(nèi)存,雖然打印含有空行的程序是會(huì)多消耗一些紙張,但是值得。

【規(guī)則3.3.1-1】在每個(gè)類聲明之后、每個(gè)函數(shù)定義結(jié)束之后都要加空行。參見示例3.3.1(a);

【規(guī)則3.3.1-2】在一個(gè)函數(shù)體內(nèi),邏輯上密切相關(guān)的語(yǔ)句之間不加空行,其它地方應(yīng)加空行分隔。

3.3.2 代碼行

【規(guī)則3.3.2-1】一行代碼只做一件事情,如只定義一個(gè)變量,或只寫一條語(yǔ)句,這樣的代碼容易閱讀,并且方便于寫注釋;

【規(guī)則3.3.2-2】if、for、while、do等語(yǔ)句自占一行,執(zhí)行語(yǔ)句不得緊跟其后,不論執(zhí)行語(yǔ)句有多少都要加{},這樣可以防止書寫失誤;

【規(guī)則3.3.2-3】if、for、while、do等語(yǔ)句的'{'要單獨(dú)占用一行;

【建議3.3.2-1】所有函數(shù)內(nèi)的變量都在函數(shù)開始處定義;

【建議3.3.2-2】盡可能在定義變量的同時(shí)初始化該變量(就近原則),如果變量的引用處和其定義處相隔比較遠(yuǎn),變量的初始化很容易被忘記。如果引用了未被初始化的變量,可能會(huì)導(dǎo)致程序錯(cuò)誤,本建議可以減少隱患。

3.3.3 代碼行內(nèi)的空格

【規(guī)則3.3.3-1】關(guān)鍵字之后要留空格,象const、virtual、inline、case 等關(guān)鍵字之后至少要留一個(gè)空格,否則無(wú)法辨析關(guān)鍵字,象if、for、while等關(guān)鍵字之后應(yīng)留一個(gè)空格再跟左括號(hào)'(',以突出關(guān)鍵字;

【規(guī)則3.3.3-2】函數(shù)名之后不要留空格,緊跟左括號(hào)'(',以與關(guān)鍵字區(qū)別;

【規(guī)則3.3.3-3】'('向后緊跟,')'、','、';'向前緊跟,緊跟處不留空格;

【規(guī)則3.3.3-4】','之后要留空格,如Function(x, y, z),如果';'不是一行的結(jié)束符號(hào),其后要留空格,如for (initialization; condition; update);

【規(guī)則3.3.3-5】賦值操作符、比較操作符、算術(shù)操作符、邏輯操作符、位域操作符,如'='、' =' '>='、'<='、' '、'*'、'%'、'&&'、'||'、'<<','^'等二元操作符的前后應(yīng)當(dāng)加空格;

【規(guī)則3.3.3-6】一元操作符如'!'、'~'、' '、'--'、'&'(地址運(yùn)算符)等前后不加空格;

【規(guī)則3.3.3-7】象'[]'、'.'、'->'這類操作符前后不加空格;

【建議3.3.3-1】對(duì)于表達(dá)式比較長(zhǎng)的for語(yǔ)句和if語(yǔ)句,為了緊湊起見可以適當(dāng)?shù)厝サ粢恍┛崭?,如for (i=0; i<10; i )和if ((a<=b) && (c<=d))

3.3.4 對(duì)齊

【規(guī)則3.3.4-1】程序的分界符'{'和'}'應(yīng)獨(dú)占一行并且位于同一列,同時(shí)與引用它們的語(yǔ)句左對(duì)齊;

【規(guī)則3.3.4-2】{ }之內(nèi)的代碼塊在'{'右邊數(shù)格處左對(duì)齊;

【規(guī)則3.3.4.3】代碼的的對(duì)齊采用TAB鍵而不采用空格鍵對(duì)齊,一般TAB鍵設(shè)置為向后空4個(gè)空格。

3.3.5 長(zhǎng)行拆分

【規(guī)則3.3.5-1】代碼行最大長(zhǎng)度宜控制在70至80個(gè)字符以內(nèi);

【規(guī)則3.3.5-2】長(zhǎng)表達(dá)式要在低優(yōu)先級(jí)操作符處拆分成新行,操作符放在新行之首(以便突出操作符),拆分出的新行要進(jìn)行適當(dāng)?shù)目s進(jìn),使排版整齊,語(yǔ)句可讀。

3.3.6 修飾符的位置

修飾符 * 和 & 應(yīng)該靠近數(shù)據(jù)類型還是該靠近變量名,是個(gè)有爭(zhēng)議的話題,若將修飾符 * 靠近數(shù)據(jù)類型,例如:int* x; 從語(yǔ)義上講此寫法比較直觀,即x是int 類型的指針,上述寫法的弊端是容易引起誤解,例如:int* x, y; 此處y容易被誤解為指針變量。雖然將x和y分行定義可以避免誤解,但并不是人人都愿意這樣做。

【規(guī)則3.3.6-1】應(yīng)當(dāng)將修飾符 * 和 & 緊靠變量名;

3.3.7 注釋

C語(yǔ)言的注釋符為'/*…*/'。C 語(yǔ)言中,程序塊的注釋常采用'/*…*/',行注釋一般采用'//…'。注釋通常用于:

(1)版本、版權(quán)聲明;

(2)函數(shù)接口說(shuō)明;

(3)重要的代碼行或段落提示。

雖然注釋有助于理解代碼,但注意不可過多地使用注釋。參見示例3.3.7。

【規(guī)則3.3.7-1】注釋是對(duì)代碼的'提示',而不是文檔,程序中的注釋不可喧賓奪主,注釋太多了會(huì)讓人眼花繚亂,注釋的花樣要少;

【規(guī)則3.3.7-2】如果代碼本來(lái)就是清楚的,則不必加注釋;例如

i ; // i 加 1,多余的注釋

【規(guī)則3.3.7-3】邊寫代碼邊注釋,修改代碼同時(shí)修改相應(yīng)的注釋,以保證注釋與代碼的一致性,不再有用的注釋要?jiǎng)h除;

【規(guī)則3.3.7-4】注釋應(yīng)當(dāng)準(zhǔn)確、易懂,防止注釋有二義性,錯(cuò)誤的注釋不但無(wú)益反而有害;

【規(guī)則3.3.7-5】盡量避免在注釋中使用縮寫,特別是不常用縮寫;

【規(guī)則3.3.7-6】注釋的位置應(yīng)與被描述的代碼相鄰,可以放在代碼的上方或右方,不可放在下方;

【規(guī)則3.3.7-8】當(dāng)代碼比較長(zhǎng),特別是有多重嵌套時(shí),應(yīng)當(dāng)在一些段落的結(jié)束處加注釋,便于閱讀;

【建議3.3.7-9】對(duì)于多行代碼的注釋,盡量不采用'/*...*/',而采用多行'//'注釋,這樣雖然麻煩,但是在做屏蔽調(diào)試時(shí)不用查找配對(duì)的'/*...*/'。

3.7.1 文件頭的注釋

文件頭的注釋請(qǐng)參見3.1,文件頭的注釋是以兩行斜杠開始,以兩行斜杠結(jié)束(以區(qū)別于函數(shù)的注釋)。

3.7.2 函數(shù)頭的注釋

一般說(shuō)來(lái)每個(gè)函數(shù)都應(yīng)該做詳細(xì)的注釋,函數(shù)頭的注釋是以一行斜杠開始,以一行斜杠結(jié)束,注釋的內(nèi)容包括'功能','參數(shù)','返回值','設(shè)計(jì)思想','調(diào)用函數(shù)','日期','修改記錄'等幾個(gè)方面,函數(shù)頭的注釋格式如下:

//////////////////////////////////////////////////////////////////////////////////////////////// Function capacity :Describe the function capacity// Parameter declare :// parameter 1: Describe the function of parameter ( input/output parameter )// parameter 2: Describe the function of parameter ( input/output parameter )//......// Return value : Describe the possible return value// Designed idea : Describe designed idea about the function// Author :// Creation date : Creation date(YY-MM-DD)// Transferred function:List the sub-function in the function// Modification record:// (一)Mender 1:Modified date:modified content/////////////////////////////////////////////////////////////////////////////////////////////

3.4 函數(shù)設(shè)計(jì)

函數(shù)是C /C程序的基本功能單元,其重要性不言而喻。函數(shù)設(shè)計(jì)的細(xì)微缺點(diǎn)很容易導(dǎo)致該函數(shù)被錯(cuò)用,所以光使函數(shù)的功能正確是不夠的。本章重點(diǎn)論述函數(shù)的接口設(shè)計(jì)和內(nèi)部實(shí)現(xiàn)的一些規(guī)則。

函數(shù)接口的兩個(gè)要素是參數(shù)和返回值。C語(yǔ)言中,函數(shù)的參數(shù)和返回值的傳遞方式有兩種:值傳遞(pass by value)和指針傳遞(pass by pointer)。C 語(yǔ)言中多了引用傳遞(pass by reference)。由于引用傳遞的性質(zhì)象指針傳遞,而使用方式卻向值傳遞,初學(xué)者常常迷惑不解,容易引起混亂,請(qǐng)先閱讀3.4.6節(jié)'引用與指針的比較'。

3.4.1 參數(shù)的規(guī)則

【規(guī)則3.4.1-1】參數(shù)的書寫要完整,不要貪圖省事只寫參數(shù)的類型而省略參數(shù)名字,如果函數(shù)沒有參數(shù),則用void填充;例如:

void SetValue(int nWidth, int nHeight);// 良好的風(fēng)格void SetValue(int, int);// 不良的風(fēng)格float GetValue(void);// 良好的風(fēng)格float GetValue();// 不良的風(fēng)格

【規(guī)則3.4.1-2】參數(shù)命名要恰當(dāng),順序要合理;

例如編寫字符串拷貝函數(shù)StringCopy,它有兩個(gè)參數(shù),如果把參數(shù)名字起為str1和str2,例如:

void StringCopy(char *str1, char *str2);

那么我們很難搞清楚究竟是把str1拷貝到str2中,還是剛好倒過來(lái),可以把參數(shù)名字起得更有意義,如叫strSource和strDestination。這樣從名字上就可以看出應(yīng)該把strSource拷貝到strDestination。還有一個(gè)問題,這兩個(gè)參數(shù)那一個(gè)該在前那一個(gè)該在后?參數(shù)的順序要遵循程序員的習(xí)慣。一般地,應(yīng)將目的參數(shù)放在前面,源參數(shù)放在后面。如果將函數(shù)聲明為:

void StringCopy(char *strSource, char *strDestination);

別人在使用時(shí)可能會(huì)不假思索地寫成如下形式:

char str[20];

StringCopy(str, 'Hello World');// 參數(shù)順序顛倒

【規(guī)則3.4.1-3】如果參數(shù)是指針,且僅作輸入用,則應(yīng)在類型前加const,以防止該指針在函數(shù)體內(nèi)被意外修改。例如:

void StringCopy(char *strDestination,const char *strSource);

【規(guī)則3.4.1-4】如果輸入?yún)?shù)以值傳遞的方式傳遞對(duì)象,則宜改用'const &'方式來(lái)傳遞,這樣可以省去臨時(shí)對(duì)象的構(gòu)造和析構(gòu)過程,從而提高效率;

【建議3.4.1-1】避免函數(shù)有太多的參數(shù),參數(shù)個(gè)數(shù)盡量控制在5個(gè)以內(nèi)。如果參數(shù)太多,在使用時(shí)容易將參數(shù)類型或順序搞錯(cuò);

【建議3.4.1-2】盡量不要使用類型和數(shù)目不確定的參數(shù);

C標(biāo)準(zhǔn)庫(kù)函數(shù)printf是采用不確定參數(shù)的典型代表,其原型為:

int printf(const chat *format[, argument]…);

這種風(fēng)格的函數(shù)在編譯時(shí)喪失了嚴(yán)格的類型安全檢查。

3.4.2 返回值的規(guī)則

【規(guī)則3.4.2-1】不要省略返回值的類型;

C語(yǔ)言中,凡不加類型說(shuō)明的函數(shù),一律自動(dòng)按整型處理,這樣做不會(huì)有什么好處,卻容易被誤解為void類型;

C 語(yǔ)言有很嚴(yán)格的類型安全檢查,不允許上述情況發(fā)生。由于C 程序可以調(diào)用C函數(shù),為了避免混亂,規(guī)定任何C / C函數(shù)都必須有類型。如果函數(shù)沒有返回值,那么應(yīng)聲明為void類型

【規(guī)則3.4.2-2】函數(shù)名字與返回值類型在語(yǔ)義上不可沖突;

違反這條規(guī)則的典型代表是C標(biāo)準(zhǔn)庫(kù)函數(shù)getchar。

例如:

char c;c = getchar();if (c == EOF)…

按照getchar名字的意思,將變量c聲明為char類型是很自然的事情。但不幸的是getchar的確不是char類型,而是int類型,其原型如下:

int getchar(void);

由于c是char類型,取值范圍是[-128,127],如果宏EOF的值在char的取值范圍之外,那么if語(yǔ)句將總是失敗,這種'危險(xiǎn)'人們一般哪里料得到!導(dǎo)致本例錯(cuò)誤的責(zé)任并不在用戶,是函數(shù)getchar誤導(dǎo)了使用者

【規(guī)則3.4.2-3】不要將正常值和錯(cuò)誤標(biāo)志混在一起返回。正常值用輸出參數(shù)獲得,而錯(cuò)誤標(biāo)志用return語(yǔ)句返回;

【建議3.4.2-1】有時(shí)候函數(shù)原本不需要返回值,但為了增加靈活性如支持鏈?zhǔn)奖磉_(dá),可以附加返回值;

例如字符串拷貝函數(shù)strcpy的原型:

char *strcpy(char *strDest,const char *strSrc);

strcpy函數(shù)將strSrc拷貝至輸出參數(shù)strDest中,同時(shí)函數(shù)的返回值又是strDest。這樣做并非多此一舉,可以獲得如下靈活性:

char str[20];

int nLength = strlen( strcpy(str, 'Hello World') );

【建議3.4.2-2】如果函數(shù)的返回值是一個(gè)對(duì)象,有些場(chǎng)合用'引用傳遞'替換'值傳遞'可以提高效率。而有些場(chǎng)合只能用'值傳遞'而不能用'引用傳遞',否則會(huì)出錯(cuò);

對(duì)于建議3.4.2-2,如果函數(shù)的返回值是一個(gè)對(duì)象,有些場(chǎng)合用'引用傳遞'替換'值傳遞'可以提高效率,而有些場(chǎng)合只能用'值傳遞'而不能用'引用傳遞',否則會(huì)出錯(cuò),例如:

class String{…// 賦值函數(shù)String& operate=(const String &other);// 相加函數(shù),如果沒有friend修飾則只許有一個(gè)右側(cè)參數(shù)friendString operate ( const String &s1, const String &s2);private:char *m_data;};String的賦值函數(shù)operate = 的實(shí)現(xiàn)如下:String & String::operate=(const String &other){if (this == &other)return *this;delete m_data;m_data = new char[strlen(other.data) 1];strcpy(m_data, other.data);return *this;// 返回的是 *this的引用,無(wú)需拷貝過程}

對(duì)于賦值函數(shù),應(yīng)當(dāng)用'引用傳遞'的方式返回String對(duì)象。如果用'值傳遞'的方式,雖然功能仍然正確,但由于return語(yǔ)句要把 *this拷貝到保存返回值的外部存儲(chǔ)單元之中,增加了不必要的開銷,降低了賦值函數(shù)的效率。例如:

String a,b,c;…a = b; // 如果用'值傳遞',將產(chǎn)生一次 *this 拷貝a = b = c; // 如果用'值傳遞',將產(chǎn)生兩次 *this 拷貝 String的相加函數(shù)operate 的實(shí)現(xiàn)如下:String operate (const String &s1, const String &s2){String temp;delete temp.data;// temp.data是僅含'\0'的字符串temp.data = new char[strlen(s1.data) strlen(s2.data) 1];strcpy(temp.data, s1.data);strcat(temp.data, s2.data);return temp;}

對(duì)于相加函數(shù),應(yīng)當(dāng)用'值傳遞'的方式返回String對(duì)象。如果改用'引用傳遞',那么函數(shù)返回值是一個(gè)指向局部對(duì)象temp的'引用'。由于temp在函數(shù)結(jié)束時(shí)被自動(dòng)銷毀,將導(dǎo)致返回的'引用'無(wú)效。例如:

c = a b;

此時(shí) a b 并不返回期望值,c什么也得不到,留下了隱患。

3.4.3 函數(shù)內(nèi)部實(shí)現(xiàn)的規(guī)則

不同功能的函數(shù)其內(nèi)部實(shí)現(xiàn)各不相同,看起來(lái)似乎無(wú)法就'內(nèi)部實(shí)現(xiàn)'達(dá)成一致的觀點(diǎn)。但根據(jù)經(jīng)驗(yàn),我們可以在函數(shù)體的'入口處'和'出口處'從嚴(yán)把關(guān),從而提高函數(shù)的質(zhì)量。

【規(guī)則3.4.3-1】在函數(shù)體的'入口處',對(duì)參數(shù)的有效性進(jìn)行檢查;

很多程序錯(cuò)誤是由非法參數(shù)引起的,我們應(yīng)該充分理解并正確使用'斷言'(assert)來(lái)防止此類錯(cuò)誤。詳見4.5節(jié)'使用斷言'

【規(guī)則3.4.3-2】在函數(shù)體的'出口處',對(duì)return語(yǔ)句的正確性和效率進(jìn)行檢查;

注意事項(xiàng)如下:

(1) return語(yǔ)句不可返回指向'棧內(nèi)存'的'指針'或者'引用',因?yàn)樵搩?nèi)存在函數(shù)體結(jié)束時(shí)被自動(dòng)銷毀,例如:

char * Func(void){char str[] = 'hello world';// str的內(nèi)存位于棧上…return str;// 將導(dǎo)致錯(cuò)誤}

(2) 要搞清楚返回的究竟是'值'、'指針'還是'引用';

(3) 如果函數(shù)返回值是一個(gè)對(duì)象,要考慮return語(yǔ)句的效率,例如:

return String(s1 s2);

這是臨時(shí)對(duì)象的語(yǔ)法,表示'創(chuàng)建一個(gè)臨時(shí)對(duì)象并返回它',不要以為它與'先創(chuàng)建一個(gè)局部對(duì)象temp并返回它的結(jié)果'是等價(jià)的,如

String temp(s1 s2);

return temp;

實(shí)質(zhì)不然,上述代碼將發(fā)生三件事。

首先,temp對(duì)象被創(chuàng)建,同時(shí)完成初始化;

然后拷貝構(gòu)造函數(shù)把temp拷貝到保存返回值的外部存儲(chǔ)單元中;

最后,temp在函數(shù)結(jié)束時(shí)被銷毀(調(diào)用析構(gòu)函數(shù))。

然而'創(chuàng)建一個(gè)臨時(shí)對(duì)象并返回它'的過程是不同的,編譯器直接把臨時(shí)對(duì)象創(chuàng)建并初始化在外部存儲(chǔ)單元中,省去了拷貝和析構(gòu)的化費(fèi),提高了效率。

類似地,我們不要將

return int(x y);// 創(chuàng)建一個(gè)臨時(shí)變量并返回它

寫成

int temp = x y;

return temp;

由于內(nèi)部數(shù)據(jù)類型如int,float,double的變量不存在構(gòu)造函數(shù)與析構(gòu)函數(shù),雖然該'臨時(shí)變量的語(yǔ)法'不會(huì)提高多少效率,但是程序更加簡(jiǎn)潔易讀。

3.4.4 其它建議

【建議3.4.4-1】函數(shù)的功能要單一,不要設(shè)計(jì)多用途的函數(shù);

【建議3.4.4-2】函數(shù)體的規(guī)模要小,盡量控制在150行代碼之內(nèi);

【建議3.4.4-3】盡量避免函數(shù)帶有'記憶'功能。相同的輸入應(yīng)當(dāng)產(chǎn)生相同的輸出帶有'記憶'功能的函數(shù),其行為可能是不可預(yù)測(cè)的,因?yàn)樗男袨榭赡苋Q于某種'記憶狀態(tài)'。這樣的函數(shù)既不易理解又不利于測(cè)試和維護(hù)。在C/C 語(yǔ)言中,函數(shù)的static局部變量是函數(shù)的'記憶'存儲(chǔ)器。建議盡量少用static局部變量,除非必需。

【建議3.4.4-4】不僅要檢查輸入?yún)?shù)的有效性,還要檢查通過其它途徑進(jìn)入函數(shù)體內(nèi)的變量的有效性,例如全局變量、文件句柄等;

【建議3.4.4-5】用于出錯(cuò)處理的返回值一定要清楚,讓使用者不容易忽視或誤解錯(cuò)誤情況。

3.4.5 使用斷言

程序一般分為Debug版本和Release版本,Debug版本用于內(nèi)部調(diào)試,Release版本發(fā)行給用戶使用。

斷言assert是僅在Debug版本起作用的宏,它用于檢查'不應(yīng)該'發(fā)生的情況。示例4.5是一個(gè)內(nèi)存復(fù)制函數(shù)。在運(yùn)行過程中,如果assert的參數(shù)為假,那么程序就會(huì)中止(一般地還會(huì)出現(xiàn)提示對(duì)話,說(shuō)明在什么地方引發(fā)了assert)。

void *memcpy(void *pvTo, const void *pvFrom, size_t size){assert((pvTo != NULL) && (pvFrom != NULL)); // 使用斷言byte *pbTo = (byte *) pvTo;// 防止改變pvTo的地址byte *pbFrom = (byte *) pvFrom;// 防止改變pvFrom的地址while(size -- > 0 )*pbTo = *pbFrom ;return pvTo;}

示例3.4.5 復(fù)制不重疊的內(nèi)存塊

assert不是一個(gè)倉(cāng)促拼湊起來(lái)的宏。為了不在程序的Debug版本和Release版本引起差別,assert不應(yīng)該產(chǎn)生任何副作用。所以assert不是函數(shù),而是宏。程序員可以把a(bǔ)ssert看成一個(gè)在任何系統(tǒng)狀態(tài)下都可以安全使用的無(wú)害測(cè)試手段。如果程序在assert處終止了,并不是說(shuō)含有該assert的函數(shù)有錯(cuò)誤,而是調(diào)用者出了差錯(cuò),assert可以幫助我們找到發(fā)生錯(cuò)誤的原因。

【規(guī)則3.4.5-1】使用斷言捕捉不應(yīng)該發(fā)生的非法情況,不要混淆非法情況與錯(cuò)誤情況之間的區(qū)別,后者是必然存在的并且是一定要作出處理的;

【規(guī)則3.4.5-2】在函數(shù)的入口處,使用斷言檢查參數(shù)的有效性(合法性);

【建議3.4.5-1】在編寫函數(shù)時(shí),要進(jìn)行反復(fù)的考查,并且自問:'我打算做哪些假定?'一旦確定了的假定,就要使用斷言對(duì)假定進(jìn)行檢查;

【建議3.4.5-2】一般教科書都鼓勵(lì)程序員們進(jìn)行防錯(cuò)設(shè)計(jì),但要記住這種編程風(fēng)格可能會(huì)隱瞞錯(cuò)誤。當(dāng)進(jìn)行防錯(cuò)設(shè)計(jì)時(shí),如果'不可能發(fā)生'的事情的確發(fā)生了,則要使用斷言進(jìn)行報(bào)警。

3.4.6 引用與指針的比較

引用是C 中的概念,初學(xué)者容易把引用和指針混淆一起。一下程序中,n是m的一個(gè)引用(reference),m是被引用物(referent)。

int m;

int &n = m;

n相當(dāng)于m的別名(綽號(hào)),對(duì)n的任何操作就是對(duì)m的操作。所以n既不是m的拷貝,也不是指向m的指針,其實(shí)n就是m它自己。

引用的一些規(guī)則如下:

(1) 引用被創(chuàng)建的同時(shí)必須被初始化(指針則可以在任何時(shí)候被初始化);

(2) 不能有NULL引用,引用必須與合法的存儲(chǔ)單元關(guān)聯(lián)(指針則可以是NULL);

(3) 一旦引用被初始化,就不能改變引用的關(guān)系(指針則可以隨時(shí)改變所指的對(duì)象)。

以下示例程序中,k被初始化為i的引用。語(yǔ)句k = j并不能將k修改成為j的引用,只是把k的值改變成為6。由于k是i的引用,所以i的值也變成了6。

int i = 5;

int j = 6;

int &k = i;

k = j;// k和i的值都變成了6;

上面的程序看起來(lái)像在玩文字游戲,沒有體現(xiàn)出引用的價(jià)值。引用的主要功能是傳遞函數(shù)的參數(shù)和返回值。C 語(yǔ)言中,函數(shù)的參數(shù)和返回值的傳遞方式有三種:值傳遞、指針傳遞和引用傳遞。

以下是'值傳遞'的示例程序。由于Func1函數(shù)體內(nèi)的x是外部變量n的一份拷貝,改變x的值不會(huì)影響n, 所以n的值仍然是0。

void Func1(int x){x = x   10;}…int n = 0;Func1(n);cout << 'n = ' << n << endl;// n = 0

以下是'指針傳遞'的示例程序。由于Func2函數(shù)體內(nèi)的x是指向外部變量n的指針,改變?cè)撝羔樀膬?nèi)容將導(dǎo)致n的值改變,所以n的值稱為10。

void Func2(int *x){(* x) = (* x) 10;}…int n = 0;Func2(&n);cout << 'n = ' << n << endl;// n = 10

以下是'引用傳遞'的示例程序。由于Func3函數(shù)體內(nèi)的x是外部變量n的引用,x和n是同一個(gè)東西,改變x等于改變n,所以n的值成為10。

void Func3(int &x){x = x   10;}…int n = 0;Func3(n);cout<< 'n = ' << n<< endl;// n = 10

對(duì)比上述三個(gè)示例程序,會(huì)發(fā)現(xiàn)'引用傳遞'的性質(zhì)象'指針傳遞',而書寫方式象'值傳遞'。

3.5 變量類型定義

4.版本管理

版本管理的必要性

軟件項(xiàng)目完全由一個(gè)人來(lái)完成是難以想象的,通常是有一個(gè)研發(fā)小組來(lái)共同分析、設(shè)計(jì)、編碼和維護(hù)。許多因素都有可能導(dǎo)致對(duì)軟件的修改,小的可能只是對(duì)某個(gè)源文件中的某個(gè)變量的定義改動(dòng),大到重新設(shè)計(jì)程序模塊甚至可能是整個(gè)需求分析變動(dòng)。由于軟件開發(fā)所固有的特征,可能會(huì)形成眾多的軟件版本,而且我們并不能保證不出現(xiàn)錯(cuò)誤的修改。所以必須要進(jìn)行版本管理。

以往的那種被譽(yù)為具有良好編程風(fēng)格的做法,諸如在對(duì)他人的源程序進(jìn)行修改時(shí)注釋修改原因,修改人和日期,如果是多個(gè)成員同時(shí)進(jìn)行了修改,那么需要進(jìn)行及時(shí)的人工的差異比較和綜合以便形成一個(gè)統(tǒng)一的新版本。這種做法在當(dāng)前的大型軟件的開發(fā)中已經(jīng)越來(lái)越?jīng)]有空間了,可以說(shuō)是一種以小作坊的形式來(lái)面對(duì)軟件的社會(huì)化大生產(chǎn),再也不可能行得通了,這樣會(huì)出現(xiàn)版本控制問題。

版本控制出現(xiàn)問題就會(huì)出現(xiàn),代碼管理混亂、解決代碼沖突困難、在代碼整合期間引入深層BUG、無(wú)法對(duì)代碼的擁有者進(jìn)行權(quán)限控制,特別是對(duì)產(chǎn)品的開發(fā),你會(huì)頻繁的進(jìn)行版本發(fā)布,這時(shí)如果沒有一個(gè)有效的管理產(chǎn)品版本的工具,一切將變得非常艱難

1. 怎樣對(duì)研發(fā)項(xiàng)目進(jìn)行整體管理;

2. 項(xiàng)目開發(fā)小組的成員之間如何以一種有效的機(jī)制進(jìn)行協(xié)調(diào);

3. 如何進(jìn)行對(duì)小組成員各自承擔(dān)的子項(xiàng)目的統(tǒng)一管理;

4. 如何對(duì)研發(fā)小組各成員所作的修改進(jìn)行統(tǒng)一匯總;

5. 如何保留修改的軌跡,以便撤銷錯(cuò)誤的改動(dòng);

6. 對(duì)在研發(fā)過程中形成的軟件的各個(gè)版本如何進(jìn)行標(biāo)識(shí),管理及差異識(shí)辨。

我們必須要引進(jìn)一種管理機(jī)制,一個(gè)版本管理機(jī)制,而且是廣義上的版本管理,它不僅需要對(duì)源代碼的版本進(jìn)行管理,而且還要對(duì)整個(gè)項(xiàng)目進(jìn)行管理,必須使用版本控制工具。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
C++編程命名規(guī)則
如何寫好 C main 函數(shù)
嵌入式C語(yǔ)言編程規(guī)范(個(gè)人規(guī)約)
Minus-C
C語(yǔ)言的代碼規(guī)范有哪些?
C/C++語(yǔ)言void及void指針深層探索
更多類似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服