C语言学习7-测试指针和地址

C语言学习1-数组、字符串
C语言学习2-指针的使用、const 指针
C语言学习3-指针和数组
C语言学习4-结构体
C语言学习5-函数

一、指针基础:理解地址与数据

1.1 什么是指针?

指针 = 内存地址,指针变量是存储内存地址的变量。

#include <stdio.h>

int main() {
    int x = 5;        // 在内存中分配一个整数空间
    int *px = &x;     // px存储x的内存地址
    
    printf("=== 基础指针演示 ===\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 内存布局示意图

内存地址布局图:

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

访问路径说明:
- &x  → 获取x的地址:0x374
- px  → px存储的值:0x374  
- *px → 访问地址0x374的内容:5

图解说明:

  • 每个变量都在内存中有自己的地址
  • 指针变量存储的是其他变量的地址
  • 通过 *指针 可以访问指针指向的数据

二、通过指针修改内存数据

2.1 修改指针指向的值

#include <stdio.h>

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

2.2 修改过程的内存变化图

修改前内存状态:

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

执行 *px = 10 后:

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

关键理解:

  • px 存储的是地址,这个地址不变
  • *px 操作的是该地址指向的内存内容
  • 修改 *px 就是修改原始变量 x 的值

三、多级指针(指针的指针)

3.1 二级指针详解

#include <stdio.h>

int main() {
    int x = 5;
    int *px = &x;      // 一级指针
    int **pxx = &px;   // 二级指针
    
    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 多级指针内存关系图

三级指针关系图:

+----------------+    指向    +----------------+    指向    +----------------+
| 三级指针 pxx   | --------> | 二级指针 px    | --------> | 一级变量 x     |
| 地址: 0x360    |           | 地址: 0x368    |           | 地址: 0x374    |
| 值: 0x368      |           | 值: 0x374      |           | 值: 5          |
+----------------+           +----------------+           +----------------+

访问路径分解:
pxx     = 0x368    (px的地址)
*pxx    = 0x374    (px的值,即x的地址)
**pxx   = 5        (x的值)

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

3.3 通过多级指针修改变量

#include <stdio.h>

int main() {
    int x = 5;
    int *px = &x;
    int **pxx = &px;
    
    printf("初始: x = %d\n", x);      // 5
    
    // 三种方式修改同一个变量
    x = 10;
    printf("直接修改: x = %d\n", x);  // 10
    
    *px = 20;  
    printf("通过px: x = %d\n", x);    // 20
    
    **pxx = 30;
    printf("通过pxx: x = %d\n", x);   // 30
    
    return 0;
}

修改过程图解:

所有修改都作用于同一内存位置:

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

四、指针的大小与系统架构

4.1 指针大小测试

#include <stdio.h>

int main() {
    printf("=== 指针大小测试 ===\n");
    printf("char* 大小: %zu 字节\n", sizeof(char*));
    printf("int* 大小: %zu 字节\n", sizeof(int*)); 
    printf("double* 大小: %zu 字节\n", sizeof(double*));
    printf("void* 大小: %zu 字节\n", sizeof(void*));
    printf("int** 大小: %zu 字节\n", sizeof(int**));
    
    return 0;
}

4.2 指针大小示意图

32位系统内存布局:

+------------------------+
| 指针变量 (4字节)        |  → 所有指针都是4字节
| 存储32位地址 (0-4GB)    |
+------------------------+

64位系统内存布局:

+------------------------+
| 指针变量 (8字节)        |  → 所有指针都是8字节  
| 存储64位地址            |
+------------------------+

关键结论:
- 指针大小只取决于系统架构
- 与指向的数据类型无关
- char*、int*、double* 大小都相同

五、函数参数中的指针

5.1 指针作为函数参数

#include <stdio.h>

// 参数中的 * 表示需要传递地址
void modifyThroughPointer(int *location, int newValue) {
    *location = newValue;  // 修改指针指向的内存
}

void demonstrateFunctionPointers() {
    int value = 100;
    
    printf("调用前: value = %d\n", value);  // 100
    
    modifyThroughPointer(&value, 200);  // 传递地址
    
    printf("调用后: value = %d\n", value);  // 200
}

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

5.2 函数调用过程图解

函数调用前:

main函数栈帧:
+-------------------+
| 变量 value = 100  |
| 地址: 0x1234      |
+-------------------+

调用 modifyThroughPointer(&value, 200):

参数传递:
+-------------------+      +------------------------+
| &value = 0x1234   | ---> | location = 0x1234      |
| newValue = 200    |      | newValue = 200         |
+-------------------+      +------------------------+
                          | *location = 200        | ← 修改0x1234处的值
                          +------------------------+

函数返回后:

main函数栈帧:
+-------------------+
| 变量 value = 200  | ← 值被修改!
| 地址: 0x1234      |
+-------------------+

5.3 指针参数的常见使用场景

#include <stdio.h>

// 场景1:返回多个值
void getMinMax(int a, int b, int *min, int *max) {
    *min = (a < b) ? a : b;
    *max = (a > b) ? a : b;
}

// 场景2:交换两个变量
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

void demonstratePracticalUsage() {
    // 返回多个值
    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);
}

交换过程图解:

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

调用 swap(&x, &y):

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

执行交换:
*a = *b    → 0x100处写入40
*b = temp  → 0x104处写入25

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

六、综合调试示例

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 指针链内存布局总图

完整指针链内存布局:

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

访问路径总结:
ptr3        = 0x368    (ptr2的地址)
*ptr3       = 0x370    (ptr2的值 = ptr1的地址)  
**ptr3      = 0x378    (ptr1的值 = data的地址)
***ptr3     = 42       (data的值)

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

七、核心概念总结

7.1 指针操作符图解

操作符作用示意图:

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

7.2 指针使用黄金法则

  1. 声明语法数据类型 *指针名
  2. 赋值规则:必须赋值为有效地址
  3. 访问数据:通过 *指针 访问
  4. 修改数据:通过 *指针 = 值 修改
  5. 安全使用:始终初始化,释放后置NULL

7.3 最佳实践代码模板

// ✅ 安全指针使用模板
void safePointerUsage() {
    // 1. 声明并初始化为NULL
    int *ptr = NULL;
    
    // 2. 分配内存并检查
    ptr = (int*)malloc(sizeof(int));
    if (ptr == NULL) {
        printf("内存分配失败\n");
        return;
    }
    
    // 3. 使用指针
    *ptr = 100;
    printf("值: %d\n", *ptr);
    
    // 4. 释放并置空
    free(ptr);
    ptr = NULL;  // 防止悬空指针
}

// ✅ 函数参数明确意图
void goodFunction(const int *input,  // 输入参数(只读)
                  int *output) {      // 输出参数(可写)
    // input指向的数据不会被修改
    *output = *input * 2;  // 修改output指向的数据
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容