什么是指针

C 语言里,每个变量都住在内存的某个位置,这个位置的编号就是内存地址。指针就是一种专门存放"地址"的变量。

打个比方:变量好比一个抽屉,里面放东西;指针好比一张写着"3 号抽屉"的纸条,它不装东西本身,而是告诉你东西放在哪里。

定义指针时,类型要和它指向的变量类型一致,写法是 类型 *名字:

int a = 10;      // 普通变量
int *p = &a;     // p 存的是 a 的地址

这里出现两个新符号,它们是一对镜像操作:

  • &a:取地址,读作"a 在哪",得到 a 的地址
  • *p:解引用,读作"p 指着的那个变量",访问该地址上存的值

打印地址和通过指针读写

#include <stdio.h>

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

    printf("%p\n", (void*)p);   // 输出 a 的地址,形如 000000000061FE14(每次运行可能不同)
    printf("%d\n", *p);         // 输出:10(顺着地址取出值)

    *p = 20;                    // 通过指针修改 a
    printf("%d\n", a);          // 输出:20
    return 0;
}

可以看到,改 *p 就是改 a 本身——这正是指针的威力:可以从函数外部直接改动某个变量的值。

指针最经典的用途:swap 函数

初学者常见的困惑是"为什么函数里改了形参,外面的变量不变"。因为 C 的实参是值拷贝,函数拿到的是复印件。想让函数修改外部变量,就要把地址传进去:

#include <stdio.h>

void swap(int *x, int *y) {
    int tmp = *x;
    *x = *y;
    *y = tmp;
}

int main(void) {
    int a = 1, b = 2;
    swap(&a, &b);                  // 传地址
    printf("a=%d b=%d\n", a, b);   // 输出:a=2 b=1,真的换过来了
    return 0;
}

如果只传 int 不传指针,交换只发生在复印件上,函数返回后毫无变化。

指针与数组的关系

数组名在大多数场景下会退化成"指向首元素的指针",所以可以用指针遍历数组:

#include <stdio.h>

int main(void) {
    int arr[4] = {10, 20, 30, 40};
    int *p = arr;               // 等价于 &arr[0]

    printf("%d\n", *p);         // 输出:10
    printf("%d\n", *(p + 1));   // 输出:20,p+1 会跨过一个 int 的大小

    for (int i = 0; i < 4; i++) {
        printf("%d ", *(p + i));   // 输出:10 20 30 40
    }
    return 0;
}

*(p + i) 也可以写成 p[i],两者完全等价——数组下标本质上就是指针运算。

暂时"不指向任何地方":NULL

指针刚声明、还没确定指向谁时,先把它置为 NULL(空指针),解引用前先判断,这是必须养成的好习惯:

int *p = NULL;
if (p != NULL) {
    printf("%d\n", *p);   // 只有确认指向有效才解引用
}

小结

  • 指针是存放内存地址的变量;& 取地址,* 解引用,二者互逆
  • *p = 20 会直接改变被指向变量的值
  • 想在函数里修改外部变量(比如 swap),必须传地址而不是传值
  • 数组名会退化为首元素指针,*(p + i) 与 p[i] 等价
  • 暂不使用的指针置 NULL,解引用前先判空

指针是 C 的灵魂,也是后续学习数组传参、字符串处理、动态内存的基础。先把它和"地址"的关系想清楚,后面就不会晕。