先有一个真正存数据的位置,例如 int a = 10;。
a: 10
指针变量保存的是地址。理解它的关键是分清 p、 *p、 &a 三件事:地址在哪里,地址指向谁,通过地址能读写什么。
普通变量直接保存数据,指针变量保存地址。用 & 获取变量地址,用 * 根据地址访问内容。
#include <stdio.h>
int main(void) {
int a = 10;
int *p = &a;
printf("a = %d\n", a);
printf("*p = %d\n", *p);
return 0;
}
大多数指针代码都可以拆成四步:定义目标变量,定义指针,保存地址,通过解引用读写。
先有一个真正存数据的位置,例如 int a = 10;。
int *p; 表示 p 指向 int 类型数据。
把 &a 赋给 p:p = &a;
*p 表示 p 指向位置里的值,可读也可写。
很多初学者不是不懂指针,而是把这几个符号混在一起了。
&a 得到变量 a 的内存地址。
声明时是指针标记,使用时是“顺着地址取值”。
p 的值通常是类似 0x1000 的地址。
如果 p 指向 a,那么 *p 就是 a。
只要指针有效,*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。
在很多表达式中,数组名会退化为首元素地址。 p + 1 不是地址数字加 1,而是移动到下一个 int 元素。
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) 越界,不能访问
指针错误通常不是语法问题,而是“地址无效”或“访问范围不对”。
int *p;
*p = 10; // p 是随机地址
int a;
int *p = &a;
*p = 10;
int *p = NULL;
printf("%d\n", *p);
if (p != NULL) {
printf("%d\n", *p);
}
int a = 10;
int *p = a; // a 是值
int a = 10;
int *p = &a; // &a 是地址
int a;
scanf("%d", a);
int a;
scanf("%d", &a);
int *p = malloc(sizeof(int));
*p = 10;
free(p);
printf("%d\n", *p);
free(p);
p = NULL;
int* get_ptr(void) {
int x = 10;
return &x;
}
int* get_ptr(void) {
int *p = malloc(sizeof(int));
*p = 10;
return p;
}
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));
char *s = "hello";
s[0] = 'H';
char s[] = "hello";
s[0] = 'H';
每次看到 *,都先问自己这三个问题。
它是否已经指向某个真实变量,或指向 malloc 成功申请到的空间?
int * 应该指向 int,char * 应该指向 char。
不要访问已经 free 的内存,也不要返回函数内部局部变量的地址。