第5章 指针


核心章节 重点掌握 指针是C语言最强大也是最难掌握的特性

本章概述

指针是C语言最强大的特性之一,也是C语言的灵魂。掌握指针是成为优秀C程序员的必经之路。

本章主要内容:

  • 指针的基本概念
  • 指针的运算
  • 指针与数组
  • 指针与函数
  • 指针数组与多级指针
  • 动态内存分配

什么是指针?

基本概念

指针是一个变量,其值为另一个变量的地址。通过指针,我们可以间接访问和操作内存中的数据。

1
2
3
int a = 10;      // 整型变量
int *p; // 指针变量
p = &a // p指向a的地址

💡 关键符号

  • &:取地址运算符,获取变量的地址
  • *:指针运算符,访问指针指向的变量

指针的定义和使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include 

int main() {
int a = 10;
int *p; // 定义指针变量

p = &a // p指向a

printf("a的值:%d\n", a); // 输出:10
printf("a的地址:%p\n", &a); // 输出:a的地址
printf("p的值:%p\n", p); // 输出:a的地址
printf("*p的值:%d\n", *p); // 输出:10

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

return 0;
}

指针的运算

指针的算术运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include 

int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *p = arr; // 指向数组首元素

printf("%d\n", *p); // 输出:10
printf("%d\n", *(p + 1)); // 输出:20
printf("%d\n", *(p + 2)); // 输出:30

p++; // 指针后移
printf("%d\n", *p); // 输出:20

return 0;
}

指针的关系运算

1
2
3
4
5
6
7
int arr[5];
int *p1 = &arr[0];
int *p2 = &arr[4];

if (p1 < p2) {
printf("p1在p2前面\n");
}

⚠️ 注意

  • 指针运算只在数组中有意义
  • 不要对未初始化的指针进行运算
  • 注意指针越界问题

指针与数组

数组名与指针

数组名在很多情况下会退化为指向首元素的指针:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include 

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

// 使用指针遍历数组
for (int i = 0; i < 5; i++) {
printf("%d ", *(p + i)); // 等价于 arr[i]
}

// 数组名是指针常量,不能修改
// arr++; // 错误!
p++; // 正确

return 0;
}

指针遍历数组

1
2
3
4
5
6
7
8
9
// 方法1:使用数组下标
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}

// 方法2:使用指针算术
for (int *p = arr; p < arr + 5; p++) {
printf("%d ", *p);
}

指针与函数

指针作为函数参数

指针参数可以实现”传引用”的效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include 

// 值传递:不会改变原变量
void swapByValue(int a, int b) {
int temp = a;
a = b;
b = temp;
}

// 指针传递:会改变原变量
void swapByPointer(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}

int main() {
int x = 10, y = 20;

swapByValue(x, y);
printf("值传递后:x=%d, y=%d\n", x, y); // 输出:x=10, y=20

swapByPointer(&x, &y);
printf("指针传递后:x=%d, y=%d\n", x, y); // 输出:x=20, y=10

return 0;
}

函数返回指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include 

int* max(int *a, int *b) {
return (*a > *b) ? a : b;
}

int main() {
int x = 10, y = 20;
int *p = max(&x, &y);

printf("最大值:%d\n", *p); // 输出:20

return 0;
}

⚠️ 注意

不要返回指向局部变量的指针,因为函数返回后局部变量会被销毁。

指针数组与数组指针

指针数组

指针数组是数组的每个元素都是指针:

1
int *arr[5];    // 5个int指针的数组

数组指针

数组指针是指向整个数组的指针:

1
int (*p)[5];    // 指向包含5个int的数组的指针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include 

int main() {
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
int (*p)[4] = arr; // 数组指针

for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}

return 0;
}

多级指针

指向指针的指针称为二级指针:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include 

int main() {
int a = 10;
int *p = &a // 一级指针
int **pp = &p // 二级指针

printf("a的值:%d\n", a); // 输出:10
printf("*p的值:%d\n", *p); // 输出:10
printf("**pp的值:%d\n", **pp); // 输出:10

**pp = 20;
printf("修改后a的值:%d\n", a); // 输出:20

return 0;
}

💡 应用场景

多级指针常用于修改指针变量的值,或在函数中返回指针。

动态内存分配

malloc和free

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include 
#include

int main() {
// 动态分配内存
int *p = (int*)malloc(5 * sizeof(int));

if (p == NULL) {
printf("内存分配失败\n");
return 1;
}

// 使用内存
for (int i = 0; i < 5; i++) {
p[i] = i + 1;
}

for (int i = 0; i < 5; i++) {
printf("%d ", p[i]);
}

// 释放内存
free(p);
p = NULL; // 防止悬空指针

return 0;
}

calloc和realloc

1
2
3
4
5
6
7
#include 

// calloc:分配并初始化为0
int *p1 = (int*)calloc(5, sizeof(int));

// realloc:重新分配内存大小
int *p2 = (int*)realloc(p, 10 * sizeof(int));

内存分配函数对比

函数 说明
malloc(size) 分配size字节内存
calloc(n, size) 分配n个size字节,并初始化为0
realloc(ptr, size) 重新分配内存大小
free(ptr) 释放动态分配的内存

⚠️ 内存管理原则

  • 谁分配,谁释放
  • 释放后置NULL,防止悬空指针
  • 避免内存泄漏

常见指针错误

未初始化的指针

1
2
int *p;      // 危险!p指向未知位置
*p = 10; // 可能导致程序崩溃

野指针

1
2
3
int *p = (int*)malloc(sizeof(int));
free(p);
*p = 10; // 错误!p指向已释放的内存

指针越界

1
2
3
4
5
int arr[5];
int *p = arr;
for (int i = 0; i <= 5; i++) {
*(p + i) = i; // 错误!i=5时越界
}

💡 安全使用指针

  • 初始化指针为NULL
  • 使用前检查指针是否为NULL
  • 释放后置NULL
  • 使用const保护不应修改的数据

综合示例:链表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include 
#include

struct Node {
int data;
struct Node *next;
};

// 创建节点
struct Node* createNode(int data) {
struct Node *newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
}

// 插入节点
void insertNode(struct Node **head, int data) {
struct Node *newNode = createNode(data);
newNode->next = *head;
*head = newNode;
}

// 打印链表
void printList(struct Node *head) {
while (head != NULL) {
printf("%d -> ", head->data);
head = head->next;
}
printf("NULL\n");
}

int main() {
struct Node *head = NULL;

insertNode(&head, 10);
insertNode(&head, 20);
insertNode(&head, 30);

printList(head);

return 0;
}

本章小结

  • ✓ 理解了指针的基本概念
  • ✓ 掌握了指针的运算
  • ✓ 学会了指针与数组、函数的结合使用
  • ✓ 了解了动态内存分配
  • ✓ 认识了指针使用的常见错误

下一步学习


© 2025 Rl. 使用 Stellar 创建
站点访问量 Loading... 站点访客数 Loading... 页面访问量 Loading...