後端語言
EP.01

C 語言面試必備
指標與記憶體管理

指標基礎、Stack vs Heap、struct、常見面試 Coding 題解析

Joseph Chen

2026
14 min read
大學踩坑紀錄

大學修 C 語言時,指標讓我痛苦了整整一個學期。後來才理解:指標只是一個儲存記憶體地址的變數。搞懂這句話,C 語言就開竅了一半。

C 語言特色

C 語言是最接近硬體的高階語言,1972 年誕生,至今仍是作業系統、嵌入式系統、驅動程式的核心語言。了解 C 語言,等於了解電腦底層如何運作。

特性C 語言Python(對照)
型別系統靜態型別,需宣告型別動態型別,自動推斷
記憶體管理手動 malloc/free自動垃圾回收(GC)
執行方式編譯成機器碼(快)直譯執行(慢 10–100x)
硬體存取可直接操作記憶體地址高度抽象,不直接存取
適合場景作業系統、韌體、驅動程式腳本、資料科學、Web
學習難度高(指標、手動記憶體)低(語法簡潔直觀)

指標(Pointer)基礎

指標是 C 語言最難也最重要的概念。一句話理解:指標是一個儲存記憶體地址的變數。它「指向」另一個變數在記憶體中的位置。

記憶體視覺化

地址: 0x1000

42

int x

地址: 0x2000

0x1000

int *p

p 存的是 x 的地址,不是 x 的。*p 才是「透過地址去讀取值」。

pointer_basics.c
#include <stdio.h>

int main() {
    int x = 42;         // 普通變數:x 存的是值 42
    int *p = &x;        // 指標宣告:p 存的是 x 的「地址」
                        // & 是「取地址運算符」
                        // * 在宣告時代表「這是指標型別」

    printf("x 的值    = %d\n",  x);    // 42
    printf("x 的地址  = %p\n",  &x);   // 0x...(記憶體地址)
    printf("p 的值    = %p\n",  p);    // 與 &x 相同
    printf("*p 的值   = %d\n",  *p);   // 42(* 在使用時代表「解參考」)

    // 透過指標修改原始變數的值
    *p = 100;
    printf("修改後 x  = %d\n",  x);    // 100(x 被改了!)

    return 0;
}

// ── 指標陷阱 ────────────────────────────────────────────
// 1. 野指標(Wild Pointer):未初始化的指標,指向未知記憶體
//    int *bad;
//    *bad = 5;  // !! 未定義行為,程式可能崩潰

// 2. NULL 指標:安全的「空指標」,使用前必須檢查
//    int *safe = NULL;
//    if (safe != NULL) { *safe = 5; }  // 安全

Stack vs Heap 記憶體

C 語言的記憶體分為兩大區域,搞懂它們是理解 C 語言記憶體管理的基礎:

Stack(堆疊)

  • 自動分配、自動釋放
  • 函數內宣告的 local 變數
  • 大小有限(通常 1–8 MB)
  • 速度極快(只是移動 stack pointer)
  • 函數返回後記憶體自動收回

Heap(堆積)

  • 手動 malloc() 分配
  • 手動 free() 釋放(忘記 = 記憶體洩漏)
  • 大小彈性,可動態調整
  • 速度較慢(OS 介入管理)
  • 生命週期跨越函數範圍
heap_memory.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    // ── malloc:分配 n bytes,不初始化(內容是垃圾值)
    int *arr = (int *)malloc(5 * sizeof(int));
    if (arr == NULL) {
        fprintf(stderr, "記憶體分配失敗\n");
        return 1;
    }

    // ── calloc:分配並初始化為 0
    int *zeroed = (int *)calloc(5, sizeof(int));
    // zeroed[0] == 0, zeroed[1] == 0 ...

    // ── realloc:重新調整已分配的大小
    arr = (int *)realloc(arr, 10 * sizeof(int));

    // 使用記憶體
    for (int i = 0; i < 10; i++) arr[i] = i * 2;

    // ── free:釋放記憶體(必做!)
    free(arr);
    free(zeroed);

    // 釋放後把指標設為 NULL,避免「懸空指標(Dangling Pointer)」
    arr = NULL;
    zeroed = NULL;

    // !! 記憶體洩漏(Memory Leak)示範(不要這樣做)
    // int *leak = malloc(100);
    // return 0;  // 沒有 free,這 100 bytes 永遠無法回收
    // 短程式影響不大,但長時間運行的 server/嵌入式系統會耗盡記憶體

    return 0;
}

記憶體洩漏(Memory Leak)

