欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Recommended C Style and Coding Standards中文翻譯版

 更新時間:2014年04月28日 11:45:23   作者:  
本文翻譯自Recommended C Style and Coding Standards(C語言編碼風格和標準),需要的朋友可以參考下


16. 可移植性

    "C語言結合了匯編的強大功能和可移植性" — 無名氏,暗指比爾.薩克。

可移植代碼的好處是有目共睹的。這一節(jié)將闡述一些編寫可移植代碼的指導原則。這里"可移植的"是指一個源碼文件能夠在不同機器上被編譯和執(zhí)行,其 前提僅僅是在不同平臺上可能包含不同的頭文件,使用不同的編譯器開關選項罷了。頭文件包含的#define和typedef可能因機器而異。一般 來說,一個新"機器"是指一種不同的硬件,一種不同的操作系統(tǒng),一個不同的編譯器,或者是這些的任意組合。參考1包含了很多關于風格和可移植 性方面的有用信息。下面是一個隱患列表,當你設計可移植代碼時應該考慮避免這些隱患:

    * 編寫可移植的代碼。只有當被證明是必要的情況下才考慮優(yōu)化的細節(jié)。優(yōu)化后的代碼往往是模糊不清、難以理解的。在一臺機器上經過優(yōu)化后的代碼,在其他機器上 可能變得更加糟糕。將采用的性能優(yōu)化手段記錄下來并盡可能多地本地化。文檔應該解釋這些手段的工作原理以及引入它們的原因(例如:"循環(huán)執(zhí)行了無 數(shù)次")

    * 要意識到很多東西天生就是不可移植的。比如處理類似程序狀態(tài)字這樣的特定硬件寄存器的代碼,以及被設計用于支持某特定硬件部件的代碼,諸如匯編器以及 I/O驅動。即使在這種情況下,許多例程和數(shù)據(jù)仍然可以被設計成機器無關的。

    * 組織源文件時將機器無關與機器相關的代碼分別放在不同文件中。之后如果這個程序需要被移植到一個新機器上時,我們就可以很容易判斷出來哪些需要被改變。為 一些文件的頭文件中機器依賴相關的代碼添加注釋。

    * 任何"實現(xiàn)相關"的行為都應該作為機器(編譯器)依賴對待。假設編譯器或硬件以一種十分古怪的方式實現(xiàn)它。

    * 注意機器字長。對象的大小可能不直觀,指針大小也不總是與整型大小相同,也不總是彼此大小相同,或者可相互自由轉換。下面的表中列舉了C語言基本類型在不 同機器和編譯器下的大小(以bit為單位)。

復制代碼 代碼如下:

type    pdp11  VAX/11 68000  Cray-2 Unisys Harris 80386
        series       family          1100   H800
char     8      8      8       8      9     8      8
short    16    16     8/16   64(32)   18    24    8/16
int      16    32     16/32  64(32)   36    24    16/32
long     32    32      32    64       36    48    32
char*    16    32      32    64       72    24    16/32/48
int*     16    32      32    64(24)   72    24    16/32/48
int(*)() 16    32      32    64       576   24    16/32/48

有些機器針對某一類型可能有不止一個大小。其類型大小取決于編譯器和不同的編譯期標志。下面表展示了大多數(shù)系統(tǒng)的"安全"類型大小。無符號與帶符 號數(shù)具有相同的大小(單位:bit)。

復制代碼 代碼如下:

Type    Minimum  No Smaller
          # Bits   Than
char       8 
short      16    char
int        16    short
long       32    int
float      24 
double     38    float
any *      14 
char *     15    any *
void *     15    any *

    * void類型可以保證有足夠位精度來表示一個指向任意數(shù)據(jù)對象的指針。void()()類型可以保證表示一個指向任意函數(shù)的指針。當你需要通用指針時 可以使用這些類型(在一些舊的編譯器里,分別用char和char()()表示)。確保在使用這些指針類型之前將其轉換回正確的類型。

    * 即使說一個int和一個char類型大小相同,它們仍可能具有不同的格式。例如,下面例子在一些sizeof(int)等于 sizeof(char)的機器上可能失敗。其原因在與free函數(shù)期望一個char,但卻傳入了一個int。

