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

C 語言指針進階:下標轉指針、指向指針的指針與實戰應用

     編輯:Java 相關知識

前言

指針是 C 語言的靈魂,也是無數開發者的「分水嶺」。本文通過三個遞進案例,徹底講透 二維數組下標轉指針訪問、雙重指針 與 函數中修改指針指向 的核心機制,並給出記憶體佈局圭解。

案例一:二維數組「下標轉指針」訪問

代碼

#include <stdio.h>

int main() {
    int a[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
    
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            // a[i][j] 等價寫法
            printf("%d ", *(*(a + i) + j));
        }
        printf("\n");
    }
    return 0;
}

記憶體佈局與推導


a        → 首行首元素地址 (int (*)[3]) 類型:指向含 3 個 int 的數組的指針
a + i    → 第 i 行首地址,類型仍為 int (*)[3],偏移 i * sizeof(int[3])
*(a + i) → 解引用,得到第 i 行數組,退化為 int* 指向行首元素
*(a + i) + j → 指向第 i 行第 j 個元素
*(*(a + i) + j) → 解引用取值,即 a[i][j]

關鍵結論

  • a 是「行指針」,步長 = 一行大小(3 * sizeof(int))。
  • *(a+i) 使類型從「行指針」降級為「元素指針」。
  • 二維數組在記憶體中連續存儲,行優先。

案例二:指向指針的指針

代碼

#include <stdio.h>

int main() {
    int a = 1;
    int *p = NULL;      // 一級指針
    int **pp = NULL;    // 二級指針
    
    pp = &p;            // pp 指向 p 的地址
    *pp = &a;           // 解引用 pp 得到 p,賦值 &a  → 等價於 p = &a
    
    printf("%d\n", **pp); // 輸出 1
    return 0;
}

記憶體圖解


內存地址    變量      值
0x7ffe...   a         1
0x7ffe...   p     →   0x7ffe... (a 的地址)
0x7ffe...   pp    →   0x7ffe... (p 的地址)

**pp = *(*pp) = *(p) = a = 1

案例三:實戰——函數中重置動態記憶體(模擬引用傳遞)

場景

C 語言只有值傳遞。若需在函數內修改主調函數的指針指向(如 realloc 失敗後重新分配、縮容),必須傳遞 指針的指針。

代碼

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
 * 重置緩衝區大小
 * @param p 指向緩衝區指針的指針
 * @param old_size 原大小
 * @param new_size 新大小
 * @return 1 成功,0 失敗
 */
int reset_buffer(char **p, int old_size, int new_size) {
    if (!p || new_size <= 0) return 0;
    
    char *new_buf = (char*)malloc(new_size);
    if (!new_buf) return 0; // 分配失敗,原緩衝區不變
    
    int copy_len = (old_size < new_size) ? old_size : new_size;
    if (*p) {
        memcpy(new_buf, *p, copy_len); // 複製舊數據
        free(*p);                      // 釋放舊緩衝區
    }
    
    *p = new_buf; // 關鍵:修改主調函數的指針指向
    return 1;
}

int main() {
    char *buf = (char*)malloc(5);
    strcpy(buf, "hello");
    printf("Before: %p -> %s\n", (void*)buf, buf);
    
    if (reset_buffer(&buf, 5, 10)) {
        printf("After expand: %p -> %s\n", (void*)buf, buf);
    }
    
    if (reset_buffer(&buf, 10, 3)) {
        printf("After shrink: %p -> %s\n", (void*)buf, buf);
    }
    
    free(buf);
    return 0;
}

輸出示例


Before: 0x55a1b2c0 -> hello
After expand: 0x55a1b2e0 -> hello
After shrink: 0x55a1b300 -> hel

核心模式總結

需求參數類型函數內操作主調傳遞
修改指針指向的內容T**p = val&var
修改指針本身指向T***p = new_addr&ptr
修改二維數組/指針數組T** 或 T(*)[N]*(*p+i)+j數組名/指針數組

避坑指南

  1. 二級指針解引用順序: *pp = &a 先解引用 pp 得到 p,再賦值。切勿寫成 **pp = a(這是修改 a 的值)。
  2. const 正確性: const char **pp vs char *const *pp vs char **const pp,分別限制不同層級可修改性。
  3. 釋放後置空: free(*p); *p = NULL; 防止野指針。
  4. 分配失敗保護: 先分配新記憶體、複製數據、釋放舊記憶體、最後修改指針,保證異常安全。

總結

  • 二維數組名 是行指針,*(a+i)+j 是訪問元素的通用指針寫法。
  • 雙重指針 實現「修改指針指向」的間接訪問,是 C 模擬引用傳遞的核心技巧。
  • 動態記憶體重置 必須用 T**,並遵循「先分配新、再釋放舊、最後切換」原則。

熟練掌握指針層級轉換,是寫出健壯、高效 C 代碼的關鍵門檻。

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