C語(yǔ)言學(xué)習(xí)7-測(cè)試指針和地址

C語(yǔ)言學(xué)習(xí)1-數(shù)組、字符串
C語(yǔ)言學(xué)習(xí)2-指針的使用、const 指針
C語(yǔ)言學(xué)習(xí)3-指針和數(shù)組
C語(yǔ)言學(xué)習(xí)4-結(jié)構(gòu)體
C語(yǔ)言學(xué)習(xí)5-函數(shù)

一、指針基礎(chǔ):理解地址與數(shù)據(jù)

1.1 什么是指針?

指針 = 內(nèi)存地址,指針變量是存儲(chǔ)內(nèi)存地址的變量。

#include <stdio.h>

int main() {
    int x = 5;        // 在內(nèi)存中分配一個(gè)整數(shù)空間
    int *px = &x;     // px存儲(chǔ)x的內(nèi)存地址
    
    printf("=== 基礎(chǔ)指針演示 ===\n");
    printf("x的值: %d\n", x);           // 5
    printf("x的地址: %p\n", &x);        // 0x7ff7bfeff374
    printf("px的值: %p\n", px);         // 0x7ff7bfeff374
    printf("px的地址: %p\n", &px);      // 0x7ff7bfeff368
    printf("*px的值: %d\n", *px);       // 5
    
    return 0;
}

1.2 內(nèi)存布局示意圖

內(nèi)存地址布局圖:

+-------------------+     指向      +-------------------+
| 指針變量 px        | -----------> | 整型變量 x        |
| 地址: 0x368       |              | 地址: 0x374       |
| 值: 0x374         |              | 值: 5            |
+-------------------+              +-------------------+

訪問(wèn)路徑說(shuō)明:
- &x  → 獲取x的地址:0x374
- px  → px存儲(chǔ)的值:0x374  
- *px → 訪問(wèn)地址0x374的內(nèi)容:5

圖解說(shuō)明:

  • 每個(gè)變量都在內(nèi)存中有自己的地址
  • 指針變量存儲(chǔ)的是其他變量的地址
  • 通過(guò) *指針 可以訪問(wèn)指針指向的數(shù)據(jù)

二、通過(guò)指針修改內(nèi)存數(shù)據(jù)

2.1 修改指針指向的值

#include <stdio.h>

int main() {
    int x = 5;
    int *px = &x;
    
    printf("修改前: x = %d, *px = %d\n", x, *px);  // 都是5
    
    *px = 10;  // 通過(guò)指針修改x的值
    
    printf("修改后: x = %d, *px = %d\n", x, *px);  // 都是10
    
    return 0;
}

2.2 修改過(guò)程的內(nèi)存變化圖

修改前內(nèi)存狀態(tài):

+-------------------+     指向      +-------------------+
| 指針 px           | -----------> | 變量 x           |
| 地址: 0x368       |              | 地址: 0x374       |
| 值: 0x374         |              | 值: 5            |
+-------------------+              +-------------------+

執(zhí)行 *px = 10 后:

+-------------------+     指向      +-------------------+
| 指針 px           | -----------> | 變量 x           |
| 地址: 0x368       |              | 地址: 0x374       |
| 值: 0x374         |              | 值: 10 ← 被修改!  |
+-------------------+              +-------------------+

關(guān)鍵理解:

  • px 存儲(chǔ)的是地址,這個(gè)地址不變
  • *px 操作的是該地址指向的內(nèi)存內(nèi)容
  • 修改 *px 就是修改原始變量 x 的值

三、多級(jí)指針(指針的指針)

3.1 二級(jí)指針詳解

#include <stdio.h>

int main() {
    int x = 5;
    int *px = &x;      // 一級(jí)指針
    int **pxx = &px;   // 二級(jí)指針
    
    printf("x = %d\n", x);           // 5
    printf("px = %p\n", px);         // x的地址
    printf("*px = %d\n", *px);       // 5
    printf("pxx = %p\n", pxx);       // px的地址  
    printf("*pxx = %p\n", *pxx);     // x的地址
    printf("**pxx = %d\n", **pxx);   // 5
    
    return 0;
}

3.2 多級(jí)指針內(nèi)存關(guān)系圖

三級(jí)指針關(guān)系圖:

+----------------+    指向    +----------------+    指向    +----------------+
| 三級(jí)指針 pxx   | --------> | 二級(jí)指針 px    | --------> | 一級(jí)變量 x     |
| 地址: 0x360    |           | 地址: 0x368    |           | 地址: 0x374    |
| 值: 0x368      |           | 值: 0x374      |           | 值: 5          |
+----------------+           +----------------+           +----------------+

