很多人第一次接觸 C 語言,都會覺得它有點「硬」。

沒有那麼多現成封裝,沒有太多語法糖,很多東西都得自己一步一步寫出來。

但也正因為如此,C 語言特別適合用來打基礎。

它會逼著你去理解:變數到底是什麼,輸入為什麼要傳地址,陣列為什麼能和迴圈配合,函數怎麼傳參,指標又為什麼總是繞不開。

如果這些最基礎的東西真正學明白了,後面無論繼續學 C++、Java、Python,還是接觸作業系統、嵌入式、資料結構,理解都會更紮實。

本文就按照初學者最容易建立認知的順序,把 C 語言的核心基礎串起來,幫你搭起一張完整的知識框架。


一、學 C 語言,先抓住三條主線

剛開始學 C 語言時,最容易犯的錯誤就是把它學成「背單詞」。

今天記住一個 if,明天記住一個 for,後天再去背 scanf、陣列、指標。

看起來學了很多,實際上腦子裡是一盤散沙。

更好的方式,是先抓住三條主線:

第一,資料怎麼存

變數、陣列、結構體、字串,本質上都在解決「資訊放在哪裡」。

第二,程式怎麼控制流程

順序執行、條件分支、迴圈重複,都是在回答「事情怎麼一步一步做」。

第三,程式怎麼找到資料

這會貫穿 scanf、函數傳參、陣列、指標,甚至檔案操作。

只要沿著這三條主線往下學,很多看似零散的知識點都會自然連起來。


二、變數:程式為什麼能「記住」資料

程式要處理資料,首先就得能把資料存起來。

而變數,就是程式裡最基礎的「儲存單元」。

例如:

int age = 18;

這句程式碼表示:定義一個名叫 age 的整型變數,並把值 18 存進去。

這裡有兩個基礎概念必須先弄清楚:識別字資料型態

1. 識別字:給資料起名字

變數名、函數名、陣列名,統稱為識別字。

識別字不是隨便寫的,它要滿足基本規則:

  • 由字母、數字、底線組成
  • 不能以數字開頭
  • 不能和關鍵字重名
  • 區分大小寫

例如:

int score;
int _num;
int age1;

這些寫法都沒問題。

但下面這些就是不合法的:

int 2a;
int if;

所以,學會定義變數的第一步,其實就是學會給資料起一個合法、清晰的名字。

2. 資料型態:決定變數能裝什麼

C 語言不是「隨便拿個變數就能裝任何東西」,每個變數都要先宣告型態。

常見基礎型態有:

int a = 10;       // 整数
float b = 3.14;   // 浮点数
char c = 'A';     // 字符

這意味著變數不僅有名字,還有「這個盒子裡允許裝什麼」的限制。

初學階段一定要建立這個意識,因為它會影響你後面所有的輸入、輸出、運算和記憶體理解。


三、輸入輸出:程式如何和人互動

程式不是只在內部悄悄計算,它還要和人打交道。

最常見的互動方式,就是輸入和輸出。

1. 輸出:把結果顯示出來

C 語言中最常見的輸出函數是 printf

例如:

printf("Hello C!\n");

如果要輸出變數,就需要用到格式控制符:

int age = 18;
printf("%d\n", age);

這裡 %d 表示「按整數格式輸出」。

常見格式說明符有:

  • %d:整數
  • %f:浮點數
  • %c:字元
  • %s:字串

例如:

float price = 19.9;
char ch = 'A';

printf("%f\n", price);
printf("%c\n", ch);

printf 最重要的不是把符號背下來,而是理解:

程式輸出資料時,必須知道要按什麼型態去解釋它。

2. 輸入:為什麼 scanf 經常帶 &

如果說 printf 是「把內容展示出來」,那麼 scanf 就是「把使用者輸入讀進來」。

例如:

int age;
scanf("%d", &age);

這行程式碼的意思是:從鍵盤讀取一個整數,並存到變數 age 中。

有些人可能會有疑問,為什麼這裡寫的是 &age,而不是 age

原因很簡單:

scanf 不是要讀取 age 當前的值,而是要把使用者輸入的內容「寫進 age 所在的位置」。

所以它需要的不是值,而是地址。

也就是說:

  • age 表示變數中的值
  • &age 表示變數在記憶體中的地址

這個 &,其實已經把你帶進了 C 語言最核心的底層思維:

程式不僅處理值,還處理值所在的位置。


四、運算子與運算式:程式到底是怎麼「算」的

變數有了,輸入輸出也有了,接下來程式就要開始處理資料。

而處理資料,離不開運算子和運算式。

1. 算術運算子

最基本的算術運算子有:

+  -  *  /  %

例如:

int a = 10;
int b = 3;

printf("%d\n", a + b);
printf("%d\n", a % b);

其中最值得新手注意的是 /%

整數除法

如果兩邊都是整數:

15 / 4

結果是 3,不是 3.75

因為整數除法只保留整數部分。

浮點除法

只要有一邊是浮點數:

15 / 4.0

結果才會變成 3.75