復制代碼 代碼如下:

    int *p = (int *) malloc (sizeof(int));
   free (p);

    * 注意,一個對象的大小不能保證這個對象的精度。Cray-2可能使用64位來存儲一個整型,但一個長整型轉換為一個整型并且再轉換回長整型后可能會被截斷 為32位。

    * 整型常量0可以強制轉型為任何指針類型。轉換后的指針稱為對應那個類型的空指針,并且與那個類型的其他指針不同??罩羔槺容^總是與常量0相當。空指針不應 該與一個值為0的變量比較。空指針不總是使用全0的位模式表示。兩個不同類型的空指針有些時候可能不同。某個類型的空指針被強制轉換為另外一個類 型的指針,其結果是該指針轉換為第二個類型的空指針。

    * 對于ANSI編譯器,當兩個類型相同的指針訪問同一塊存儲區(qū)時,則它們比較是相等的。當一個非0整型常量被轉換為指針類型時,它們可能與其他指針相等。對 于非ANSI編譯器,訪問同一塊存儲區(qū)的兩個指針比較可能并不相同。例如,下面兩個指針比較可能相等或不相等,并且他們可能或可能沒有訪問同一塊 存儲區(qū)域。

復制代碼 代碼如下:

    ((int *) 2 )
    ((int *) 3 )

如果你需要'magic'指針而不是NULL,要么分配一些內存,要么將指針視為機器相關的。

復制代碼 代碼如下:

extern int x_int_dummy;    /* in x.c */
#define X_FAIL    (NULL)
#define X_BUSY    (&x_int_dummy)
#define X_FAIL    (NULL)
#define X_BUSY    MD_PTR1  /* MD_PTR1 from "machdep.h" */

    * 浮點數(shù)字既包含精度也包含范圍。這些都是數(shù)據(jù)對象大小無關的。但是,一個32位浮點數(shù)在不同機器上溢出時的值有所不同。同時,4.9乘以5.1在不同的機 器上可能產生兩個不同的數(shù)字。在圓整(rounding)和截斷方面的差異將給出特別不同的答案。

    * 在一些機器上,一個雙精度浮點數(shù)在精度或范圍方面可能比一個單精度浮點數(shù)還要低。

    * 在一些機器上,double值的前半部分可能是一個具有相同值的float類型。千萬不要依賴于此。

    * 提防帶符號字符。例如,在某些VAX系統(tǒng)上,用在表達式中的字符是符號擴展的,但在其他一些機器上并非如此。對有符號和無符號有依賴的代碼是不可移植的。 例如,如果假設c是正值,arrayc在c為有符號且為負值時將無法正常工作。如果你一定要假設signed或unsigned字符的話,請 用SIGNED或UNSIGNED為其加上注釋。無符號字符的行為可由unsigned char保證。

    * 避免對ASCII做假設。如果你必須假設,那么請將其記錄下來并本地化。請記住字符很可能用不止8位表示。

    * 大多數(shù)機器采用2的補碼表示數(shù),但我們在代碼中不應該利用這一特點。使用等價移位操作替代算術運算的優(yōu)化尤其值得懷疑。如果必須這么做,那么機器相關的代 碼應該用#ifdef定義,或者操作應該在#ifdef宏判定下執(zhí)行。你應該衡量一下使用這種難以理解的代碼所節(jié)省的時間與做代碼移植時找bug 所花費的時間相比孰多孰少。

    * 一般情況下,如果字長或值范圍非常重要,應該使用typedef定義具有特定大小的類型。大型程序應該具有一個統(tǒng)一的頭文件用于提供通用的、大小 (size)敏感的類型的typedef定義,這樣更加便于修改以及在緊急修復時查找大小敏感的代碼。無符號類型比有符號整型更加編譯器無關。如 果既可以用16bit也可以用32bit標識一個簡單for循環(huán)的計數(shù)器,我們應該使用int。因為對于當前機器來說,通過整型可以獲取更高效 (自然)的存儲單元。

    * 數(shù)據(jù)對齊也很重要。例如,在不同的機器上,一個四字節(jié)的整型數(shù)的可能以任意地址作為起始地址,也可能只允許以偶數(shù)地址作為起始地址,或者只能以4的整數(shù)倍 的地址作為起始地址。因此,一個特定的結構體的各個元素在不同的機器上的偏移量有不同,即使給定的這些元素在所有機器上的大小相同。事實上,一個 包含一個32位指針和一個8位字符的結構提在三個不同的機器上可能有三個不同的大小。作為一個推論,對象指針可能無法自由互換;通過一個指向起始 地址為奇數(shù)地址長度為4個字節(jié)的指針保存一個整型數(shù)有時可以正常工作,但有時則會導致產生core,有些時候靜悄悄地失敗了(在這個過程中會破壞 其他數(shù)據(jù))。在那些不按字節(jié)尋址的機器上,字符指針更是"事故高發(fā)地區(qū)"。對齊考慮以及加載器的特殊性使得很容易輕率地認為兩個連續(xù)聲明的變量在 內存中也是連在一起的,或者某個類型的變量已經被適當對齊并可以用作其他類型變量使用了。

    * 在一些機器上,諸如VAX(小端),一個字的字節(jié)隨著地址的增加,其重要性提高;而另外一些機器上,諸如68000(大端),隨著地址的增加,其重要性降 低。字或更大數(shù)據(jù)對象(諸如一個雙精度字)的字節(jié)順序可能并不相同。因此,任何依賴對象內從左到右方向位模式的代碼都值得特別細致的審查。只有當 結構體中兩個不同的位字段不被連接以及不被當作一個單元時,這些位字段才具備可移植性。事實上,連接任意兩個變量都是不可移植的行為。

    * 結構體中有一些未使用的空洞。猜想聯(lián)合體用于類型欺騙。尤其是,一個值不應該在存儲時使用一個類型,而在讀取時使用另外一種類型。對聯(lián)合體來說,一個顯式 的標簽(tag)字段可能會很有用。

    * 不同的編譯器在返回結構體時使用不同的約定。這就會導致代碼在接受從不同編譯器編譯的庫代碼中返回的結構體值時會出現(xiàn)錯誤。結構體指針不是問題。

    * 不要假設參數(shù)傳遞機制。特別是指針大小以及參數(shù)求值順序,大小等。例如,下面的代碼就不具備可移植性。

