變數型態(type)介紹 在 C 中,所有的變數在使用前都必須宣告其型態(type),以下是 C 提供的基本型態
- char:character,為單一 byte
- short:short integer
- int:integer
- long:long integer
- float:floating point
- double:doule-precision floating point
而每種變數型態都有其大小的範圍:(以下用個簡單範例來說明)
#include <stdio.h>
#include <limits.h> //要取得變數型態的大小範圍值,必須 include 此 header file
main()
{
//short(short integer) 的範圍 => 16 bits
printf("%d\n", SHRT_MIN); //-32768
printf("%d\n", SHRT_MAX); // 32767
//int(interget) 的範圍
printf("%d\n", INT_MIN); //-2147483648
printf("%d\n", INT_MAX); // 2147483647
}
以上是在 Linux 的環境上的執行結果,每個 int 的長度為 32 bits;而各種變數型態在不同的 OS 平台上可能會有不同的長度,這可能需要注意一下。
使用不同型態的變數,撰寫方式也必須不同 以下用一個華氏(fahrenheit)轉攝氏(celsius)範例來說明:
#include <stdio.h>
main()
{
float fahr, celsius;
int lower, upper, step;
lower = 0;
upper = 300;
step = 20;
//以 while 的方式撰寫
fahr = lower;
while(fahr <= upper) {
//由於指定變數型態為 float,前面相除的兩個變數必須以 float 方式去撰寫
//若沒有,會被 compiler 視為 int 而遭截斷為 0
celsius = (5.0 / 9.0) * (fahr - 32.0);
//以比較漂亮的方式印出結果
printf("%3.0f\t%6.1f\n", fahr, celsius);
fahr = fahr + step;
} //end while
//以 while 的方式撰寫 =============== End
//以 for 的方式撰寫
for(fahr = 0 ; fahr <= upper ; fahr = fahr + step)
printf("%3.0f\t%6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32.0));
//以 for 的方式撰寫 =============== End
}
Symbolic Constant 的使用 在程式中,如果直接使用某個數字,而未註明其用途,可能會讓其他看程式碼的人不知道原作者的用意為何。因此最好每個值都可以賦與其意義,讓使用上不會太過於突兀,其中一個方法就是使用「Symbolic Constant」。
而使用的語法如下:
#define [name] [replacement text]
以下用個簡單範例來說明:
#include <stdio.h>
//Symbolic Constant 的定義 (名稱通常以「大寫」表示)
#define LOWER 0
#define UPPER 300
#define STEP 20
main()
{
int fahr;
for(fahr = LOWER ; fahr <= UPPER ; fahr = fahr + STEP)
printf("%3d %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32));
}
如此一來,每個值都知道其代表意義為何了!!
Character Input and Output在程式中,字元(字串)資料的處理是相當普遍的。
在 C 中,資料的 input 與 output 的功能是由 library 所提供,因此不太需要須考慮其實作的細節。
接著,文字資料的輸入或輸出方面,不論來源是鍵盤,或是檔案,都會被視為由許多字元所組合而成的 stream;而 text stream 則是一行一行分開,且有順序的字元,每一行都包含 0 或以上的字元,且在結尾帶有一個「n」(newline)字元。
File Copying
C standard library 中提供了許多存取單一字元的 function,其中最常用的就是
putchar() 以及
getchar() 兩個 function,以下有個簡單範例將來源資料一個一個字元取出並印出來:
#include <stdio.h>
main()
{
//為了確保變數可以存入所有字元,因此宣告型態為 int
int c;
//取得一個字元
while((c = getchar()) != EOF) { //未到檔案結尾(End of File),繼續執行
putchar(c); //輸出一個字元
printf("\n%s\n", "-----------------------");
} //end while
}
PS. 其實 EOF 的值為 -1
Character & Word & Line Counting首先說明計算的規則:
- Character:非 EOF 字元
- Word:非空白、n、t .... 等字元
- Line:n 字元
以下用個簡單範例說明:
#include <stdio.h>
#define IN 1 //在 word 內
#define OUT 0 //在 word 外
main()
{
//number of characters
//number of words
//number of lines
long nc, nw, nl;
nc = nw = nl = 0; //初始化變數
//保留輸入字元之用
int c;
//檢查是否在 word 中
int state;
while((c = getchar()) != EOF) {
++nc; //character count + 1
//檢查是否為 new line
if(c == '\n')
++nl;
//檢查字元是否為空白、new line 或是 tab
if(c == ' ' || c == '\n' || c == '\t')
state = OUT; //設定在 word 之外
else if(state == OUT) { //字元不是空白,也不是 new line,也不是 tab,但狀態正在 word 之外
//表示進入新的 word 中
state = IN; //設定為進入 word 中
++nw;
}
} //end while
printf("%ld %ld %ld\n", nc, nl, nw);
}
【註】輸入 Ctrl + D 即為丟出 EOF 訊息! Arrays看過上面範例後,如果要計算 0~9 各出現了幾次,要怎麼做呢? 有兩種作法:
- 宣告 10 個變數,分別儲存 0~9 所出現的次數
- 宣告 1 個長度為 10 的 array,每個 element 分別儲存 0~9 出現的次數
顯而易見的,使用 array 是比較好的選擇,以下用個範例程式來說明:
#include <stdio.h>
main()
{
int c, i, nwhite, nother;
int ndigit[10]; //宣告長度為 10 的 int array
nwhite = nother = 0;
for(i = 0 ; i < 10 ; i++)
ndigit[i] = 0;
while((c = getchar()) != EOF) {
if(c >= '0' && c <= '9') //檢查是否為數字
ndigit[c - '0']++; //由於 c 是 char 型態,因此透過此方式轉為 int
else if(c == ' ' || c == '\n' || c == '\t')
nwhite++;
else
nother++;
} //end while
printf("digits = ");
for(i = 0 ; i < 10 ; i++)
printf(" %d", ndigit[i]);
printf(", white space = %d, other = %d\n", nwhite, nother);
}
Functionfunction 的目的在於將程式封裝,讓使用 function 的人不需要知道 function 是如何達成其所宣告可達成的功能,只要拿來使用即可。
這個概念在 OO 設計中是相當重要的一部分,當然 C 也提供了這個功能,以下用個範例程式來說明:
#include <stdio.h>
//function 要在前面先進行宣告
//此處定義的參數名稱不會與以下的程式中的變數名稱有所衝突(僅宣告而已)
//甚至可以省略為 int power(int, int)
int power(int m, int n);
main()
{
int i;
for(i = 0 ; i < 10 ; i++)
printf("%6d %6d %6d\n", i, power(2, i), power(-3, i));
}
//回傳型態:int
//function 名稱:power
//參數 1:base
//參數 2:n
int power(int base, int n) //計算次方的 function
{
int i, p;
p = 1;
for(i = 1 ; i <=n ; i++)
p = p * base;
return p;
}
Arguments - Call by Value在 C 中,所有 function 的參數都是 Call by Value,也就是呼叫 function 時,程式會將要傳入的變數複製一份再傳入,而不會用原本的變數,因此在 function 中對變數的任何操作,並不會影響到原本的變數。
對於 function 來說,參數值可以視為 funtion 開始就初始化並已經指定好值的變數。
PS. 其實參數名稱所代表的是 memory address,也就是之後會提到的 pointer Character Array在一般程式語言中很常用的 string 型態,在 C 中是沒有的;而在 C 中若是要表示 string,就必須要使用 char array。
除此之外,為了方便辨識 string 的結尾,C 會在 char array 最後方加入「n」與「0」 兩個字元,如下所示:
範例字串:
this is a dog! 0
| 1
| 2
| 3
| 4
| 5
| 6
| 7
| 8
| 9
| 10
| 11
| 12
| 13
| 14
| 15
|
t
| h
| i
| s
|
| i
| s
|
| a
|
| d
| o
| g
| !
| \n
| \0
|
表示上面那個 string,最少需要長度為 16 的 char 陣列,其中 index 為 14、15 個兩個字元,是 C 用來做 string 結尾表示之用,因此在處理 string 的時候,必須要注意這個地方。
以下用個範例來說明 char array 的使用:
#include <stdio.h>
#define MAXLINE 1000
//宣告 function
int getline(char[], int maxline);
void copy(char[], char[]);
main()
{
int len; //目前資料行的長度
int max; //目前最大的資料行長度
char line[MAXLINE]; //目前讀取的資料行
char longest[MAXLINE]; //最長資料行的儲存陣列
max = 0;
while((len = getline(line, MAXLINE)) > 0) { //取得每一行資料,並檢查長度是否大於0
if(len > max) { //資料長度比目前最長的還要更長
max = len; //修改最長資料長度變數
copy(longest, line); //最長資料陣列的資料也隨著更換
}
//印出最長的資料行
if(max > 0)
printf("%s", longest);
} //end while
}
//讀取每行的資料進入 s 陣列,並回傳陣列長度
int getline(char s[], int lim)
{
int c, i;
//資料行檢查條件:
//(1)尚未到達資料行最大行數(通常這不會到達)
//(2)未遇到 EOF 字元 (按下 Enter 或是 Ctrl+D 會送出 EOF 字元)
//(3)未遇到 new line 字元
for(i = 0 ; (i < lim - 1 && (c = getchar()) != EOF && c != '\n') ; i++)
s[i] = c;
if(c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0'; //「n」與「0」為 string 結束的最後兩個字元
return i;
}
//將 from 陣列的內容複製到 to 陣列 (假設陣列夠大)
void copy(char to[], char from[])
{
int i;
i = 0;
while((to[i] = from[i]) != '\0') //檢查是否到了 string 結束字元
i++;
}
由上面的程式可知道,要印出 string 型態的變數,必須使用「
%s」這個符號,而 C 也會自動將表示 string 結尾的兩個字元給忽略不印。
External Variables and Scope這個部份要談到外部變數(external variables)以及其有效範圍。
在一般的 function 中,變數的有效範圍僅限於該 function 內,每次呼叫相同的 function ,裡面的變數值也不一定會相同;且 function 之間的變數是毫無相關的。
但若是有共同變數的需求呢? 此時就要借用 external variable 的方式來達成了!
其中,最重要的關鍵字即為「
extern」的使用,以下用個範例來說明:
#include <stdio.h>
#define MAXLINE 1000
int max;
char line[MAXLINE];
char longest[MAXLINE];
//修改為使用 external variable 的版本,因此不需要參數了
int getline(void); //void 表示沒有參數(可省略)
int copy(void);
main() {
int len;
//extern int max; //使用 extern 關鍵字,就知道使用的是 external variable
//extern char longest[];
max = 0;
while((len = getline()) > 0) {
if(len > max) {
max = len;
copy();
}
} //end while
if(max > 0)
printf("%s\n", longest);
} //end main
//取得一行資料的內容
int getline(void) {
int c, i;
extern char line[]; //使用 extern 關鍵字,就知道使用的是 external variable
for(i = 0 ; i < MAXLINE - 1 && (c = getchar()) != EOF && c != '\n' ; i++)
line[i] = c;
if(c = '\n') {
line[i] = c;
i++;
}
line[i] = '\0';
return i;
} //end getline
//複製陣列
int copy(void) {
int i;
extern char line[], longest[]; //使用 extern 關鍵字,就知道使用的是 external variable
i = 0;
while((longest[i] = line[i]) != '\0')
i++;
} //end copy
外部變數的使用,有時候固然方便,不過也會因為不小心誤改了變數值而造成其他 function 執行上的錯誤。
就軟體工程的觀念來說,此種方式會讓 function 不夠模組化,因為必須依賴 external variable 才能正常運作,對於 reuse 來說並不會有什麼幫助,因此使用上還是要注意一下。
PS. 其實不使用 extern 關鍵字也是可以通過編譯的! (extern 整行拿掉)一般來說,若有一群 external variable 需要定義被使用,通常會使用額外的檔案來進行變數的定義,然後在程式中把該檔案引用進來;而通常這一類的檔案都稱為 header,副檔名為「
.h」,在檔案一開始就會被 include 進來。
就像每個程式一開始的「
#include <stdio.h>」就是標準的用法,將常用的 I/O function 給引用進來。
參考資料 - The GNU C Library - A.5.2 Range of an Integer Type