訪問(wèn)路徑分解:
pxx     = 0x368    (px的地址)
*pxx    = 0x374    (px的值,即x的地址)
**pxx   = 5        (x的值)

&pxx    = 0x360    (pxx自己的地址)
&px     = 0x368    (px自己的地址)  
&x      = 0x374    (x自己的地址)

3.3 通過(guò)多級(jí)指針修改變量

#include <stdio.h>

int main() {
    int x = 5;
    int *px = &x;
    int **pxx = &px;
    
    printf("初始: x = %d\n", x);      // 5
    
    // 三種方式修改同一個(gè)變量
    x = 10;
    printf("直接修改: x = %d\n", x);  // 10
    
    *px = 20;  
    printf("通過(guò)px: x = %d\n", x);    // 20
    
    **pxx = 30;
    printf("通過(guò)pxx: x = %d\n", x);   // 30
    
    return 0;
}

修改過(guò)程圖解:

所有修改都作用于同一內(nèi)存位置:

+----------------+    指向    +----------------+    指向    +----------------+
| pxx            | --------> | px             | --------> | x              |
| 值: 0x368      |           | 值: 0x374      |           | 值: 30 ← 最終值 |
+----------------+           +----------------+           +----------------+
        ↑                           ↑                           ↑
        |                           |                           |
     **pxx=30                    *px=20                      x=10
    (第三層修改)                (第二層修改)                (第一層修改)

四、指針的大小與系統(tǒng)架構(gòu)

4.1 指針大小測(cè)試

#include <stdio.h>

int main() {
    printf("=== 指針大小測(cè)試 ===\n");
    printf("char* 大小: %zu 字節(jié)\n", sizeof(char*));
    printf("int* 大小: %zu 字節(jié)\n", sizeof(int*)); 
    printf("double* 大小: %zu 字節(jié)\n", sizeof(double*));
    printf("void* 大小: %zu 字節(jié)\n", sizeof(void*));
    printf("int** 大小: %zu 字節(jié)\n", sizeof(int**));
    
    return 0;
}

4.2 指針大小示意圖

32位系統(tǒng)內(nèi)存布局:

+------------------------+
| 指針變量 (4字節(jié))        |  → 所有指針都是4字節(jié)
| 存儲(chǔ)32位地址 (0-4GB)    |
+------------------------+

64位系統(tǒng)內(nèi)存布局:

+------------------------+
| 指針變量 (8字節(jié))        |  → 所有指針都是8字節(jié)  
| 存儲(chǔ)64位地址            |
+------------------------+

關(guān)鍵結(jié)論:
- 指針大小只取決于系統(tǒng)架構(gòu)
- 與指向的數(shù)據(jù)類型無(wú)關(guān)
- char*、int*、double* 大小都相同

五、函數(shù)參數(shù)中的指針

5.1 指針作為函數(shù)參數(shù)

#include <stdio.h>

// 參數(shù)中的 * 表示需要傳遞地址
void modifyThroughPointer(int *location, int newValue) {
    *location = newValue;  // 修改指針指向的內(nèi)存
}

void demonstrateFunctionPointers() {
    int value = 100;
    
    printf("調(diào)用前: value = %d\n", value);  // 100
    
    modifyThroughPointer(&value, 200);  // 傳遞地址
    
    printf("調(diào)用后: value = %d\n", value);  // 200
}

int main() {
    demonstrateFunctionPointers();
    return 0;
}

5.2 函數(shù)調(diào)用過(guò)程圖解

函數(shù)調(diào)用前:

main函數(shù)棧幀:
+-------------------+
| 變量 value = 100  |
| 地址: 0x1234      |
+-------------------+

調(diào)用 modifyThroughPointer(&value, 200):

參數(shù)傳遞:
+-------------------+      +------------------------+
| &value = 0x1234   | ---> | location = 0x1234      |
| newValue = 200    |      | newValue = 200         |
+-------------------+      +------------------------+
                          | *location = 200        | ← 修改0x1234處的值
                          +------------------------+

函數(shù)返回后:

main函數(shù)棧幀:
+-------------------+
| 變量 value = 200  | ← 值被修改!
| 地址: 0x1234      |
+-------------------+

5.3 指針參數(shù)的常見(jiàn)使用場(chǎng)景

#include <stdio.h>

// 場(chǎng)景1:返回多個(gè)值
void getMinMax(int a, int b, int *min, int *max) {
    *min = (a < b) ? a : b;
    *max = (a > b) ? a : b;
}

// 場(chǎng)景2:交換兩個(gè)變量
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

