指針被譽為 C 語言的靈魂,也是初學者最容易感到棘手的知識點。本文將系統梳理 C 語言指針的安全聲明、初始化規範,以及常見的使用陷阱與防範對策,助你寫出更健壯、高效的代碼。
看似簡單的聲明暗藏玄機:
int* ptr1, ptr2; // ptr1 是指針,ptr2 卻是整數
正確做法是為每個變量單獨加上星號,或使用 typedef 別名:
int* ptr1, *ptr2;
使用 typedef 而非宏定義,能讓編譯器進行作用域檢查,更加安全:
typedef int* PINT; PINT ptr1, ptr2; // 兩者皆為指針
未初始化的指針包含隨機垃圾地址,解引用會導致未定義行為:
int* p;
printf("%d\n", *p); // 極度危險
int *pi = NULL;if (pi != NULL)assert(pi != NULL); 在調試階段捕獲空指針緩衝區溢出 是指寫入數據超出分配容量,覆蓋合法數據。堆棧作為進程間指令的臨時存儲區,同樣面臨溢出風險。常見誘因包括:未檢查數組下標、指針算術運算失誤、使用
gets、誤用strcpy/strcat等不安全函數。
float *vector = malloc(20 * sizeof(float));
if (vector == NULL) {
// 處理分配失敗
} else {
// 正常使用 vector
}
正確初始化:
int num; int *pi = #
錯誤示範(企圖對未初始化指針賦值地址):
int num; int *pi; *pi = # // 錯誤:pi 未指向有效內存
指向已釋放內存或超出作用域對象的指針。詳細請參考《C迷途指針》專題。
C 語言不進行邊界檢查,以下代碼演示了越界寫入對相鄰數組的破壞:
#include <stdio.h>
int main() {
char firstName[8] = "1234567";
char middleName[8] = "1234567";
char lastName[8] = "1234567";
middleName[-2] = 'X'; // 破壞 firstName
middleName[10] = 'X'; // 破壞 lastName
printf("%p %s\n", firstName, firstName);
printf("%p %s\n", middleName, middleName);
printf("%p %s\n", lastName, lastName);
return 0;
}
運行結果直觀展示了內存佈局與越界後果:

內存分配示意圖:

數組作為參數傳遞時會退化為指針,必須額外傳遞長度以防越界:
#include <stdio.h>
void replace(char buffer[], char replacement, size_t size) {
size_t count = 0;
while (*buffer && count++ < size) {
*buffer = replacement;
buffer++;
}
}
int main() {
char name[8];
strcpy(name, "Alexander"); // 本身已溢出
replace(name, '+', sizeof(name));
printf("%s\n", name);
return 0;
}
sizeof 計算元素個數對指針用 sizeof 只能得到指針大小,而非數組長度:
int buffer[20];
int *pbuffer = buffer;
for (int i = 0; i < sizeof(buffer); i++) // 錯誤:循環 20*sizeof(int) 次
*(pbuffer++) = 0;
正確寫法:i < sizeof(buffer) / sizeof(int)
C 語言無內建有界指針,需手動實現邊界檢查:
#define SIZE 32
char name[SIZE];
char *p = name;
if (p >= name && p < name + SIZE) {
// 有效區域
}
進階:封裝驗證函數(Linux 下利用 _etext 符號判斷代碼段地址):
#include <stdio.h>
#include <stdlib.h>
int valid(void *p) {
extern char _etext;
return (p != NULL) && ((char*)p > &_etext);
}
int global;
int main(void) {
int local;
printf("pointer to local var valid? %d\n", valid(&local));
printf("pointer to static var valid? %d\n", valid(&global));
printf("pointer to function valid? %d\n", valid((void*)main));
int *p = (int*)malloc(sizeof(int));
printf("pointer to heap valid? %d\n", valid(p));
printf("pointer to end of allocated heap valid? %d\n", valid(++p));
free(--p);
printf("pointer to freed heap valid? %d\n", valid(p));
printf("null pointer valid? %d\n", valid(NULL));
return 0;
}
Linux 運行結果:
pointer to local var valid? 1
pointer to static var valid? 1
pointer to function valid? 0
pointer to heap valid? 1
pointer to end of allocated heap valid? 1
pointer to freed heap valid? 1
null pointer valid? 0
另可借助 ANSI-C 邊界檢查工具(如 CBMC)進行靜態分析。