取模運算

15 % 4

結果是 3,表示餘數。

% 只能用於整數,但它非常實用。

判斷奇偶、拆分數字、處理週期規律時,經常都要用到它。

2. 複合賦值

除了:

a = a + 3;

也可以寫成:

a += 3;

類似地還有:

a -= 3;
a *= 3;
a /= 3;
a %= 3;

這些寫法在迴圈和陣列處理中尤其常見。

3. 邏輯運算

邏輯運算子用於組合多個條件:

  • !:非
  • &&:與
  • ||:或

例如:

if (age >= 18 && age <= 60)
{
    printf("符合条件\n");
}

所以程式並不是只能判斷一個條件,它還可以把多個條件組合起來,一起做出決策。


五、分支:讓程式學會「做選擇」

現實問題裡,很多事情都不是順著往下做,而是「如果滿足某個條件,就執行 A;否則執行 B」。

這就是分支結構的作用。

1. if:最基礎的條件判斷

例如:

if (x > 0)
{
    printf("x是一个正数");
}
else
{
    printf("x不是一个正数");
}

這類程式碼幾乎就是自然語言的直接翻譯:

  • 如果條件成立,就執行前面的程式碼區塊
  • 否則,就執行後面的程式碼區塊

2. 多分支判斷

如果情況更多,可以用 else if 繼續擴展:

if (score >= 90)
    printf("A");
else if (score >= 80)
    printf("B");
else if (score >= 70)
    printf("C");
else if (score >= 60)
    printf("D");
else
    printf("E");

這種結構特別適合處理「分段規則」,比如成績分級、費用計算、年齡劃分等。

3. switch:處理固定值更清晰

如果判斷條件是「某個變數等於哪個固定值」,switch 會更整齊:

switch (grade)
{
case 'A':
    printf("优秀");
    break;
case 'B':
    printf("良好");
    break;
default:
    printf("其他");
}

這裡最關鍵的一點是 break

如果忘了寫,程式會繼續往下執行,出現「貫穿」的效果。


六、迴圈:讓程式學會重複做事

如果分支是「做選擇」,那麼迴圈就是「重複做」。

電腦最擅長的事情之一,就是把同一套規則準確地執行很多遍。

而迴圈,正是這種能力的體現。

1. while:條件成立就一直執行

int i = 1;
while (i <= 5)
{
    printf("%d\n", i);
    i++;
}

這段程式碼會輸出 1 到 5。

while 的特點是:先判斷,再執行。

所以如果條件一開始就是假,迴圈可能一次都不執行。

2. do...while:至少先執行一次

int i = 1;
do
{
    printf("%d\n", i);
    i++;
} while (i <= 5);

while 不同,do...while 是先執行一次,再判斷條件。

3. for:最常用的計數迴圈

for (int i = 1; i <= 5; i++)
{
    printf("%d\n", i);
}

for 把初始化、條件判斷和更新寫在一行裡,結構更緊湊,所以在實際程式設計中非常常見。

4. 迴圈的真正價值

初學時最常見的迴圈任務包括:

  • 求和
  • 統計個數
  • 找最大值和最小值
  • 走訪陣列
  • 輸出某個範圍內滿足條件的數

所以迴圈不是單純的語法範本,而是一種很重要的程式設計思維:

把問題拆成「重複執行同一套步驟」的過程。


七、函數:把一段功能單獨封裝起來

當程式越來越長,所有程式碼都寫在 main 裡就會顯得很亂。

這時就需要函數。

函數可以理解為:

一段專門用來完成某個任務的程式碼區塊,並且可以被重複呼叫。

例如:

int add(int a, int b)
{
    int c = a + b;
    return c;
}

呼叫時寫成:

int sum = add(10, 20);
printf("%d", sum);

1. 函數為什麼重要

函數最核心的價值有兩個:

  • 把複雜程式拆開,讓結構更清晰
  • 把重複邏輯封裝起來,避免反覆寫同樣的程式碼

2. 參數和傳回值

函數中的參數,是把資料傳進去。

傳回值,是把結果帶出來。

這兩個概念看起來簡單,但它們幾乎決定了函數的全部意義。

3. 值傳遞和地址傳遞

這是學函數時非常關鍵的一步。

普通參數傳遞,本質上是把值複製一份給函數。

函數內部即使修改了參數,也不一定會影響外部變數。

如果傳進去的是地址,情況就不同了。

函數可以透過這個地址,直接操作外部變數所在的那塊記憶體。

這也是為什麼函數、scanf 和指標之間會有非常強的聯繫。

4. 陣列作為函數參數

陣列傳參尤其值得注意。

因為陣列作為參數傳入函數時,本質上傳的是首元素地址。

這意味著函數內部对陣列元素的修改,往往會直接影響原陣列。

這件事對理解「地址傳遞」非常重要。


八、陣列:當一個變數不夠用時

如果程式只處理一個數,一個變數就夠了。

但如果要處理 10 個成績、5 個座標、一個班的分數列表,單個變數顯然不夠。

這時就要用陣列。