void demonstratePracticalUsage() {
    // 返回多個(gè)值
    int x = 25, y = 40;
    int min, max;
    getMinMax(x, y, &min, &max);
    printf("min=%d, max=%d\n", min, max);
    
    // 交換變量
    printf("交換前: x=%d, y=%d\n", x, y);
    swap(&x, &y);
    printf("交換后: x=%d, y=%d\n", x, y);
}

交換過(guò)程圖解:

交換前:
+-----------+    +-----------+
| x = 25    |    | y = 40    |
| 地址:0x100|    | 地址:0x104|
+-----------+    +-----------+

調(diào)用 swap(&x, &y):

+-----------+    +-----------+
| a → 0x100 |    | b → 0x104 |
| temp = 25 |    |           |
+-----------+    +-----------+

執(zhí)行交換:
*a = *b    → 0x100處寫(xiě)入40
*b = temp  → 0x104處寫(xiě)入25

交換后:
+-----------+    +-----------+
| x = 40    |    | y = 25    |
| 地址:0x100|    | 地址:0x104|
+-----------+    +-----------+

六、綜合調(diào)試示例

6.1 完整的指針鏈分析

#include <stdio.h>

void debugPointerChain() {
    int data = 42;
    int *ptr1 = &data;
    int **ptr2 = &ptr1;
    int ***ptr3 = &ptr2;
    
    printf("=== 完整指針鏈分析 ===\n");
    
    printf("data: %d (地址:%p)\n", data, &data);
    printf("ptr1: %p (地址:%p) → *ptr1=%d\n", ptr1, &ptr1, *ptr1);
    printf("ptr2: %p (地址:%p) → **ptr2=%d\n", ptr2, &ptr2, **ptr2);
    printf("ptr3: %p (地址:%p) → ***ptr3=%d\n", ptr3, &ptr3, ***ptr3);
}

int main() {
    debugPointerChain();
    return 0;
}

6.2 指針鏈內(nèi)存布局總圖

完整指針鏈內(nèi)存布局:

+-------------+    指向    +-------------+    指向    +-------------+    指向    +-------------+
| ptr3        | --------> | ptr2        | --------> | ptr1        | --------> | data        |
| 地址:0x360  |           | 地址:0x368  |           | 地址:0x370  |           | 地址:0x378  |
| 值:0x368    |           | 值:0x370    |           | 值:0x378    |           | 值:42       |
+-------------+           +-------------+           +-------------+           +-------------+

訪問(wèn)路徑總結(jié):
ptr3        = 0x368    (ptr2的地址)
*ptr3       = 0x370    (ptr2的值 = ptr1的地址)  
**ptr3      = 0x378    (ptr1的值 = data的地址)
***ptr3     = 42       (data的值)

&ptr3       = 0x360
&ptr2       = 0x368  
&ptr1       = 0x370
&data       = 0x378

七、核心概念總結(jié)

7.1 指針操作符圖解

操作符作用示意圖:

+-------------+    &操作符    +-------------+
| 變量 x      | <----------- | &x          |
| 值: 5       |  獲取地址     | 值: 0x374   |
| 地址: 0x374 |              +-------------+
+-------------+
      |
      | *操作符
      | 解引用
      v
+-------------+
| *px         |
| 值: 5       |
+-------------+

7.2 指針使用黃金法則

  1. 聲明語(yǔ)法數(shù)據(jù)類型 *指針名
  2. 賦值規(guī)則:必須賦值為有效地址
  3. 訪問(wèn)數(shù)據(jù):通過(guò) *指針 訪問(wèn)
  4. 修改數(shù)據(jù):通過(guò) *指針 = 值 修改
  5. 安全使用:始終初始化,釋放后置NULL

7.3 最佳實(shí)踐代碼模板

// ? 安全指針使用模板
void safePointerUsage() {
    // 1. 聲明并初始化為NULL
    int *ptr = NULL;
    
    // 2. 分配內(nèi)存并檢查
    ptr = (int*)malloc(sizeof(int));
    if (ptr == NULL) {
        printf("內(nèi)存分配失敗\n");
        return;
    }
    
    // 3. 使用指針
    *ptr = 100;
    printf("值: %d\n", *ptr);
    
    // 4. 釋放并置空
    free(ptr);
    ptr = NULL;  // 防止懸空指針
}

// ? 函數(shù)參數(shù)明確意圖
void goodFunction(const int *input,  // 輸入?yún)?shù)(只讀)
                  int *output) {      // 輸出參數(shù)(可寫(xiě))
    // input指向的數(shù)據(jù)不會(huì)被修改
    *output = *input * 2;  // 修改output指向的數(shù)據(jù)
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容