復制代碼 代碼如下:

        c = foo (getchar(), getchar());

    char
    foo (c1, c2, c3)
    char c1, c2, c3;
    {
        char bar = *(&c1 + 1);
        return (bar);            /* often won't return c2 */
    }

    * 上面的例子有諸多問題。??赡芟蛏显鲩L,也可能向下增長(事實上,甚至都不需要一個棧)。參數(shù)在傳入時可能被擴大,例如一個char可能以int型被傳 入。參數(shù)可能以從左到右,從右到左,或以任意順序壓入棧,或直接放在寄存器中(根本無需壓棧)。參數(shù)求值的順序也可能與壓棧的次序有所不同。一個 編譯器可能使用多種(不兼容的)調用約定。

    * 在某些機器上,空字符指針((char *)0)常被當作指向空字符串的指針對待。不要依賴于此。

   *  不要修改字符串常量。下面就是一個臭名昭著的例子

復制代碼 代碼如下:

    s = "/dev/tty??";
  strcpy (&s[8], ttychars);

    * 地址空間可能有空洞。簡單計算一個數(shù)組中未分配空間的元素(在數(shù)組實際存儲區(qū)域之前或之后)的地址可能會導致程序崩潰。如果這個地址被用于比較,有時程序 可以運行,但會破壞數(shù)據(jù),報錯,或陷入死循環(huán)。在ANSI C中,指向一個對象數(shù)組的指針指向數(shù)組結尾后的第一個元素是合法的,這在一些老編譯器上通常是安全的。不過這個"在外邊"不可以被解引用。

    * 只有==和!=比較可用于某給定類型的所有指針。當兩個指針指向同一個數(shù)組內的元素(或數(shù)組后第一個元素)時,使用<<、<=、& amp; gt;或>=對兩個指針進行比較是可移植的。同樣,僅僅對指向同一個數(shù)組內的元素(或數(shù)組后第一個元素)的兩個指針使用算術操作符才是可移 植的。

    * 字長(word size)也影響移位和掩碼。下面代碼在一些68000機器上只會將一個整型數(shù)的最右三個位清0,而在其他機器上它還會將高地址的兩個字節(jié)清零。x &= 0177770 使用 x &= ~07可以在所有機器上正常工作。位字段(bitfield)沒有這些問題。

    * 表達式內的副作用可能導致代碼語義是編譯器相關的,因為在大多數(shù)情況下C語言的求值順序是沒有顯式定義的。下面是一個臭名昭著的例子:

復制代碼 代碼如下:

    a[i] = b[i++];

    在上面的例子中,我們只知道b的下標值沒有被增加。a的下標i值可能是自增后的值也可能是自增前的值。

復制代碼 代碼如下:

    struct bar_t { struct bar_t *next; } bar;
    bar->next = bar = tmp;