malloc 分配的記憶體若不 free,即使變數超出 scope,記憶體也不會自動回收。在長時間運行的系統(如嵌入式設備、伺服器)裡,累積的洩漏最終會讓系統崩潰。工具:valgrind 可以自動偵測洩漏。

struct(結構體)

struct 是 C 語言的自訂複合型別,把相關的資料打包在一起。C++ 的 class、Python 的 dataclass 概念都源自這裡。

struct_demo.c
#include <stdio.h>
#include <string.h>

// 定義 struct(慣用 typedef,省去每次寫 struct 關鍵字)
typedef struct {
    int   id;
    char  name[50];
    float gpa;
} Student;

// 函數接收 struct 指標(避免複製整個 struct,效率更好)
void print_student(const Student *s) {
    printf("ID: %d, 姓名: %s, GPA: %.2f\n", s->id, s->name, s->gpa);
    //        ↑ 用 -> 存取指標的成員(等同 (*s).id)
}

int main() {
    // 初始化方式 1:指定成員
    Student s1 = { .id = 1, .name = "Joseph", .gpa = 3.8 };

    // 初始化方式 2:直接賦值
    Student s2;
    s2.id = 2;
    strcpy(s2.name, "Alice");  // 字串用 strcpy,不能用 =
    s2.gpa = 3.5;

    // . 存取(直接變數)vs -> 存取(指標)
    printf("s1.id = %d\n", s1.id);          // 用 .
    Student *ptr = &s1;
    printf("ptr->id = %d\n", ptr->id);      // 用 ->

    print_student(&s1);
    print_student(&s2);

    // Heap 上的 struct
    Student *dynamic = (Student *)malloc(sizeof(Student));
    dynamic->id = 3;
    strcpy(dynamic->name, "Bob");
    free(dynamic);

    return 0;
}

常用字串函數速查

C 語言的字串是 char 陣列,以 \0(null terminator)結尾。所有字串函數都在 <string.h>

函數用途注意事項
strlen(s)取得字串長度(不含 \0)返回 size_t,非 int
strcpy(dst, src)複製字串dst 空間必須足夠,危險!建議用 strncpy
strcat(dst, src)字串串接同上,注意緩衝區溢位
strcmp(s1, s2)比較字串相等返回 0,s1 < s2 返回負數
sprintf(buf, fmt, ...)格式化寫入字串類似 printf,但寫到 buffer
strtok(s, delim)切割字串會修改原字串,多執行緒不安全

C vs C++ vs Python 比較

面向CC++Python
OOP不支援完整支援(class)完整支援(class)
記憶體管理手動 malloc/free手動 + RAII (智能指標)自動 GC
執行速度最快接近 C最慢
標準函式庫精簡龐大(STL)龐大(豐富生態系)
適合場景OS/韌體/驅動遊戲引擎/系統軟體腳本/AI/Web

面試常考題

Q1. int *p 和 int p 的差異?

int p 是普通整數變數,儲存一個整數值。int *p 是指標變數,儲存一個記憶體地址(該地址存放 int)。*p 解參考後才能讀取或修改那個地址的值。

Q2. malloc 和 calloc 的差異?

malloc(n) 分配 n bytes,內容不初始化(是垃圾值)。calloc(count, size) 分配 count*size bytes,並將所有 bytes 初始化為 0。calloc 稍慢,但更安全,能避免讀到未初始化值的 bug。

Q3. free 之後還能使用那個指標嗎?

不行。free 後那塊記憶體被歸還給 OS,繼續使用是「懸空指標(Dangling Pointer)」,屬於未定義行為,可能導致程式崩潰或資料被覆寫。習慣:free 之後立刻 ptr = NULL,使用前先檢查 NULL。

Q4. Stack overflow 在什麼情況會發生?

Stack 大小有限(通常 1–8 MB)。最常見原因是無終止條件的遞迴(infinite recursion),每次遞迴都在 Stack 上分配新的 stack frame,最終耗盡 Stack 空間,作業系統發出 SIGSEGV 信號終止程式。

Q5. 如何用 C 實作 swap 函數?

必須用指標。void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; }。若傳值(int a, int b),函數內交換的是副本,呼叫端的值不會改變。這是指標在函數參數的最典型應用。

Q6. const int *p 和 int * const p 的差異?

const int *p(pointer to const int):p 本身可以改變(指向不同地址),但不能透過 p 修改所指的值。int * const p(const pointer to int):p 本身不可改變(固定指向某地址),但可以透過 p 修改所指的值。

上一篇

這是系列第一篇

下一篇

EP.02 — C#

ASP.NET 面試題

C語言
指標
記憶體管理
struct
面試題
EP.01