# 你所不知道的C語言：指標篇筆記


## 基礎定義

[你所不知道的C語言：指標篇](https://hackmd.io/@sysprog/c-pointer)

物件 (Object) 在 C 語言中指的不是物件導向的 Class，而是「在執行期間，一段可以儲存數值的記憶體區域」。指標就是用來操作這段區域的工具。

「指標」是連接「記憶體 (Memory)」與「物件 (Object)」的橋樑。

**Scalar Type (純量)：** 指標是一種純量。這解釋了為什麼 `ptr++` 不一定只是加 1 (byte)，而是加上「一個單位」。

- **Incomplete Type (不完全型別)：**解釋了為什麼我們可以寫 `struct Node;` (前向宣告) 然後就開始用 `struct Node *ptr`。因為編譯器只要知道這是一個「指標」(固定大小，例如 8 bytes)，它就能工作，不需要知道 struct 裡面的詳細內容。

### 「衍生宣告型別 (Derived Declarator Types)」

這是 C 語言規格書中的術語。簡單來說，C 語言有一些「基本型別」（如 int, char, float），而我們可以在這些基本型別之上，「衍生」出三種變體：

陣列 (Array)

函式 (Function)

指標 (Pointer)

### void *

為什麼這很重要？ 因為電腦記憶體最小單位是 Byte (8-bit)，而 char 正好代表一個 Byte。 所以 memcpy (記憶體複製) 這種函式，雖然是用來搬運任何東西 (int, float, struct...)，但它的參數是用 void *。這意味著在 C 語言眼中，所有資料在底層都可以被視為一連串的 Bytes (char*) 來處理。

### -> vs

古代觀點 (Compiler 很笨)： 在 C 語言剛誕生時（甚至是 C 的前身 B 語言或 BCPL），硬體和編譯器非常原始。

Struct 只是「假象」： 早期的 Struct 不能當作一個完整的物件被搬來搬去（不能直接放在暫存器），它只是一塊記憶體。

*p 的困境： 如果你寫*p，早期的編譯器會試圖把整個 Struct 讀出來，但硬體做不到（因為 Struct 可能很大，暫存器放不下）。所以 (*p).x 在當年是無法翻譯成機器碼的。

-> 的誕生： 為了讓編譯器知道「我不是要讀整個 Struct，我只是要算地址偏移量 (Offset)」，發明了 ->。

意義： p->x 對早期編譯器來說，意思是：「拿 p 的地址，加上 x 的偏移量，然後存取那個位置」。這是一個一步到位的指令，避開了「讀取整個 Struct」的無理要求。

### condition_of

很多現代語言（Java, Python, Go）試圖隱藏指標，覺得它危險。但這段話告訴你：指標不是 C 語言發明的，它是電腦硬體原本的樣子。

極致應用：Linux Kernel 的 list_head
文章最後提到 Linux 核心最經典的設計 struct list_head。

問題： 在 C++ 或 Java 裡，你可以寫 List<Student> 或 List<Car>。但在 C 語言裡沒有 Template/Generic，要怎麼寫一個「萬用」的 Linked List？

Linux 的解法： 侵入式鏈結串列 (Intrusive Linked List)。 不讓 List 去包住資料，而是讓資料去「包含」List。

container_of 的黑魔法： 既然 List 只有 prev 和 next 指標，當我拿到一個 list_head 指標時，我要怎麼知道它是屬於哪個 student 的？ 這就需要用到 指標偏移運算 (Pointer Arithmetic)。

### 指標 (Pointer) 與 型別系統 (Type System) 的硬核知識

1. 為什麼 (********puts) 可以運作？
2. 這是在解釋為什麼 str 和 &str 印出來的位址數值一樣，但它們在編譯器眼中的意義完全不同。

這段在教你如何判斷 const 到底在保護誰。判斷的秘訣是：以 * 為分界線。

宣告寫法,const 在 *的哪邊？,誰不能改？,術語
const int* p,左邊 (修飾 int),內容 (*p = 5 ❌),Pointer to Constant
int const* p,左邊 (修飾 int),內容 (*p = 5 ❌),Pointer to Constant
int* const p,右邊 (修飾 p),指標本身 (p = &b ❌),Constant Pointer
const int * const p,兩邊都有,都不能改,Constant Pointer to Constant

### "\0"

字串本質： C 語言沒有真正的字串型別，只有 C-style string (以 \0 結尾的 char 陣列)。

風險： 為了找出字串結尾，程式必須一直掃描記憶體直到遇到 \0。如果駭客惡意傳入一個沒有結尾的字串，就會導致程式讀到不該讀的記憶體，甚至造成 Buffer Overflow 攻擊。

. Lvalue 的真正含義
誤解： 一般人以為 Lvalue 只是「可以放在等號左邊的值」。

正解 (C99)： Lvalue = Locator Value。

它代表一個 「有名字的、可以定位的記憶體物件 (Object)」。

例如：int a = 10;

a 是 Lvalue (因為它對應到一塊記憶體)。

10 不是 Lvalue (它只是暫時數值，沒地方定位)。

gcc 錯誤訊息： lvalue required as increment operand。意思是 ++ 需要一個有位址的變數來寫入新值，不能對純數值做 ++。