1. 一維陣列:一組相同型態的資料

int arr[5] = {1, 2, 3, 4, 5};

這表示定義一個包含 5 個整數的陣列。

陣列的本質,可以理解為:

把一組同型態的資料按順序放在一起。

2. 索引從 0 開始

陣列最重要的規則之一,就是索引從 0 開始。

arr[0]   // 第一个元素
arr[1]   // 第二个元素

這個小細節,是初學者最容易犯錯的地方之一。

3. 陣列和迴圈是天然搭檔

陣列的常見操作,幾乎都要和迴圈搭配使用:

for (int i = 0; i < 5; i++)
{
    printf("%d\n", arr[i]);
}

走訪、求和、查找、統計,基本都離不開這個組合。

4. 二維陣列:陣列的陣列

例如:

int arr[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};

二維陣列很適合表示表格、矩陣、成績單這類二維資料。


九、指標:理解 C 語言最關鍵的一步

很多人一聽到指標就緊張。

它確實是 C 語言裡比較抽象的部分,但也是最能體現這門語言底層特性的地方。

1. 指標是什麼

一句話先記住:

指標是存放地址的變數。

例如:

int a = 10;
int *p = &a;

這裡:

  • a 是普通變數
  • &aa 的地址
  • p 是指標變數
  • p 裡存的是 a 的地址

2. p 表示什麼

如果 p 存的是 a 的地址,那麼:

*p

表示透過這個地址,取出對應位置上的值。

例如:

printf("%d\n", *p);

輸出的就是 10

3. 一級指標和二級指標

如果一個指標變數本身也有地址,那麼就可以繼續定義「指向指標的指標」:

int a = 10;
int *p = &a;
int **pp = &p;

雖然看起來繞,但本質上只是「地址也可以繼續被儲存」。

4. 指標和陣列關係很深

例如:

int arr[5] = {10, 20, 30, 40, 50};

訪問第三個元素時:

arr[2]
*(arr + 2)

在很多場景中是等價的。

這說明陣列名和地址之間存在非常緊密的聯繫,而這正是理解陣列、函數傳參和指標運算的關鍵。


十、結構體、字串和預處理

當前面的基礎都打穩以後,C 語言就會慢慢從「練語法」過渡到「表達真實物件」。

1. 結構體:把多個屬性組合到一起

例如:

struct Person
{
    char name[10];
    int age;
    float height;
};

這意味著一個「人」可以同時擁有姓名、年齡、身高等多個屬性。

訪問結構體成員時,常用寫法是:

person.age

如果是結構體指標,則要寫成:

p->age

結構體的重要意義在於,它讓程式不只是處理單個值,而是開始處理「一個物件」。

2. 字串

在 C 語言中,字串其實不是獨立資料型態,而是字元陣列。

例如:

char str[] = "hello";

它的本質是若干字元連續存放,並以 \0 作為結束標記。

理解這一點,對後面學習字串長度、字元陣列空間、字串處理函數都非常重要。

3. #define:編譯前替換

例如:

#define PI 3.14

這不是變數,而是預處理階段進行的文字替換。

入門階段知道這一點就夠了:

它的核心不是「存資料」,而是「在編譯前先替換程式碼內容」。


十一、檔案:讓程式和外部資料打交道

學到這裡,程式就不只是和鍵盤、螢幕打交道了。

它還可以讀取和寫入檔案。

1. 檔案操作的基本入口

在 C 語言中,檔案通常透過檔案指標來操作。

例如:

FILE *fp = fopen("output.txt", "w");

這表示以寫入方式開啟一個檔案。

2. 常見開啟方式

常見模式有:

  • "r":唯讀
  • "w":只寫
  • "a":追加
  • "rb" / "wb":二進位方式讀寫
  • +:讀寫結合

開啟方式決定了程式能對檔案做什麼。

3. 常見檔案讀寫函數

例如:

  • fgetc / fputc
  • fgets / fputs

從理解上說,檔案操作和前面的輸入輸出本質是一致的:

只不過輸入輸出的物件,從鍵盤和螢幕,變成了磁碟上的檔案。


十二、一張完整的圖

以上內容是一條逐層展開的路徑:

  • 從變數開始,學會存資料
  • 用輸入輸出和程式建立互動
  • 用運算子處理資料
  • 用分支和迴圈控制流程
  • 用函數拆分任務
  • 用陣列處理一批資料
  • 用指標理解地址和記憶體
  • 再擴展到結構體、字串、檔案這些更真實的應用場景

如果把 C 語言基礎看作蓋房子,那麼變數、輸入輸出、分支、迴圈、函數、陣列、指標,就是一層一層往上搭起來的結構。

單獨看,每個知識點都不算特別複雜。但只有把它們融會貫通,你才會真正從「會寫幾個例子」,走到「開始理解程式設計」。

這也是為什麼很多人覺得 C 語言難,但真正學進去之後,又會覺得它特別基礎。

因為它不是在教你「怎麼拼程式碼」,而是在教你:

電腦程式到底是如何運作的。