在第二個例子中,bar->next的地址很可能在bar被賦值之前被計算使用。

復制代碼 代碼如下:

bar = bar->next = tmp;

第三個例子中,bar可能在bar->next之前被賦值。雖然這可能有悖于"賦值從右到左處理"的規(guī)則,但這確是一個合法的解析。考慮下 面的例子:

復制代碼 代碼如下:

long i;
short a[N];
i = old
i = a[i] = new;

賦給i的值必須是一個按照從右到左的處理順序進行賦值處理后的值。但是i可能在ai被賦值前而被賦值為"(long) (short)new"。不同編譯器作法不同。

    * 質疑代碼中出現(xiàn)的數(shù)值(“魔數(shù)”)。

    * 避免使用預處理器技巧。一些諸如使用/ /粘和字符串以及依賴參數(shù)字符串展開的宏會破壞代碼可靠性。

復制代碼 代碼如下:

    #define FOO(string)    (printf("string = %s",(string)))
    …
  FOO(filename);

只是在有些時候會擴展為

復制代碼 代碼如下:

 (printf("filename = %s",(filename)))

小心。詭異的預處理器在一些機器上可能導致宏異常中斷。下面是一個宏的兩種不同實現(xiàn)版本:

復制代碼 代碼如下:

  #define LOOKUP(chr)    (a['c'+(chr)])    /* Works as intended. */
  #define LOOKUP(c)    (a['c'+(c)])        /* Sometimes breaks. */

第二個版本的LOOKUP可能以兩種不同的方式擴展,并且會導致代碼異常中斷。

    * 熟悉現(xiàn)有的庫函數(shù)和定義(但不用太熟悉。與其外部接口相反,庫基礎設施的內部細節(jié)常會改變并且沒有警告,這些細節(jié)常常也是不可移植的)。你不應該再自己重 新編寫字符串比較例程、終端控制例程或為系統(tǒng)結構編寫你自己的定義。自己動手實現(xiàn)既浪費你的時間,又使得你的代碼可讀性變差,因為另外一個讀者需 要知道你是否在新的實現(xiàn)中做了什么特殊的事情,并嘗試證實它們的存在。同時這樣做會使得你無法充分利用一些輔助的微代碼或其他有助于提高系統(tǒng)例程 性能的方法。更進一步,它將是一個bug的高產源頭。如果可能的話,要知道公共庫之間的差異(如ANSI、POSIX等等)。

    * 如果lint可用,請使用lint。這個工具對于查找代碼中機器相關的構造、其他不一致性以及順利通過編譯器檢查的程序bug時具有很高價值。如果你的編 譯器具備打開警告的開關,請打開它。

    * 質疑在代碼塊內部的與代碼塊外部switch或goto有關聯(lián)的標簽(Label)。

    無論類型在哪里,參數(shù)都應該被轉換為適當?shù)念愋?。當NULL用在沒有原型的函數(shù)調用時,請對NULL進行轉換。不要讓函數(shù)調用成為類型欺騙發(fā)生的地方。C 語言的類型提升規(guī)則很是讓人費解,所以盡量小心。例如,如果一個函數(shù)接受一個32位長的長整型做為參數(shù),但實際傳入的卻是一個16位長的整型數(shù), 函數(shù)??赡軙o法對齊,這個值也可能會被錯誤提升。

    * 在混用有符號和無符號值的算術計算時請使用顯式類型轉換

    * 應該謹慎使用跨程序的goto、longjmp。很多實現(xiàn)"忘記"恢復寄存器中的值了。盡可能將關鍵的值聲明為volatile,或將它們注釋為 VOLATILE。

    * 一些鏈接器將名字轉換為小寫,并且一些鏈接器只識別前六個字母作為唯一標識。在這些系統(tǒng)上程序可能會悄悄地中斷運行。

    * 當心編譯器擴展。如果使用了編譯器擴展,請將他們視為機器依賴并用文檔記錄下來。

    * 通常程序無法在數(shù)據(jù)段執(zhí)行代碼或者無法將數(shù)據(jù)寫入代碼段。即使程序可以這么做,也無法保證這么做是可靠的。

17. 標準C

現(xiàn)代C編譯器支持一些或全部的ANSI提議的標準C。無論何時可能的話,盡量用標準C編寫和運行程序,并且使用諸如函數(shù)原型,常量存儲以及 volatile(易失性)存儲等特性。標準C通過給優(yōu)化器提供有有效的信息以提升程序的性能。標準C通過保證所有編譯器接受同樣的輸入語言以及提供相關 機制隱藏機器相關內容或對于那些機器相關代碼提供警告的方式提升代碼的可移植性。

