注釋符號
- 幾個似非而是的注釋問題
例子:
(A) int / * ... * /i;
(B) char * s = "abcdefgh //hijklmn";
(C) //Is it a \valid comment?
(D) in/ * ... * /t i;
我們知道C語言里可以有兩種注釋方式:“/* */” 和 “ // ”。那么上面幾條注釋是否正確。
(A)中,有人認為Bain一起剔除掉注釋后代碼會被解析成int i,所以不正確。編譯器的確會將注釋剔除,但不是簡單的剔除,而是 用空格代替原來的注釋 。 如:/* 這是*/ # /*一條*/ define /*合法的*/ ID /*預處理 */ replacement /*指*\ list /* 令*/
(B)中,我們知道雙引號引起來的都是字符串常量,那雙斜杠也不例外。
(C)中,這是一條合法的注釋,因為“\”是一條接續符。
(D)中,前面說過注釋會被空格替換,那這里也很好理解了。
y = x /*p
只要斜杠(/)和星號(*)之間沒有空格,都會被當作注釋的開始,這一點一定要注意。怎么寫出出色的注釋
- 注釋應當準確、易懂、防止有二義性。錯誤的注釋不但無益反而有害
- 邊寫代碼邊注釋,修改代碼的同時修改相應的注釋,以保證注釋與代碼的一致性,注釋太多了會讓人眼花繚亂。
- 一目了然的語句不加注釋。
- 對于全局數據(全局變量、常量定義等)必須要加注釋。
- 注釋采用英文,盡量避免在注釋中使用縮寫,特別是不常用的縮寫。
- 注釋的位置應與被描述的代碼相鄰,可以與語句在同一行,也可以在上行,但不可放在下方。同一結構中不同域的注釋要對齊。
- 當代碼比較長,特別是有多重嵌套時,應當在一些段落的結束處加注釋,便于閱讀。
- 注釋的縮進要與代碼的縮進一致。
- 注釋代碼段應注重“為何做(why)”而不是“怎么做(how)”
- 數值的單位一定要注釋。
- 對變量的范圍給出注釋,尤其是參數。
- 對一系列的數字編號給出注釋,尤其在編寫底層驅動程序的時候(比如引腳編號)。
- 對于函數的入口/出口數據、條件語句、分支語句給出注釋。
- 避免在一行代碼或表達式的中間插入注釋。
- 復雜的函數中,在分支語句、循環語句結束之后需要適當的注釋,方便區分各分支或循環體。
- 對于不需要被編譯的區域要使用條件編譯來實現,例如,使用帶有注釋的 #if 或 #ifdef 結構。
接續符和轉義符
C語言里以反斜杠(\)表示斷行。編譯器會將反斜杠剔除掉,跟在后面的字符自動續接到前一行。但是注意:反斜杠之后不能有空格,反斜杠的下一行之前也不能有空格。
反斜杠除了可以被用作接續符外,還能被用做轉義字符的開始標識。常用的轉義字符以及含義如下圖:
單引號、雙引號
雙引號引起來的都是字符串常量,單引號引起來的都是字符常量。
邏輯運算符
“ || ”和“ && ”是我們經常用到的邏輯運算符,與按位運算符“ | ”和“ & ”是兩碼事。
int i = 0;
int j = 0;
if ((++i > 0) || (++j > 0))
{// 打印出i和j的值
}//結果: i = 1; j = 0;
不要驚訝。邏輯運算符“ || ”兩邊的條件只要有一個為真,其結果就為真;邏輯運算符“ && ”兩邊的條件只要有一個結果為假,其結果就為假。if ((++i > 0) || (++j > 0))語句中,先計算(++i > 0),發現其結果為真,后面的(++j > 0)便不再計算;同樣“&&”運算符也要注意這種情況。這是容易出錯的地方。在某些情況下,邏輯運算符可以代替if語句
int a = 3;
int b = 5;
if (a < b)
{a = b;
}
/************/
//等同于
(a < b) && (a = b);//如果前面為假,則整體為假,后面也不需要執行
//如果前面為真,還要考慮后面是否為真,繼續執行后面的式子
位運算符
C語言中位運算包括下面幾種:
&……按位與
| …….按位或
^…….按位異或
~…….取反
<<….左移
>>….右移
前四種操作很簡單,一般不會出錯。但要注意按位運算符“ | ”和 “ & ”與邏輯運算符“ || ”和 “ && ”完全是兩碼事,別混淆了。其中按位異或操作可以實現不用第3個臨時變量交換兩個變量的值: a ^=b; b ^= a; a ^= b;但并不推薦這么做,因為這樣的代碼讀起來很費勁。
- 位操作需要用宏定義好后再使用。
//例如常用的位操作宏
#define SETBIT(x, y) ((x) |= (y))
#define CLRBIT(x, y) ((x) &= ~(y)) // 要十分小心y是否是有符號數
//建議不適用取反操作,而是自己計算需要的值,否則非常容易出錯
#define TOGLBIT(x, y) ((x) ^= (y))
#define TESTBIT(x, y) ((x) & (y))
- 如果位操作符’~’和‘<<’ 應用于基本類型無符號字符型或無符號短整型的操作數,結果會立即轉換成操作數的基本類型。
#include <stdint.h>
#inlcude <stdio.h>
int main()
{uint8_t port = 0x5aU;uint8_t result_8;uint16_t result_16;result_8 =(~port)>>4; //不能得到期待的0xa5result_8 =((uint8_t)(~port))>>4; //正確的寫法result_16=((uint16_t)(~(uint16_t)port))>>4; //正確的寫法return 0;
}
- 位運算符不能用于基本類型(underlying type)是有符號的操作數上。
- 一元減運算符不能用在基本類型無符號的表達式上,除非在使用之前對兩個操作數進行大小判斷,且被減數必須大于減數
- 左移和右移
左移運算符“<<”是雙目運算符,其功能是把“<<”左邊的運算數的各二進位全部左移若干位,由“<<”右邊的數制定移動的位數,高位丟棄,地位補0.
右移運算符“>>”是雙目運算符,其功能是把“>>”左邊的運算數的各二進位全部右移若干位,由“>>”右邊的數制定移動的位數。但注意:對于有符號數,在右移時,符號位將隨同移動。當為正數時,最高位補0;而為負數時,符號位為1,最高位補0或是補1取決于編譯系統的規定。Turbo
C和很多系統規定補1.
左移和右移的位數不能大于和等于數據的長度,不能小于0.
++、- - 操作符
之前的博客里有這方面的介紹(傳送門)
貪心法:每一個符號應該包含盡可能多的字符。還需要注意的是:除了字符串和字符常量,符號的中間不能嵌有空白(空格、制表符、換行符等),比如:==是單個符號,而==是兩個等號
除法
三條性質:
- 最重要的一點,我們希望q * b + r == a,因為這是定義余數的關系。
- 如果我們改變 a 的正負號,希望 q 的符號也隨之改變,但 q 的絕對值不會變。
- 當 b > 0 時,我們希望保證r >= 0 且 r < b .
但是他們不能同時成立。大多數編程語言選擇了放棄第3條,而改為要求余數與被除數的正負號相同,這樣性質1和性質2就可以得到滿足。大多數 C 語言編譯器也都是如此。
運算符的優先級
記憶技巧:
① 偽運算符的優先級最高,單目運算符優先級總是高于雙目;
② 對于雙目運算符而言,算術運算>位運算>邏輯運算;
③ 自右向左結合的運算符只有單目運算符和賦值運算符。
2 一些容易出錯的優先級問題,見下表。