前言
電腦程式大部分的工作都和資料處理相關。在程式語言中,變數 (variable) 相當於資料的標籤,我們可透過變數來操作資料。在本文中,我們介紹在 C 語言中使用變數的方式。
實字 (Literal)
實字是指直接寫死在程式裡的資料。在教學用範例程式裡時常會看到實字,因為從外界讀取資料的程式會比較複雜,直接使用程式內部寫好的資料則簡單得多。以下是一些實字的例子:
12345
(整數)123.456
(浮點數)"Hello World"
(C 字串){1, 2, 3, 4, 5}
(整數陣列)
宣告變數
C 語言的變數包含四個要件:
- 資料型別 (data type)
- 識別字 (identifier)
- 值 (value)
- 可視度 (scope)
絕大部分的程式語言都有資料型別的概念。資料型別用來規範資料在程式中合理的操作。我們在前一篇文章介紹過資料型別,此處不詳述。
在電腦程式中,編譯器或直譯器透過識別字來了解程式碼的意義。有些識別字是內建的,像是保留字 (keywords)、內建變數、內建函式、標準函式庫的函式等。有些識別字是程式設計者宣告後創造出來的,像是變數。
變數是資料的標籤,而非資料本身。電腦程式很大一部分是在操作資料,變數在本質上是用來操作資料的一種語法特性。
以下變數 i
的型別是 int
,值為 3
:
int i = 3;
以下變數 s
的型別是指向 char
的指標,值為 C 字串 "Hello World"
:
char *s = "Hello World";
我們會在後續文章中介紹 C 語言的字元和字串,這裡先觀察一下變數的宣告方式即可。
使用變數
我們以簡短的例子來看如何使用變數:
#include <assert.h> /* 1 */
#include <stdio.h> /* 2 */
int main(void) /* 3 */
{ /* 4 */
int a = 4; /* 5 */
int b = 3; /* 6 */
printf("4 + 3 = %d\n", a + b); /* 7 */
printf("4 - 3 = %d\n", a - b); /* 8 */
assert(a == 4); /* 9 */
assert(b == 3); /* 10 */
return 0; /* 11 */
} /* 12 */
我們在第 5 行和第 6 行宣告整數型態變數 a
和另一個整數型態變數 b
,並分別對兩變數賦值。變數對於 C 程式來說,是由程式設計者自行定義的,所以要先宣告才能使用。
接著,我們在第 7 行和第 8 行使用兩變數進行加法和減法運算,並將運算當成參數結果傳入 printf()
函式。實際的效果是在終端機印出運算結果。
最後,我們在第 9 行和第 10 行用 assert()
巨集確認變數 a
和變數 b
在運算過程中都沒有實質的變化。因為我們沒有把運算的結果回存到 a
或 b
上。
在這個簡短的例子中,我們重覆使用 a
和 b
多次。其實這兩個變數本身只是一個符號,我們透過這兩個變數操作整數形態資料 4
和 3
。
宣告常數 (Constant)
我們在宣告變數後,無法預防變數遭到預期外的修改。若確認某變數的值在整個程式中不會變化,可以加上 const
保留字來修飾該變數,這時候該變數視為常數。我們若在程式中修改常數,C 編譯器會引發錯誤,阻止程式編譯,藉此提醒程式設計者該修正程式碼了。
參考以下反例:
const double pi = 3.1415927;
pi = 4.0; // Error!
我們宣告了型別為 double
的變數 pi
,由於我們預期 pi
的值是定值,故加上 const
修飾 pi
。事後我們意圖修改 pi
的值時,引發該程式的錯誤,這時候我們就知道程式碼出問題了。
由於 C 標準函式庫 math.h 已經定義了較精準的 pi 常數 M_PI
,我們不應該另行宣告一個常數 pi。這裡只是展示 const
的用法。
合理的變數名稱
變數命名有固定的條件:
- 第一個字元是字母或底線
- 第二個以後的字元是字母、底線或數字
由於 C 語言會把一些新的保留字用首字底線的方式來命名,像 _Bool
,目前不建議用首字底線來命名變數。除此之外,變數名稱有一些風格上的建議,詳見下一節。
變數的命名風格
撰碼風格 (coding style) 是讓程式碼易讀的一種撰碼方式,這是建議事項,而非強制的。變數命名風格也是撰碼風格的一環。以下是常見的變數命名風格:
PascalCase
camelCase
snake_case
Pascal case 在 C 語言甚少使用。但可考慮用來命名結構體 (structure) 或聯合體 (union),因為結構體或聯合體在概念上接近類別 (class)。
Camel case 在 C 語言也少用,這種風格主要在 Java 或 C# 圈子中流行。
C 語言常用 snake case 或是縮寫來命名變數或函式。像是 isalnum() 函式用來檢查某個字元是否為字串或數字。由 gtk_init() 的名稱可知,這是一個用來初始化的函式,並在函式名稱使用 gtk_
前綴來模擬命名空間 (namespace)。
保留字 (Keywords)
保留字在程式碼中有特別的意義,故不能用來命名變數。這裡有一份 C 語言保留字的清單。原本的 C 語言使用小寫字當成保留字,現代 C 語言為了保持現有程式的相容性,新的保留字改以首字底線來命名,再加上選擇性的巨集宣告。
不要刻意背誦保留字,要在學習程式設計的過程中自然地學會。此外,好的程式碼編輯器會用顏色提示保留字所在的地方,我們可以很容易地看到保留字所在的位置。
可視度 (Scope)
可視度是一個隱含在程式碼中的概念。由小至大分為以下數種:
- 區塊可見 (block scope)
- 函式可見 (function scope)
- 檔案可見 (file scope)
- 全域可見 (global scope)
例如,下列整數型態變數 n
的可視度是函式可見:
int main(void)
{
int n = 3;
/* Use `n` here. */
return 0;
}
下列無號整數型態變數 i
的可視度則是區塊可見:
int main(void)
{
{
size_t i;
for (i = 0; i < 10; ++i) {
/* Repeat something here. */
}
}
/* `i` is not visible here. */
return 0;
}
實際上,上述範例程式碼是在 ANSI C 中使用 for
迴圈的計數器的小技巧。在 C99 中則會改寫如下:
int main(void)
{
for (size_t i = 0; i < 10; ++i) {
/* Repeat something here. */
}
/* `i` is not visible here. */
return 0;
}
剛開始學習 C 語言的時候,程式碼很簡短,不會感受到可視度的重要。當程式變長後,可視度的重要性會慢慢浮現出來。簡單的原則是僅開放最低限度的可視度,最好不要使用全域變數。