C 語言採用值傳遞:函數參數為實參副本。若需在函數內修改呼叫端變數,必須傳遞其位址(指標)。若需修改指標本身(如分配記憶體並回傳),則需傳遞指標的指標(二級指標)。
void swap(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
int main() {
int m = 5, n = 10;
printf("m=%d, n=%d\n", m, n);
swap(&m, &n);
printf("m=%d, n=%d\n", m, n); // 輸出 m=10, n=5
return 0;
}
傳遞位址 &m, &n,函數通過解引用修改原變數。
不希望函數修改數據時,用 const 保護:
void passconstant(const int *num1, int *num2) {
*num2 = *num1; // OK:讀取 num1,修改 num2
// *num1 = 100; // 編譯錯誤:嘗試修改 const 數據
}
int main() {
const int a = 100;
int b = 5;
passconstant(&a, &b);
printf("a=%d, b=%d\n", a, b); // a=100, b=100
return 0;
}
| 特性 | 棧 | 堆 |
|---|---|---|
| 分配方式 | 編譯器自動 | 程式設計師手動 |
| 釋放方式 | 自動(函數返回) | 手動 free,否則洩漏 |
| 生存週期 | 函數執行期間 | 手動釋放前持續有效 |
| 大小限制 | 較小(Windows 典型 1-2MB) | 受限於虛擬記憶體,較大 |
| 分配效率 | 極快 | 較慢,易產生碎片 |
| 存儲內容 | 區域變數、參數、返回位址 | 動態分配物件 |
// 錯誤示範
int *allocateArray(int size, int value) {
int arr[size]; // 區域陣列,棧上分配
for (int i = 0; i < size; i++) arr[i] = value;
return arr; // 返回棧地址,函數返回後失效!
}
正確做法 1:動態分配 + 呼叫端釋放
int *allocateArray(int size, int value) {
int *arr = (int *)malloc(size * sizeof(int));
for (int i = 0; i < size; i++) arr[i] = value;
return arr; // 堆記憶體,持續有效
}
int main() {
int *vec = allocateArray(5, 45);
for (int i = 0; i < 5; i++) printf("%d\n", vec[i]);
free(vec); // 呼叫端負責釋放
return 0;
}
正確做法 2:呼叫端分配,函數填充
int *allocateArray(int *arr, int size, int value) {
if (arr) for (int i = 0; i < size; i++) arr[i] = value;
return arr; // 為鏈式調用返回指標
}
int main() {
int *vec = (int *)malloc(5 * sizeof(int));
allocateArray(vec, 5, 45);
// 使用 vec...
free(vec);
return 0;
}
函數內分配記憶體並將指標回傳給呼叫端:
void allocateArray(int **arr, int size, int value) {
*arr = (int *)malloc(size * sizeof(int));
if (*arr) for (int i = 0; i < size; i++) *(*arr + i) = value;
}
int main() {
int *vec = NULL;
allocateArray(&vec, 5, 45);
for (int i = 0; i < 5; i++) printf("%d\n", vec[i]);
free(vec);
return 0;
}
關鍵:&vec 傳遞指標位址,函數通過 *arr 解引用修改原指標值。
遞歸函數需修改父節點的 lchild/rchild 指標,故須傳遞二級指標:
typedef struct Node {
char ch;
struct Node *lchild, *rchild;
} Node, *BTree;
void CreateTree(Node **pTree) {
char ch;
scanf(" %c", &ch);
if (ch == '#') {
*pTree = NULL;
} else {
*pTree = (Node *)malloc(sizeof(Node));
(*pTree)->ch = ch;
CreateTree(&(*pTree)->lchild); // 傳遞左子指標位址
CreateTree(&(*pTree)->rchild); // 傳遞右子指標位址
}
}
int main() {
BTree root = NULL;
CreateTree(&root); // 傳遞根指標位址
// 後續遍歷...
return 0;
}
&(*pTree)->lchild 即「指向當前節點左子指標的指標」,遞歸層層傳遞,最終在葉子位置建立節點並掛鏈到父節點。
const 表達「只讀」語意,編譯器協助防錯指標傳遞是 C 語言核心機制,掌握其層級關係與記憶體語意,能寫出正確、高效、安全的系統級代碼。