以一般的 C 開發習慣來說,variable name 會使用小寫的英文字母來命名;而 symbolic constants 則會用大寫的英文字母來命名 。
另外變數的命名有幾點需要注意:
- 名稱的長度必須在 31 的字元之內
- 不可使用 key words (例如:if、else、int、float .... etc)
Data Types and Sizes
C 所提供的基本型態,在之前已經介紹過了。
另外需要注意的是,同樣的型態在不同的 OS 中,或是在不同的 CPU 中(32 bits 或 64 bits),所使用的長度可能不一樣,例如:int 有可能用 16 bits 或是 32 bits 來表示。
另外,有兩個與變數值為正或負相關的關鍵字為「signed」與「unsigned」。
一般若沒有使用的情況下,則會預設為 signed,表示變數是可以表示出正負數的;而 unsigned 則是指定正數(非負數)。
想當然爾,每個型態所使用的長度是相同的,但有些要表現負數,有些卻不用,然而這樣的差別,就影響了各型態所能表現值的範圍;舉例來說,每個 char 佔了 8 bits 的記憶體空間,若是在 signed 的情況下,可以表現 -128~127;但若是在 unsigned 的情況下,就可以表現 0~255。因此可以看出,關鍵字的使用會影響到變數值可表現的範圍。
以下用段程是碼來顯示各型態變數值可表現的範圍:(under Linux)
#include <stdio.h>
#include <limits.h>
main() {
//signed data type
printf(" signed char min : %d\n", SCHAR_MIN); //-128
printf(" signed char max : %d\n", SCHAR_MAX); // 127
printf(" signed short min : %d\n", SHRT_MIN); //-32768
printf(" signed short max : %d\n", SHRT_MAX); // 32767
printf(" signed int min : %d\n", INT_MIN); //-2147483648
printf(" signed int max : %d\n", INT_MAX); // 2147483647
printf(" signed long min : %ld\n", LONG_MIN); //-2147483648
printf(" signed long max : %ld\n", LONG_MAX); // 2147483647
//unsigned data type
printf("unsigned char max : %u\n", UCHAR_MAX); //255
printf("unsigned short max : %u\n", USHRT_MAX); //65535
printf("unsigned int max : %u\n", UINT_MAX); //4294967295
printf("unsigned long max : %lu\n", ULONG_MAX); //4294967295
} //end main
Constants
由於 C 支援了許多不同型態的變數,因此在表示的時候,為了辨識不同型態的變數值,會有不同的表示方式,以下一一來說明。
整數(interger)
以整數型態來說,若是 int 則是沒有任何問題,不需要加上特殊符號,因為 C 預設的 integer 型態即為 int;而若是 long,則必須要在變數值後方加上大寫或小寫 L,例如:123456789L;而若加上是 unsigned 的數值,又必須在變數值後方加上大寫或小寫 U,因此 unsigned long 的變數值,後方要加上「ul」或「UL」。
而整數一般常用的表示方式為 10 進位(octal)或是 16 進位(hexadecimal)。其中變數值以「0」為開頭的為 10 進位的變數值,例如:031(表示為 31);「0x」開頭的則是 16 進位的變數值,例如:0x1F(表示 31)。
綜合以上說明,舉例來說,若變數值為 0x1FUL,則表示此變數為 unsigned long 型態,其值為 31。
浮點數(floating)
再來是浮點數型態,由於 C 預設 float 型態為 double,因此 double 型態的變數值後方就不需多加任何符號;但若是 float,則需要在變數值後方加上大寫或小寫 F;另外一個比較特殊的,浮點數變數值加上大寫或小寫 L 則是表示 long double。
字元(character)
字元這個部份能表示的可就多了! 在一般的表示方法中,所表示的為 ASCII 的字元,每個字元有其對應的 int 值,例如:'0' 代表 48,因此字元可以用來進行比對。
此外,還有許多特殊符號或是特殊效果,必須使用跳脫字元來表示,看起來像多個字元,但其實對 C 來說,只是單一個字元! 以下列表說明:
跳脫字元 | 意義 | 跳脫字元 | 意義 |
\a | alert(cell) character | \\ | backslash |
\b | backspace | \? | question mark |
\f | form feed | \' | single quote |
\n | new line | \" | double quote |
\r | carriage return | \000 | octal number |
\t | horizontal tab | \xhh | hexadecimal number |
\v | vertical tab |
其中有一個較為特殊的是「\0」,其所代表的意義為 null,用來做為 string 的結尾表示之用,因此一個 string 儲存於記憶體時,除了 string所需要的記憶體長度外,還必須要多一個 bit 的空間來儲存這個代表 string 結尾的字元。
另外,針對 string 處理的部份,standard header 提供了「string.h」,其中包含了許多關於 string 的 function 可以使用,例如:計算 string 長度的 strlen(s)。
列舉(enumeration)
最後 enumeration 是比較特殊的部份,而 enumeration 其實就是一群 int 變數值的集合,然後用比較容易瞭解的方式去表示。
在 enumeration 中會出現很多變數名稱,如果沒有指定,預設從第一個開始,其 int value 為 0,第二個則為 1,其他依此類推..........
舉例來說:
PS.變數名稱多以大寫英文字母來表示。//第一個 NO 所代表的 int value 為 0
//第二個 YES 所代表的 int value 為 1
enum boolean { NO, YES };
//為每一個變數名稱指定值(每個跳脫字元都屬於 int value)
enum escapes { BELL = '\a', BACKSPACE = '\b', TAB = '\t'
NEWLINE = '\n', VTAB = '\v', RETURN='\r' };
//指定第一個的 int value 為 1,之後不指定就會從 1 遞增下去
//FEB = 2、MAR = 3、APR = 4 ..... etc
enum months { JAN=1, FEB, MAR, APR, MAY, JUN,
JUL, AUG, SEP, OCT, NOV, DEC};
Declarations
在這個部份,並沒有什麼特殊,只是記得變數使用前必須要宣告!
另外,可以在宣告變數時直接給定其值,不需多一行來指定。
其中比較需要注意的是,用「const」關鍵字所宣告的變數,其值是不能夠被改變的;若程式中去修改,compiler 會發出錯誤警告。
例如:
另外,使用 const 的宣告方式,也可以用在 function 的參數上,例如:string.h header 中的「int strlen(const char[])」,如此一來即表示,該傳入的參數在 function 中是不能被更改的!!//用 const 宣告的變數,其值都不可被修改
const double e = 2.71283745345;
const char msg[] = "warning: ";
Arithmetic Operators
在算術相關的操作上,比較需要注意的是「%」(取餘數)符號無法使用在 float 或 double 型態上的變數。
另外,「/」與「%」針對負數的處理,可能會因為機器的不同而有所不同。
Relational and Logical Operators
在優先權的部份,arithmetic operators 是比 relational operators 來的稍微高一些,使用時必須注意一點。
以下直接用範例來說明:
因此,若有很多條件判斷時,程式在撰寫上必須多注意,不然很容易產生很難處理的 bug。//條件判斷的部份在 for loop 的中間一段,條件如下:
//(1)i 小於 lim - 1
//(2)由getchar()取得的字元不是代表new line(n)
//(3)由getchar()取得的字元不是代表結尾(EOF)
for(i = 0 ; i < lim - 1 && (c = getchar()) != 'n' && c != EOF ; i++)
//可以用括號的方式將條件敘述清楚,雖然多了幾個括號,不過不會有優先權搞錯的問題
//因此改寫如下:
for(i = 0 ; (i < (lim - 1)) && ((c = getchar()) != 'n') && (c != EOF) ; i++)
//以下兩種寫法,其實都是可以用的
//只是第一種看起來比較容易讓人理解,但實際使用上可能情況更為複雜
if(!valid)
if(valid == 0)
Type Conversions
由於寫程式的時候會遇到的情況很多,因此對於變數進行型態上的轉換是很平常的事情,因此在型態轉換上有幾點必須注意的事情:
- 一般來說,若對變數進行操作,則會被轉換為共通的型態。
- 有些情況下 compiler 會進行自動轉換,不過規則是較小型態的變數會轉換為較大型態的變數。
- 若是將較大型態的變數值指定給較小型態的變數,compiler 會發出警告,但不會認為是錯誤的(但會喪失部份精確度)
因此舉例來說,char 型態的變數,本就是由一個 byte 的記憶體位置所組成,因此也可以當作 int 來表示或使用。
以下用個程式範例,來說明如何將數字 string 轉為 int 值:
另外一個程式,是將大寫字母轉換為小寫字母:int atoi(char s[]) {
int i, n;
n = 0;
//在 for loop 中,s[i] 會自動被轉為 int
for(i = 0 ; s[i] >= '0' && s[i] <= '9' ; i++)
n = 10 * n + (s[i] - '0'); //括號中的敘述會自動轉為 int
return n;
} //end atoi
以上的作法是因為那些 char 型態的變數都屬於 ASCII 的範圍內,因此可以順利轉換為 int。int lower(int c) {
if(c >= 'A' && c <= 'Z')
return c + 'a' - 'A';
else
return c;
} //end lower
不過,變數型態的轉換其實不需要這麼麻煩,因為 C 提供的 standard header 中就有名稱為「ctype.h」的 header 提供給程式設計師方便不同型態間的轉換。
若是透過 ctype.h 中所提供的 fcuntion,就不需要考慮在不同平台之間 character set 可能不一樣的問題了! 若是要字母轉小寫,可以使用 tolower(int ch),若是要檢查是不是數字,可以用 isdigit(int ch)。
除了上述由 compiler 所自行進行的轉換外,也可以強制進行變數型態的轉換,轉換的語法如下:
(type-name)expression在某些情況下,強制轉換是需要的,例如在 math.h 中提供的 sqrt(double num) function,其中參數部分就必須是 double,因此若有一個 integer variable 為 n,可以透過以下方式代入:
sqrt((double)n)透過此方式,就可以在代入 sqrt function 計算之前將型態強制轉換為 double。
但是若是事先有定義出 function 的 prototype,compiler 就會自行協助轉換的工作,例如:
double sqrt(double); // function prototype
root2 = sqrt(2); // 2 將會自動轉為 double 型態代入
Bitwise Operators
C 提供了六種運算子作為 bit 的處理! 分別是:
運算元 | 說明 |
& | AND |
| | OR |
^ | Exclusive OR |
<< | 位元左移 |
>> | 位元右移 |
~ | 1's 補數 |
這些用在 bit 的操作,一般來說可能不會常使用,不過在特殊的用途上其實還蠻常會運用到的,例如:網路程式的開發。
AND 通常是作為 mask 之用。
OR 通常作為將特定 bit 開啟(0 -> 1)之用。
至於位元左右移,可以達成乘以 2 or 除以 2 的效果。