首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C語言指針進階:指針函數 vs 函數指針、二級指針、指針數組指針完全圖解

     編輯:關於C語言

前言

「指針函數」與「函數指針」一字之差、天壤之別;二級指針、指針數組指針更是 C 語言指針體系的皇冠。本文通過**語法拆解、內存佈局圖解、實戰代碼模式**,徹底澄清這四大核心概念,建立不可動搖的指針心智模型。

一、指針函數 vs 函數指針:本質區別

維度 指針函數 函數指針
本質 **函數** (返回指針) **指針變量** (指向函數)
聲明語法 返回類型 *函數名(參數表);
int *func(int x, int y);
返回類型 (*指針名)(參數表);
int (*func)(int x, int y);
核心區別 * **不在括號內**,結合到返回類型 * **在括號內**,結合到標識符 (改變優先級)
用途 返回動態分配內存、靜態數組元素地址、鏈表節點等 回調函數、策略模式、狀態機、動態分發、qsort 比較器
調用方式 int *p = func(a, b); func = &real_func; func(a, b); 或 (*func)(a, b);

1.1 指針函數實戰:返回二維數組行指針


int *GetDate(int wk, int dy) {
    static int calendar[5][7] = { ... }; // 靜態存儲期,生命周期全程
    return &calendar[wk-1][dy-1];        // 返回元素地址
}

int main() {
    int *p = GetDate(2, 3);
    printf("%d\n", *p); // 解引用獲取值
}

關鍵:返回指針指向的內存**必須在函數返回後仍有效** (靜態、全局、堆分配)。**嚴禁返回局部變量地址** (棧內存釋放)。

1.2 函數指針實戰:菜單驅動/策略模式


void FileFunc() { puts("File"); }
void EditFunc() { puts("Edit"); }

int main() {
    void (*funcp)(); // 聲明函數指針,指向無參無返回值函數
    funcp = FileFunc; // 賦值 (函數名隱式轉為地址)
    funcp();          // 調用
    
    funcp = EditFunc;
    (*funcp)();       // 顯式解引用調用,語義更清晰
}

二、二級指針 (**ptr):修改指針本身的鑰匙

2.1 內存佈局


char c = 'A';
char *p = &c;       // 一級指針:指向 char
char **cp = &p;     // 二級指針:指向 char*

// 訪問鏈路:
// cp      → p 的地址
// *cp     → p (char*)
// **cp    → c ('A')

2.2 核心用途 1:函數內修改實參指針 (輸出參數)


void FindCredit(int **fpp) {
    while (**fpp != 0) {
        if (**fpp < 0) break;
        else (*fpp)++; // 修改實參指針 p 本身 (使其指向下一個元素)
    }
}

int main() {
    int vals[] = {7, 6, 5, -4, 3, 2, 1, 0};
    int *fp = vals;       // fp 指向數組首元素
    FindCredit(&fp);      // 傳遞 fp 的地址
    printf("%d\n", *fp);  // 輸出 -4 (fp 已被函數移動到 -4 處)
}

關鍵:(*fpp)++ 先解引用獲取 fp,再遞增 fp。**括號不可省** (優先級:* 高於 ++)。

2.3 核心用途 2:動態分配二維數組/字符串數組


int **matrix = malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++)
    matrix[i] = malloc(cols * sizeof(int));
// 釋放時逆序
for (int i = 0; i < rows; i++) free(matrix[i]);
free(matrix);

三、指向指針數組的指針:字符串數組遍歷的標準模式

3.1 字符串數組與指針數組


char *Names[] = {
    "Bill", "Sam", "Jim", "Paul", "Charles", NULL // 以 NULL 結束
};
// Names 類型:char*[6] → 退化為 char** (指向指針數組首元素的指針)

3.2 標準遍歷模式


char **nm = Names; // nm 指向第一個 char* (即 "Bill")
while (*nm != NULL) {
    printf("%s\n", *nm); // *nm 是 char*,%s 打印字符串
    nm++;                // 移動到下一個 char* (即 "Sam")
}

3.3 關鍵語法 *nm++

  • *nm++ = *(nm++) (後綴 ++ 優先級高於 *)。
  • 效果:**先取值** *nm (當前字符串指針) 用於打印,**再遞增** nm 指向下一個指針。
  • 等價於:printf("%s\n", *nm); nm++;。

3.4 空指針作為哨兵

  • 數組以 NULL (或 0) 結尾,循環條件 *nm != NULL。
  • 優勢:動態增刪字符串時,**無需修改遍歷代碼**,只需保證末尾有 NULL。

四、綜合辨析速查表

聲明 讀法 類型 典型用途
int *func(); 函數 func 返回 int* 指針函數 工廠函數、查找返回地址
int (*func)(); 指針 func 指向返回 int 的函數 函數指針 回調、策略、虛函數表
int **pp; 指向 int* 的指針 二級指針 修改指針實參、二維動態數組
char **argv; 指向字符串數組的指針 指針數組指針 main 參數、字符串列表遍歷
int (*p)[N]; 指向含 N 個 int 數組的指針 數組指針 二維數組行指針、矩陣操作

五、避坑指南

  1. ❌ int *p = func(); *p = 10; 若 func 返回局部變量地址 → UB。✅ 靜態/堆/全局內存。
  2. ❌ void (*fp)() = func(); 調用了 func。✅ void (*fp)() = func; (無括號)。
  3. ❌ void **pp = &p; 若 p 類型不匹配 (如 int*) → 編譯警告/運行時錯誤。✅ 類型嚴格匹配。
  4. ❌ char **p = 2D_array; 二維數組不可直接轉為雙重指針。✅ char (*p)[COLS] = 2D_array;。
  5. ✅ 讀複雜聲明用 **「右左法則」 (Clockwise/Spiral Rule)**:從標識符出發,先右後左,遇括號轉向。

結語

指針函數**返回地址**,函數指針**存儲地址**;二級指針**修改一級指針**,指針數組指針**遍歷字符串列表**。掌握聲明語法的優先級規則 (括號改變結合性)、內存佈局的指向關係、以及典型應用場景 (回調/輸出參數/動態數組/命令行參數),即可駕馭 C 語言指針體系的最高難度。**畫圖、寫最小測試代碼驗證、用右左法則讀聲明**,是攻克指針迷宮的三把鑰匙。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved