13 · 指针入门
- 理解地址和 * / &
- 会用指针改外部变量
- 知道数组名和指针的关系
地址与取值
变量住在内存里,有门牌号(地址)。
&a 取 a 的地址;
*p 表示「p 指向的那个东西」。
画一张图帮助理解:
执行 int *p = &a; 后,p 这个盒子(自己也有地址)里存的是 0x100。
*p 就是「按 0x100 找到 a」。所以 *p = 20 等于 a = 20。
指针类型 int * 里的 int 是在说:「我指向的东西应被当成 int 来读写」。
#include <stdio.h>
int main(void)
{
int a = 10;
int *p = &a; /* p 存的是 a 的地址 */
printf("%d\n", a); /* 10 */
printf("%d\n", *p); /* 也是 10 */
*p = 20; /* 通过地址改 a */
printf("%d\n", a); /* 20 */
return 0;
}
用指针当「输出参数」
这是函数「改外面变量」的标准做法。一个函数可以有多个「输出」,靠多个指针参数写出去:
#include <stdio.h>
void set_to_max(int a, int b, int *out)
{
*out = (a > b) ? a : b;
}
int main(void)
{
int result;
set_to_max(3, 9, &result); /* 把 result 的地址交给 out */
printf("%d\n", result); /* 9 */
return 0;
}
数组和指针
数组名在表达式里常退化成「首元素地址」。a[i] 等价 *(a+i)。
所以 p = a; 后,p[0] 就是 a[0]。
注意:数组名本身不能被赋值成别的(a = p; 非法),它是「固定的首地址」。
int a[3] = {10, 20, 30};
int *p = a;
/* p[0]==10, *(p+1)==20, a[2]==30 */
必须小心
- 未初始化就使用(野指针):里面是随机地址,写它可能程序崩溃或改坏内存
- 指向已销毁的局部变量(函数已返回,那块栈内存不再属于你)
- 对 NULL 解引用:系统会直接崩(段错误)
指针交换两个数
这是指针最经典、最必须会的用法:
#include <stdio.h>
void swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
int main(void)
{
int x = 3, y = 8;
swap(&x, &y);
printf("x=%d y=%d\n", x, y); /* x=8 y=3 */
return 0;
}
NULL:空指针
可以用 NULL 表示「暂时不指向任何人」。使用前先判断:
#include <stdio.h>
#include <stddef.h>
void print_if(int *p)
{
if (p == NULL)
{
printf("空指针\n");
return;
}
printf("%d\n", *p);
}
单片机场景 · 指针:通过地址修改变量
指针保存地址。先指向有效对象,再解引用读取或修改。
边界与验证:y 保存当时读到的值 5,不是对 v 的持续引用。*p = 9 后,v 为 9,y 仍为 5。
unsigned char v = 5;
unsigned char *p = &v;
unsigned char y = *p;
*p = 9;
本课过关
必做&a 表示?
记法要成对:& 是「取地址」,* 是「按地址取内容」。int a=10; int *p=&a; 里,p 这个盒子存的是 a 的门牌号。*p 就是「找到那个门牌号对应的盒子」,读它得到 10,写它等于改 a。若问题问的是 &a,答案是 a 的地址,不是值 10,也不是逻辑与。逻辑与写在条件里如 if(a&&b),和取地址 & 是不同语境下的同一符号,靠类型系统区分。
- 写函数 void inc(int *x),让 *x 加 1。
- 解释 *p = 5 和 p = 5 的区别。
- 为什么 scanf 要写 &age 而不是 age?
先自己写完,再点开看解答
1:inc
void inc(int *x)
{
*x = *x + 1; /* 或 (*x)++ */
}2:*p=5 与 p=5:*p=5 是「把 5 写进 p 指向的那格」;p=5 是「让 p 这个指针本身存地址 5」。后者几乎总是错的——地址 5 通常不是你的合法内存。
3:scanf 为何要 &age:scanf 需要知道「往哪里写」。age 只是数值,&age 才是盒子的地址。漏写 & 是 scanf 最常见的崩溃原因(未定义行为)。
下一课:结构体——把一组相关数据打包。