01. 什么是指针
指针就是存内存地址的变量。就像快递地址,指针里存的是某个东西放在内存里的哪个位置。声明指针用星号,取地址用 &,解引用用 *(到那个地址去拿东西)。理解指针是 C 语言最重要的基本功,数组、字符串、动态内存都和指针有关。
c
#include <stdio.h>
int main() {
int a = 42;
int *p = &a; // p 存的是 a 的地址
printf("a 的值: %d\n", a); // 42
printf("a 的地址: %p\n", &a); // 某个地址如 0x7fff...
printf("p 存的值(地址): %p\n", p);
printf("p 指向的值: %d\n", *p); // 42
*p = 100; // 通过指针修改 a 的值
printf("a 的新值: %d\n", a); // 100
return 0;
}星号在声明时表示这个变量是指针,在使用时表示解引用。
02. 指针运算和数组
数组名本身就是指向第一个元素的指针。arr[i] 本质上是 *(arr + i),编译器帮你翻译了。指针加减整数会移动指针的位置,移动的字节数是类型大小乘以整数。指针相减得到两个地址之间隔了几个元素。空指针用 NULL 表示,解引用 NULL 会直接崩溃。
c
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *p = arr; // 指向第一个元素
printf("%d\n", *p); // 10
printf("%d\n", *(p + 1)); // 20 - 指针+1跳到下一个int
printf("%d\n", p[2]); // 30 - 等价于*(p+2)
// 遍历数组
for (int *ptr = arr; ptr < arr + 5; ptr++) {
printf("%d ", *ptr); // 10 20 30 40 50
}
// 指针相减
int *p1 = &arr[4];
int *p2 = &arr[1];
printf("相隔 %ld 个元素\n", p1 - p2); // 3
return 0;
}指针运算超出了数组边界(越界),读到的是垃圾数据,写到外面可能破坏其他变量。
03. 指针和函数
C 语言函数参数是值传递的,传进去的是副本。如果函数里要修改外部的变量,必须传指针(传地址)。swap 交换函数是最经典的例子。指针作为返回值时要小心,不能返回局部变量的地址,因为函数返回后局部变量就释放了。
c
#include <stdio.h>
// 值传递 - 改不了
void bad_swap(int a, int b) {
int tmp = a;
a = b;
b = tmp;
}
// 指针传递 - 可以改
void swap(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
int main() {
int x = 10, y = 20;
swap(&x, &y);
printf("x=%d, y=%d\n", x, y); // x=20, y=10
return 0;
}需要修改外部变量时传指针,这是 C 语言里最常见的指针用途。
04. 指针数组和数组指针
指针数组是存指针的数组:int *arr[5]。数组指针是指向数组的指针:int (*p)[5],括号不能省因为 [] 优先级比 * 高。最常用的是字符串数组:char *arr[] 存了一堆字符串的指针。main 函数的参数 char *argv[] 就是一个指针数组。
c
#include <stdio.h>
int main() {
// 指针数组:每个元素是一个指针
const char *strs[] = {"C语言", "指针", "数组"};
for (int i = 0; i < 3; i++) {
printf("%s\n", strs[i]);
}
// 数组指针:指向一整个数组
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*p)[3] = matrix; // p 指向有3个int的数组
printf("%d\n", p[0][1]); // 2
printf("%d\n", p[1][2]); // 6
return 0;
}读 C 声明有个技巧:从名字开始,先看右边再看左边。int *p[5]:p 是数组(先看右边[5]),数组里存的是指针(再看左边*)。
05. 函数指针
函数指针存的是函数的入口地址。声明语法有点奇怪:返回值 (*指针名)(参数列表)。用某个函数名赋值,通过指针名调函数。函数指针常用作回调函数,比如 qsort 排序时要传一个比较函数,信号处理注册回调,还有模仿面向对象的多态。
c
#include <stdio.h>
#include <stdlib.h>
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
// 函数指针作为参数
int calculate(int (*op)(int, int), int x, int y) {
return op(x, y);
}
// qsort 比较函数
int compare(const void *a, const void *b) {
return *(int*)a - *(int*)b;
}
int main() {
int (*func)(int, int); // 函数指针声明
func = add;
printf("add: %d\n", func(3, 5)); // 8
func = sub;
printf("sub: %d\n", func(3, 5)); // -2
printf("calc: %d\n", calculate(add, 10, 20)); // 30
// qsort 使用
int arr[] = {5, 2, 8, 1, 3};
qsort(arr, 5, sizeof(int), compare);
return 0;
}函数指针的声明看起来很复杂,可以用 typedef 简化:typedef int (*Operation)(int, int);
知识测验
第 1/4 题正确 0
C 语言中取变量的地址用什么运算符?
下一节
下一节 内存管理