17.1 兼容性

編寫很容易移植到老編譯器上的代碼。例如,有條件地在global.h中定義一些新(標準中的)關鍵字,比如const和volatile。標準編譯器預 定義了預處理器符號STDC(見腳注8)。void類型很難簡單地處理正確,因為很多老編譯器只理解void,但不認識void。最簡單的方法就是定義一 個新類型VOIDP(與機器和編譯器相關),通常在老編譯器下該類型被定義為char*。

復制代碼 代碼如下:

#if __STDC__
    typedef void *voidp;
#   define COMPILER_SELECTED
#endif
#ifdef A_TARGET
#    define const
#    define volatile
#    define void int
    typedef char *voidp;
#    define COMPILER_SELECTED
#endif
#ifdef …
    …
#endif
#ifdef COMPILER_SELECTED
#    undef COMPILER_SELECTED
#else
    { NO TARGET SELECTED! }
#endif

注意在ANSI C中,#必須是同一行中預處理器指示符的第一個非空白字符。在一些老編譯器中,它必須是同一行中的第一個字符。

當一個靜態(tài)函數(shù)具有前置聲明時,前置聲明必須包含存儲修飾符。在一些老編譯器中,這個修飾符必須是"extern"。對于ANSI編譯器,這個存儲修飾符 必須為static,但全局函數(shù)依然必須聲明為extern。因此,靜態(tài)函數(shù)的前置聲明應該使用一個#define,例如FWD_STATIC,并通 過#ifdef適當定義。

一個"#ifdef NAME"應該要么以"#endif"結尾,要么以"#endif / NAME /結尾,不應該用"#endif NAME"結尾。對于短小的#ifdef不應該使用注釋,因為通過代碼我們可以明確其含義。

ANSI的三字符組可能導致內容包含??的字符串的程序神秘的中斷。

17.2 格式化

ANSI C的代碼風格與常規(guī)C一樣,但有兩點意外:存儲修飾符(storage qualifiers)和參數(shù)列表。

由于const和volatile的綁定規(guī)則很奇怪,因此每個const或volatile對象都應該單獨聲明。

復制代碼 代碼如下:

int const *s;        /* YES */
int const *s, *t;    /* NO */

具備原型的函數(shù)將參數(shù)聲明和定義歸并在一個參數(shù)列表中了。應該在函數(shù)的注釋中提供各個參數(shù)的注釋。

復制代碼 代碼如下:

/*
 * `bp': boat trying to get in.
 * `stall': a list of stalls, never NULL.
 * returns stall number, 0 => no room.
 */
