指针是 C 语言的灵魂。理解指针是掌握 C 语言的关键!
指针是一个存储内存地址的变量。
想象内存是一排房子,每个房子有门牌号(地址):
内存地址: 1000 1004 1008 1012
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
数据: │ 42 │ │ 3.14│ │ 'A' │ │1000 │
└─────┘ └─────┘ └─────┘ └─────┘
↑
指针(存储 1000 这个地址)
int number = 42; // 普通变量
int* ptr; // 指针变量,可以存储 int 的地址
ptr = &number; // & 取地址运算符,获取 number 的地址
number (int) ptr (int*)
┌─────────┐ ┌─────────┐
│ 42 │ │ 0x1000 │ ← 存储的是 number 的地址
└─────────┘ └─────────┘
↑ │
0x1000 (地址) └── 指向 number
& - 取地址运算符int x = 10;
int* ptr = &x; // ptr 存储 x 的地址
* - 解引用运算符int x = 10;
int* ptr = &x;
printf("%d\n", *ptr); // 输出 10,访问 ptr 指向的值
*ptr = 20; // 修改 ptr 指向的值为 20
printf("%d\n", x); // 输出 20,x 被修改了!
int* int_ptr; // 指向 int 的指针
float* float_ptr; // 指向 float 的指针
char* char_ptr; // 指向 char 的指针
void* void_ptr; // 通用指针,可以指向任何类型
int x = 10;
int_ptr = &x; // ✅ 类型匹配
float f = 3.14f;
int_ptr = &f; // ⚠️ 类型不匹配,会有警告
数组名本质上是指针:
int arr[5] = {1, 2, 3, 4, 5};
// 数组名就是指向第一个元素的指针
printf("%p\n", arr); // 数组首地址
printf("%p\n", &arr[0]); // 同上
// 指针算术
printf("%d\n", *arr); // 1,第一个元素
printf("%d\n", *(arr+1)); // 2,第二个元素
printf("%d\n", arr[2]); // 3,第三种写法
int arr[5] = {10, 20, 30, 40, 50};
int* ptr = arr; // 指向数组开头
// 方法 1:指针算术
for (int i = 0; i < 5; i++) {
printf("%d ", *(ptr + i));
}
// 方法 2:移动指针
for (int i = 0; i < 5; i++) {
printf("%d ", *ptr);
ptr++; // 移动到下一个元素
}
// 传值:函数内修改不影响原变量
void increment_by_value(int x) {
x++; // 只修改了副本
}
// 传指针:函数内修改影响原变量
void increment_by_pointer(int* x) {
(*x)++; // 修改原变量
}
int main(void) {
int a = 10;
int b = 10;
increment_by_value(a);
increment_by_pointer(&b);
printf("a = %d\n", a); // 10,没变
printf("b = %d\n", b); // 11,变了!
return 0;
}
typedef struct {
int score;
bool running;
} Game;
// ❌ 传值:无法修改原游戏
void reset_game(Game game) {
game.score = 0;
game.running = true;
}
// ✅ 传指针:可以修改原游戏
void reset_game_ptr(Game* game) {
game->score = 0;
game->running = true;
}
int main(void) {
Game g = {100, false};
reset_game(g); // 没用
reset_game_ptr(&g); // 有效!
return 0;
}
typedef struct Snake {
int x, y, length;
} Snake;
// 在堆上创建蛇(生命周期由你控制)
Snake* create_snake(int x, int y) {
Snake* snake = malloc(sizeof(Snake));
snake->x = x;
snake->y = y;
snake->length = 1;
return snake;
}
// 使用
Snake* my_snake = create_snake(10, 10);
printf("蛇在 (%d, %d)\n", my_snake->x, my_snake->y);
// 用完要释放
free(my_snake);
// 指针数组:数组的每个元素都是指针
int a = 1, b = 2, c = 3;
int* ptr_array[3] = {&a, &b, &c};
printf("%d\n", *ptr_array[0]); // 1
printf("%d\n", *ptr_array[1]); // 2
printf("%d\n", *ptr_array[2]); // 3
int* ptr; // ❌ 未初始化,指向随机地址
*ptr = 10; // ❌ 危险!可能崩溃
int x = 10;
int* ptr = &x; // ✅ 正确
int* ptr = malloc(sizeof(int));
free(ptr); // 释放内存
*ptr = 10; // ❌ 野指针!内存已释放
ptr = NULL; // ✅ 释放后置为 NULL
int x = 10;
float* ptr = &x; // ⚠️ 类型不匹配
int x = 42;
int* ptr = &x;
// 各种访问方式
printf("%d\n", x); // 42,直接访问
printf("%p\n", &x); // 地址,x 的地址
printf("%d\n", *ptr); // 42,解引用
printf("%p\n", ptr); // 地址,ptr 存储的值(x 的地址)
printf("%p\n", &ptr); // 地址,ptr 自己的地址
& 和 * 运算符void swap(int* a, int* b) {
// 实现交换
}
用指针遍历数组,找出最大值
下一课:内存管理