C 指针入门图文课

C 语言指针变量使用流程

指针变量保存的是地址。理解它的关键是分清 p*p&a 三件事:地址在哪里,地址指向谁,通过地址能读写什么。

&a 先拿到地址
p = &a 再保存地址
*p 顺着地址读写
NULL / free 避开无效地址
p 保存 a 的地址,p 指向 a 内存中的两个变量 int a 10 地址 0x1000 int *p 0x1000 地址 0x2000 p = &a p 里面装的是 a 的地址,所以 *p 可以访问 a

1. 指针变量到底装了什么

普通变量直接保存数据,指针变量保存地址。用 & 获取变量地址,用 * 根据地址访问内容。

变量 a 的地址被保存进指针 p 变量 a 10 &a = 0x1000 指针 p 0x1000 p = &a *p 的结果就是 10
最小可运行示例
#include <stdio.h>

int main(void) {
    int a = 10;
    int *p = &a;

    printf("a  = %d\n", a);
    printf("*p = %d\n", *p);

    return 0;
}
可以把 p 想成一张写着门牌号的纸, *p 才是顺着门牌号找到房间后看到的内容。

2. 指针变量的标准使用流程

大多数指针代码都可以拆成四步:定义目标变量,定义指针,保存地址,通过解引用读写。

1 准备目标变量

先有一个真正存数据的位置,例如 int a = 10;

a: 10
2 定义同类型指针

int *p; 表示 p 指向 int 类型数据。

p: ?
3 保存有效地址

&a 赋给 p:p = &a;

p: 0x1000
4 通过 *p 访问

*p 表示 p 指向位置里的值,可读也可写。

*p: 10

3. 四个符号速记

很多初学者不是不懂指针,而是把这几个符号混在一起了。

&

取地址

&a 得到变量 a 的内存地址。

*

定义或解引用

声明时是指针标记,使用时是“顺着地址取值”。

p

地址本身

p 的值通常是类似 0x1000 的地址。

*p

地址里的值

如果 p 指向 a,那么 *p 就是 a。

4. 解引用既能读,也能写

只要指针有效,*p 可以放在表达式右边读取,也可以放在左边修改。

读取:拿到指向位置的值

读值
int a = 10;
int *p = &a;

printf("%d\n", *p);  // 输出 10

*p 此时等价于“读取 a 的值”。

写入:修改指向位置的值

改值
int a = 10;
int *p = &a;

*p = 20;
printf("%d\n", a);   // 输出 20

*p = 20 会把 a 改成 20。

5. 数组名和指针偏移

在很多表达式中,数组名会退化为首元素地址。 p + 1 不是地址数字加 1,而是移动到下一个 int 元素。

0x1000 1 *(p + 0)
0x1004 2 *(p + 1)
0x1008 3 *(p + 2)
0x100C ? *(p + 3)
数组与指针
int arr[3] = {1, 2, 3};
int *p = arr;

printf("%d\n", *(p + 0)); // 1
printf("%d\n", *(p + 1)); // 2
printf("%d\n", *(p + 2)); // 3

// *(p + 3) 越界,不能访问

6. 常见错误对照

指针错误通常不是语法问题,而是“地址无效”或“访问范围不对”。

错误 1 未初始化指针

错误写法
int *p;
*p = 10;   // p 是随机地址
正确写法
int a;
int *p = &a;
*p = 10;

错误 2 空指针解引用

错误写法
int *p = NULL;
printf("%d\n", *p);
正确写法
if (p != NULL) {
    printf("%d\n", *p);
}

错误 3 把值当地址

错误写法
int a = 10;
int *p = a;   // a 是值
正确写法
int a = 10;
int *p = &a;  // &a 是地址

错误 4 scanf 少写 &

错误写法
int a;
scanf("%d", a);
正确写法
int a;
scanf("%d", &a);

错误 5 free 后继续使用

错误写法
int *p = malloc(sizeof(int));
*p = 10;
free(p);
printf("%d\n", *p);
正确写法
free(p);
p = NULL;

错误 6 返回局部变量地址

错误写法
int* get_ptr(void) {
    int x = 10;
    return &x;
}
正确写法
int* get_ptr(void) {
    int *p = malloc(sizeof(int));
    *p = 10;
    return p;
}

错误 7 数组越界

错误写法
int arr[3] = {1, 2, 3};
int *p = arr;
printf("%d\n", *(p + 3));
正确写法
printf("%d\n", *(p + 0));
printf("%d\n", *(p + 1));
printf("%d\n", *(p + 2));

错误 8 修改字符串常量

错误写法
char *s = "hello";
s[0] = 'H';
正确写法
char s[] = "hello";
s[0] = 'H';

7. 写指针代码前的自查清单

每次看到 *,都先问自己这三个问题。

问题 1:p 里有有效地址吗

它是否已经指向某个真实变量,或指向 malloc 成功申请到的空间?

问题 2:类型匹配吗

int * 应该指向 int,char * 应该指向 char。

问题 3:生命周期还在吗

不要访问已经 free 的内存,也不要返回函数内部局部变量的地址。

口诀:取地址用 &,访问值用 *;指针先指向有效地址,再解引用。