int
enter_pier (boat_t const *bp, stall_t *stall)
{
    …

17.3 原型

應該使用函數(shù)原型使得代碼更加健壯并且運行時性能更好。不幸地是原型的聲明

復制代碼 代碼如下:

extern void bork (char c);

與定義不兼容。

復制代碼 代碼如下:

void
bork (c)
char c;
 …

原型中c應該以機器上最自然的類型傳入,很可能是一個字節(jié)。而非原型化(向后兼容)的定義暗示c總是以一個整型傳入。如果一個函數(shù)具有可類型提升的參數(shù), 那么調用者和被調用者必須以相等地方式編譯。要么都必須使用函數(shù)原型,要么都不使用原型。如果在程序設計時參數(shù)就是可以提升類型的,那么問題就可以被避 免,例如bork可以定義成接受一個整型參數(shù)。

如果定義也是原型化的,上面的聲明將工作正常。

復制代碼 代碼如下:

void
bork (char c)
{
    …

不幸地是,原型化的語法將導致非ANSI編譯器拒絕這個程序。

但我們可以很容易地通過編寫外部聲明來同時適應原型與老編譯器。

復制代碼 代碼如下:

#if __STDC__
#    define PROTO(x) x
#else
#    define PROTO(x) ()
#endif

extern char **ncopies PROTO((char *s, short times));

注意PROTO必須使用雙層括號。

最后,最好只使用一種風格編寫代碼(例如,使用原型)。當需要非原型化的版本時,可使用一個自動轉換工具生成。

17.4 Pragmas

Pragmas用于以一種可控的方式引入機器相關的代碼。很顯然,pragma應該被視為機器相關的。不幸地是,ANSI pragmas的語法使得我們無法將其隔離到機器相關的頭文件中了。

Pragmas分為兩類。優(yōu)化相關的可以被安全地忽略。而那些影響系統(tǒng)行為(需要pragmas)的Pragmas則不能忽略。需要的pragmas應該結合#ifdef使用,這樣如果一個pragma都沒有選到,編譯過程將退出。

兩個編譯器可能通過兩個不同的方式使用同一個給定的pragma。例如,一個編譯器可能使用haggis發(fā)出一個優(yōu)化信號。而另一個可能使用它暗示一個特 定語句,一旦執(zhí)行到此,程序應該退出。不過,一旦使用了pragma,它們必須總是被機器相關的#ifdef包圍。對于非ANSI編譯器,Pragmas 必須總是被#ifdef。確保對#pragma的#進行縮進,否則一些較老的預處理器處理它時會掛起。

復制代碼 代碼如下:

#if defined(__STDC__) && defined(USE_HAGGIS_PRAGMA)
    #pragma (HAGGIS)
#endif

    "ANSI標準中描述的'#pragma'命令具有任意實現(xiàn)定義的影響。在GNU C預處理中,'#pragma'首先嘗試運行游戲'rogue';如果失敗,它將嘗試運行游戲'hack';如果失敗,它將嘗試運行GNU Emacs顯示漢諾塔;如果失敗,它將報告一個致命錯誤。無論如何,預處理將不再繼續(xù)。"

    — GNU CC 1.34 C預處理手冊。

18. 特殊考慮

這節(jié)包含一些雜項:‘做'與'不做'。

    * 不要通過宏替換來改變語法。這將導致程序對于所有人都是難以理解的,除了那個肇事者。

    * 不要在需要離散值的地方使用浮點變量。使用一個浮點數(shù)作為循環(huán)計數(shù)器無疑是搬起石頭砸自己的腳??偸怯?lt;=或>=測試浮點數(shù),對它們永遠不要 用精確比較(==或!=)。

    * 編譯器也有bug。常見且高發(fā)的問題包括結構體賦值和位字段。你無法泛泛的預測一個編譯器都有哪些bug。但你可以在程序中避免使用那些已知的在所有編譯 器上都存在問題的結構。你無法讓你寫的任何代碼都是有用的,你可能仍然會遇到bug,并且在這期間編譯器很可能會被修復。因此,只有當你被強制使 用某個特定的充斥bug的編譯器時,你才應該"圍繞"著編譯器bug寫代碼。

    * 不要依賴自動代碼美化工具。良好代碼風格的主要受益者就是代碼的編寫者,并且尤其在手寫算法或偽代碼的早期設計階段。自動代碼美化工具只應該用在那些已經 完成、語法正確并且此后不能滿足當空白和縮進被更為關注的要求時。伴隨著對細致程序員的細節(jié)的關注,對于那些將函數(shù)或文件布局解釋清楚的工作,程 序員們會做得更好(換句話說,一些視覺布局是由意圖而不是語法決定的,美化工具無法了解到程序員的思想)。粗心的程序員應該學習成為一個細致的程 序員,而不是依賴美化工具讓代碼可讀性更好。

    * 意外地遺漏邏輯比較表達式中的第二個=是一個常犯的問題。使用顯式測試。避免對賦值使用隱式測試。

復制代碼 代碼如下:

abool = bbool;
if (abool) { …

當嵌入的賦值表達式使用時,確保測試是顯式的,這樣后續(xù)它就無法被"修復"了。

復制代碼 代碼如下:

while ((abool = bbool) != FALSE) { …
while (abool = bbool) { …    /* VALUSED */
while (abool = bbool, abool) { …

    顯式地注釋那些在正??刂屏髦獗恍薷牡淖兞浚蚱渌赡茉诰S護過程中中斷的代碼。

    現(xiàn)代編譯器會自動將變量放到寄存器中。對于你認為最關鍵的變量慎用寄存器。在極端情況下,用寄存器標記2-4個最為關鍵的值,并且將剩余的標記為 REGISTER。后者在那些具有較多寄存器的機器上可以#define為寄存器。

19. Lint

Lint是一個C程序檢查工具,用于檢查C語言源碼文件,探測和報告諸如類型不兼容、函數(shù)定義與調用不一致以及潛在的bug等情況。強烈建議在所 有程序上使用lint工具,并且期望大多數(shù)工程將lint作為官方驗收程序的一部分。

應該注意的是使用lint的最好方法不是將lint作為官方驗收之前的一道必須跨過的柵欄,而是作為一個在代碼發(fā)生添加或變更之后使用的工具。 Lint可以發(fā)現(xiàn)一些隱藏的bug并且可以在問題發(fā)生前保證程序的可移植性。lint產生的許多信息確實暗示了一些事情是錯誤的。一個有意思的故 事是關于一個漏掉了fprintf的一個參數(shù)的程序:

復制代碼 代碼如下:

fprintf ("Usage: foo -bar <file>\n");

作者從未有過一個問題。但每當一個正常用戶在命令行上犯錯,這個程序就會產生一個core。許多版本的lint工具都能發(fā)現(xiàn)這個問題。

大多l(xiāng)int選項都值得我們學習。一些選項可能在合法的代碼上給出警告,但它們也會捕捉到許多把事情搞遭的代碼。注意'–p'只能為庫的一個子 集檢查函數(shù)調用和類型的一致性,因此程序為了最大化的覆蓋檢查,應該同時進行帶–p和不帶–p的lint檢查。

Lint也可以識別代碼里的一些特殊注釋。這些注釋可以強制讓lint在發(fā)現(xiàn)問題時關閉警告輸出,還可以作為一些特殊代碼的文檔。

20. Make

另外一個非常有用的工具是make。在開發(fā)過程中,make只會重新編譯那些上次make后發(fā)生了改變的模塊。它也可以用于自動化其他任務。一些 常見的約定包括:

復制代碼 代碼如下:

all
     執(zhí)行所有二進制文件的構建過程

clean
     刪除所有中間文件

debug
     構建一個測試用二進制文件a.out或debug

depend
     制作可傳遞的依賴關系

install
     安裝二進制文件,庫等

deinstall
     取消安裝

mkcat
     安裝手冊

lint
    運行l(wèi)int工具

print/list
    制作一個所有源文件的拷貝

shar
    為所有源文件制作一個shar文件

spotless
     執(zhí)行make clean,并將源碼存入版本控制工具。注意:不會刪除Makefile,即便它是一個源文件。

source
     撤銷spotless所做的事情。

tags
     運行ctags(建議使用-t標志)

rdist
     分發(fā)源碼到其他主機

file.c
     從版本控制系統(tǒng)中檢出這個文件


除此之外,通過命令行也可以定義Makefile使用的值(如"CFLAGS")或源碼中使用的值(如"DEBUG")。

21. 工程相關的標準

除了這里提到內容外,每個獨立的工程都期望能建立附加標準。下面是每個工程程序管理組需要考慮的問題中的一部分:

    * 哪些額外的命名約定需要遵守?尤其是,那些用于全局數(shù)據(jù)的功能歸類以及結構體或聯(lián)合體成員名字的系統(tǒng)化的前綴約定非常有用。

    * 什么樣的頭文件組織適合于工程特定的數(shù)據(jù)體系結構?

    * 應該建立什么樣的規(guī)程來審核lint警告?需要確立一個與lint選項一致的寬容度,保證lint不會針對一些不重要的問題給出警告,但同時保證真正的bug或不一致問題不被隱藏。

    * 如果一個工程建立了自己的檔案庫,它應該計劃向系統(tǒng)管理員提供一個lint庫文件。這個lint庫文件允許lint工具檢查對庫函數(shù)的兼容性使用。

    * 需要使用哪種版本控制工具?

22. 結論

這里描述了一套C語言編程風格的標準。其中最重要的幾點是:

    * 合理使用空白和注釋,使得我們通過代碼布局就可以清楚地看出程序的結構。使用簡單表達式、語句和函數(shù),使他們可以很容易地被理解。

    * 記住,在將來某個時候你或其他人很可能會被要求修改代碼或讓代碼運行在一臺不同的機器上。精心編寫代碼,使得其可以移植到尚不確定的機器上。局部化你的優(yōu)化,因為這些優(yōu)化經常讓人困惑,并且對于該優(yōu)化措施是否適合其他機器我們持悲觀態(tài)度。

    * 許多風格選擇是主觀武斷的。保持代碼風格一致比遵循這些絕對的風格規(guī)則更重要(尤其是與組織內部標準保持一致)?;煊蔑L格比任何一種糟糕的風格都更加糟糕。

無論采用哪種標準,如果認為該標準有用就必須遵循它。如果你覺得遵循某條標準時有困難,不要僅僅忽略它們,而是在和你當?shù)氐拇髱熁蚪M織內的有經驗的程序員討論后再做決定。

23. 參考資料

B.A. Tague, C Language Portability, Sept 22, 1977. This document issued by department 8234 contains three memos by R.C. Haight, A.L. Glasser, and T.L. Lyon dealing with style and portability.
S.C. Johnson, Lint, a C Program Checker, Unix Supplementary Documents, November 1986.
R.W. Mitze, The 3B/PDP-11 Swabbing Problem, Memorandum for File, 1273-770907.01MF, September 14, 1977.
R.A. Elliott and D.C. Pfeffer, 3B Processor Common Diagnostic Standards- Version 1, Memorandum for File, 5514-780330.01MF, March 30, 1978.
R.W. Mitze, An Overview of C Compilation of Unix User Processes on the 3B, Memorandum for File, 5521-780329.02MF, March 29, 1978.
B.W. Kernighan and D.M. Ritchie, The C Programming Language, Prentice Hall 1978, Second Ed. 1988, ISBN 0-13-110362-8.
S.I. Feldman, Make -- A Program for Maintaining Computer Programs, UNIXSupplementary Documents, November 1986.
Ian Darwin and Geoff Collyer, Can't Happen or / NOTREACHED / or Real Programs Dump Core, USENIX Association Winter Conference, Dallas 1985 Proceedings.
Brian W. Kernighan and P. J. Plauger The Elements of Programming Style. McGraw-Hill, 1974, Second Ed. 1978, ISBN 0-07-034-207-5.
J. E. Lapin Portable C and UNIX System Programming, Prentice Hall 1987, ISBN 0-13-686494-5.
Ian F. Darwin, Checking C Programs with lint, O'Reilly & Associates, 1989. ISBN 0-937175-30-7.
Andrew R. Koenig, C Traps and Pitfalls, Addison-Wesley, 1989. ISBN 0-201-17928-8.

相關文章

  • C++實現(xiàn)LeetCode(73.矩陣賦零)

    C++實現(xiàn)LeetCode(73.矩陣賦零)

    這篇文章主要介紹了C++實現(xiàn)LeetCode(73.矩陣賦零),本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下
    2021-07-07
  • C中qsort快速排序使用實例

    C中qsort快速排序使用實例

    在學習C++ STL的sort函數(shù),發(fā)現(xiàn)C中也存在一個qsort快速排序,要好好學習下C的庫函數(shù)啊
    2014-01-01
  • C語言完數(shù)的實現(xiàn)示例

    C語言完數(shù)的實現(xiàn)示例

    C語言中的完數(shù)指的是一個正整數(shù),本文主要介紹了C語言完數(shù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-05-05
  • C++ Assert()斷言機制原理以及使用方法

    C++ Assert()斷言機制原理以及使用方法

    下面小編就為大家?guī)硪黄狢++ Assert()斷言機制原理以及使用方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-01-01
  • C語言中對文件最基本的讀取和寫入函數(shù)

    C語言中對文件最基本的讀取和寫入函數(shù)

    這篇文章主要介紹了C語言中對文件最基本的讀取和寫入函數(shù),是C語言入門學習中的基礎知識,需要的朋友可以參考下
    2015-08-08
  • C++如何保存bmp圖片

    C++如何保存bmp圖片

    這篇文章主要介紹了C++如何保存bmp圖片問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • C++中套接字庫sockpp的使用詳解

    C++中套接字庫sockpp的使用詳解

    sockpp是一個開源、簡單、現(xiàn)代的C++套接字庫,這篇文章主要為大家詳細介紹一下套接字庫sockpp的使用,文中的示例代碼講解詳細,感興趣的小伙伴可以學習一下
    2023-11-11
  • C++ Boost Heap使用實例詳解

    C++ Boost Heap使用實例詳解

    Boost是為C++語言標準庫提供擴展的一些C++程序庫的總稱。Boost庫是一個可移植、提供源代碼的C++庫,作為標準庫的后備,是C++標準化進程的開發(fā)引擎之一,是為C++語言標準庫提供擴展的一些C++程序庫的總稱
    2022-11-11
  • C++?Date類的具體使用(構建,重載等)

    C++?Date類的具體使用(構建,重載等)

    本文主要介紹了C++?Date類的具體使用(構建,重載等),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-07-07
  • c語言顏色代碼詳解

    c語言顏色代碼詳解

    在本篇文章里小編給大家整理的是關于c語言顏色代碼的知識點內容,需要的朋友們可以參考下。
    2020-02-02

最新評論