C语言指针

概述

本文档详细介绍C语言中指针的使用方法,包括指针的基本概念、指针算术、指针与数组的关系、指针与函数的关系、指针数组和数组指针、多级指针、const指针、空指针和野指针、指向函数的指针等内容。指针是C语言中最强大也最容易出错的特性之一,掌握指针的使用对于编写高效、灵活的C程序至关重要。

指针的基本概念

指针的定义

指针是一个变量,它存储的是另一个变量的内存地址。在C语言中,指针的声明语法如下:

1
类型 *指针变量名;

指针的初始化与使用

指针的初始化是将一个变量的地址赋值给指针变量,使用&运算符获取变量的地址。通过指针访问其指向的变量的值,称为”解引用”,使用*运算符。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 指针的基本概念
int num = 100;
int *ptr; // 声明一个指向int类型的指针

ptr = # // 初始化指针,指向变量num的地址

printf("变量num的值: %d\n", num);
printf("变量num的地址: %p\n", &num);
printf("指针ptr存储的地址: %p\n", ptr);
printf("指针ptr解引用得到的值: %d\n", *ptr);

// 通过指针修改变量的值
*ptr = 200;
printf("通过指针修改后num的值: %d\n", num);

补充示例:指针的大小

1
2
3
4
5
// 指针的大小
printf("int* 指针的大小: %zu 字节\n", sizeof(int*));
printf("char* 指针的大小: %zu 字节\n", sizeof(char*));
printf("double* 指针的大小: %zu 字节\n", sizeof(double*));
printf("void* 指针的大小: %zu 字节\n", sizeof(void*));

指针算术

指针算术的基本概念

指针算术是指对指针进行加减运算,指针算术的结果取决于指针所指向的类型的大小。例如,对于int*类型的指针,ptr++会使指针向前移动sizeof(int)个字节。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 指针算术
int arr[] = {10, 20, 30, 40, 50};
int *arr_ptr = arr; // 数组名是指向首元素的指针

printf("arr[0] = %d, *arr_ptr = %d\n", arr[0], *arr_ptr);
printf("arr[1] = %d, *(arr_ptr + 1) = %d\n", arr[1], *(arr_ptr + 1));
printf("arr[2] = %d, *(arr_ptr + 2) = %d\n", arr[2], *(arr_ptr + 2));

// 指针自增
arr_ptr++;
printf("arr_ptr++后, *arr_ptr = %d\n", *arr_ptr);

// 指针自减
arr_ptr--;
printf("arr_ptr--后, *arr_ptr = %d\n", *arr_ptr);

// 指针减法
int *ptr1 = &arr[4];
int *ptr2 = &arr[1];
printf("ptr1 - ptr2 = %ld (相差%d个元素)\n", ptr1 - ptr2, ptr1 - ptr2);

指针算术遍历数组

1
2
3
4
5
6
7
8
9
10
11
12
// 指针算术遍历数组
void pointer_arithmetic(int *arr, int size) {
printf("指针算术遍历数组: ");
for (int *p = arr; p < arr + size; p++) {
printf("%d ", *p);
}
printf("\n");
}

// 使用指针算术遍历数组
int numbers[] = {1, 2, 3, 4, 5};
pointer_arithmetic(numbers, 5);

补充示例:不同类型指针的算术运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 不同类型指针的算术运算
char char_arr[] = {'a', 'b', 'c', 'd', 'e'};
int int_arr[] = {1, 2, 3, 4, 5};

char *char_ptr = char_arr;
int *int_ptr = int_arr;

printf("char_ptr = %p\n", char_ptr);
char_ptr++;
printf("char_ptr++后 = %p (移动了%zu字节)\n", char_ptr, sizeof(char));

printf("int_ptr = %p\n", int_ptr);
int_ptr++;
printf("int_ptr++后 = %p (移动了%zu字节)\n", int_ptr, sizeof(int));

指针与数组

数组名作为指针

在C语言中,数组名可以看作是指向数组首元素的指针,因此可以使用指针的方式访问数组元素。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
// 指针与数组
int numbers[] = {1, 2, 3, 4, 5};
int *num_ptr = numbers;

printf("使用指针遍历数组: ");
for (int i = 0; i < 5; i++) {
printf("%d ", *(num_ptr + i));
}
printf("\n");

// 数组名作为指针
printf("numbers + 0 = %p, &numbers[0] = %p\n", numbers + 0, &numbers[0]);
printf("*(numbers + 2) = %d, numbers[2] = %d\n", *(numbers + 2), numbers[2]);

使用指针遍历数组

1
2
3
4
5
6
7
8
9
10
11
// 使用指针遍历数组
void print_array_with_pointer(int *arr, int size) {
for (int i = 0; i < size; i++) {
printf("%d ", *(arr + i));
}
printf("\n");
}

// 使用函数遍历数组
int array[] = {10, 20, 30, 40, 50};
print_array_with_pointer(array, 5);

补充示例:指针与二维数组

1
2
3
4
5
6
7
8
9
10
// 指针与二维数组
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};

// 使用指针访问二维数组
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", *(*(matrix + i) + j));
}
printf("\n");
}

指针与函数

指针作为函数参数

指针作为函数参数可以实现以下功能:

  1. 修改函数外部变量的值
  2. 传递大型数据结构,避免拷贝开销
  3. 实现多返回值

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 使用指针交换两个变量
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}

// 修改指针指向的值
void modify_value(int *ptr) {
*ptr = *ptr * 2;
}

// 使用交换函数
int x = 10, y = 20;
printf("交换前: x = %d, y = %d\n", x, y);
swap(&x, &y);
printf("交换后: x = %d, y = %d\n", x, y);

// 使用修改值函数
int value = 50;
printf("修改前value的值: %d\n", value);
modify_value(&value);
printf("修改后value的值: %d\n", value);

补充示例:指针作为函数返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 指针作为函数返回值
int *create_array(int size) {
int *arr = (int *)malloc(size * sizeof(int));
if (arr == NULL) {
return NULL;
}
// 初始化数组
for (int i = 0; i < size; i++) {
arr[i] = i + 1;
}
return arr;
}

// 使用函数返回的指针
int *dynamic_array = create_array(5);
if (dynamic_array != NULL) {
printf("动态数组: ");
print_array_with_pointer(dynamic_array, 5);
free(dynamic_array); // 释放内存
}

指针数组和数组指针

指针数组

指针数组是一个数组,其元素是指针。声明语法如下:

1
类型 *数组名[数组大小];

数组指针

数组指针是一个指针,它指向一个数组。声明语法如下:

1
类型 (*指针名)[数组大小];

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 指针数组:存储指针的数组
int a = 1, b = 2, c = 3;
int *ptr_array[] = {&a, &b, &c};

printf("指针数组元素值: ");
for (int i = 0; i < 3; i++) {
printf("%d ", *(ptr_array[i]));
}
printf("\n");

// 数组指针:指向数组的指针
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*matrix_ptr)[3] = matrix; // 指向包含3个int元素的数组的指针

printf("数组指针访问二维数组: \n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", *(*(matrix_ptr + i) + j));
}
printf("\n");
}

补充示例:字符串指针数组

1
2
3
4
5
6
7
8
9
10
11
12
// 字符串指针数组
char *strings[] = {
"Hello",
"World",
"C",
"Language"
};

printf("字符串指针数组: \n");
for (int i = 0; i < 4; i++) {
printf("%s\n", strings[i]);
}

多级指针

多级指针的定义

多级指针是指向指针的指针,例如二级指针是指向一级指针的指针,三级指针是指向二级指针的指针,以此类推。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 多级指针
int value2 = 1000;
int *ptr_level1 = &value2; // 一级指针
int **ptr_level2 = &ptr_level1; // 二级指针
int ***ptr_level3 = &ptr_level2; // 三级指针

printf("value2的值: %d\n", value2);
printf("*ptr_level1: %d\n", *ptr_level1);
printf("**ptr_level2: %d\n", **ptr_level2);
printf("***ptr_level3: %d\n", ***ptr_level3);

// 通过三级指针修改原始值
***ptr_level3 = 2000;
printf("通过三级指针修改后value2的值: %d\n", value2);

补充示例:二级指针与二维数组

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
// 二级指针与二维数组
int rows = 2, cols = 3;

// 使用二级指针创建二维数组
int **dynamic_matrix = (int **)malloc(rows * sizeof(int *));
for (int i = 0; i < rows; i++) {
dynamic_matrix[i] = (int *)malloc(cols * sizeof(int));
}

// 初始化二维数组
int count = 1;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
dynamic_matrix[i][j] = count++;
}
}

// 打印二维数组
printf("动态二维数组: \n");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", dynamic_matrix[i][j]);
}
printf("\n");
}

// 释放内存
for (int i = 0; i < rows; i++) {
free(dynamic_matrix[i]);
}
free(dynamic_matrix);

const指针和指向const的指针

指向const的指针

指向const的指针是指不能通过指针修改所指向对象的值的指针,声明语法如下:

1
2
3
const 类型 *指针名;
// 或
类型 const *指针名;

const指针

const指针是指指针本身的地址不能改变的指针,声明语法如下:

1
类型 *const 指针名;

指向const的const指针

指向const的const指针是指既不能修改指针指向,也不能修改所指对象的值的指针,声明语法如下:

1
const 类型 *const 指针名;

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// const指针和指向const的指针
int const_val = 500;

// 指向const的指针:不能通过指针修改所指对象的值
const int *const_ptr = &const_val;
printf("const_ptr指向的值: %d\n", *const_ptr);
// *const_ptr = 600; // 错误:不能修改const对象的值

// const指针:指针本身的地址不能改变
int mutable_val = 700;
int *const fixed_ptr = &mutable_val;
printf("fixed_ptr指向的值: %d\n", *fixed_ptr);
*fixed_ptr = 800; // 可以修改所指对象的值
printf("修改后fixed_ptr指向的值: %d\n", *fixed_ptr);
// fixed_ptr = &const_val; // 错误:不能改变const指针的指向

// 指向const的const指针:既不能修改指针指向,也不能修改所指对象的值
const int *const const_fixed_ptr = &const_val;
printf("const_fixed_ptr指向的值: %d\n", *const_fixed_ptr);
// *const_fixed_ptr = 900; // 错误:不能修改所指对象的值
// const_fixed_ptr = &mutable_val; // 错误:不能改变指针指向

空指针和野指针

空指针

空指针是指向NULL的指针,NULL是一个宏定义,表示一个空地址。空指针通常用于初始化指针或表示指针不指向任何有效的内存位置。

野指针

野指针是指未初始化的指针,它指向随机的内存地址,使用野指针是非常危险的,可能会导致程序崩溃或数据损坏。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 空指针和野指针
// 空指针:指向NULL的指针
int *null_ptr = NULL;
printf("空指针的值: %p\n", null_ptr);

// 避免使用野指针
int *wild_ptr; // 未初始化的指针,指向随机地址
// printf("*wild_ptr = %d\n", *wild_ptr); // 危险:野指针解引用

// 正确做法:初始化指针或设置为NULL
int *safe_ptr = NULL;

// 使用指针前检查是否为NULL
if (safe_ptr != NULL) {
printf("*safe_ptr = %d\n", *safe_ptr);
} else {
printf("safe_ptr是NULL指针,不能解引用\n");
}

补充示例:指针的安全使用

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
// 指针的安全使用
int *ptr = NULL;
int value = 100;

// 分配内存
ptr = (int *)malloc(sizeof(int));
if (ptr == NULL) {
printf("内存分配失败\n");
return 1;
}

// 使用指针
*ptr = value;
printf("*ptr = %d\n", *ptr);

// 释放内存后将指针设置为NULL
free(ptr);
ptr = NULL;

// 再次检查指针
if (ptr != NULL) {
printf("*ptr = %d\n", *ptr);
} else {
printf("ptr已被设置为NULL\n");
}

指向函数的指针

函数指针的定义

函数指针是指向函数的指针,它可以存储函数的地址,并通过指针调用函数。声明语法如下:

1
返回类型 (*指针名)(参数类型1, 参数类型2, ...);

示例代码

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
// 指向函数的指针
// 声明指向函数的指针
int (*func_ptr)(int, int);

// 简单的加法函数
int add(int a, int b) {
return a + b;
}

// 简单的减法函数
int subtract(int a, int b) {
return a - b;
}

// 将函数地址赋值给指针
func_ptr = add;

// 使用指针调用函数
int result = func_ptr(10, 20);
printf("使用函数指针调用add(10, 20) = %d\n", result);

// 切换函数指针指向
func_ptr = subtract;
result = func_ptr(10, 20);
printf("使用函数指针调用subtract(10, 20) = %d\n", result);

补充示例:函数指针作为参数

1
2
3
4
5
6
7
8
9
10
11
12
// 函数指针作为参数
void calculate_and_print(int a, int b, int (*operation)(int, int)) {
int result = operation(a, b);
printf("结果: %d\n", result);
}

// 使用函数指针作为参数
printf("加法: ");
calculate_and_print(10, 20, add);

printf("减法: ");
calculate_and_print(10, 20, subtract);

补充示例:函数指针数组

1
2
3
4
5
6
// 函数指针数组
int (*operations[])(int, int) = {add, subtract};

printf("使用函数指针数组:\n");
printf("加法: %d\n", operations[0](10, 20));
printf("减法: %d\n", operations[1](10, 20));

指针的高级应用

动态内存分配

指针与动态内存分配密切相关,使用malloccallocreallocfree函数可以在运行时分配和释放内存。

示例代码

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
// 动态内存分配
int *dynamic_arr = NULL;
int size = 5;

// 分配内存
dynamic_arr = (int *)malloc(size * sizeof(int));
if (dynamic_arr == NULL) {
printf("内存分配失败\n");
return 1;
}

// 初始化动态数组
for (int i = 0; i < size; i++) {
dynamic_arr[i] = i + 1;
}

// 打印动态数组
printf("动态数组: ");
print_array_with_pointer(dynamic_arr, size);

// 重新分配内存
size = 10;
dynamic_arr = (int *)realloc(dynamic_arr, size * sizeof(int));
if (dynamic_arr == NULL) {
printf("内存重新分配失败\n");
return 1;
}

// 初始化新分配的内存
for (int i = 5; i < size; i++) {
dynamic_arr[i] = i + 1;
}

// 打印重新分配后的动态数组
printf("重新分配后的动态数组: ");
print_array_with_pointer(dynamic_arr, size);

// 释放内存
free(dynamic_arr);
dynamic_arr = NULL; // 避免野指针

指针与结构体

指针可以指向结构体,使用指针访问结构体成员可以提高访问效率,特别是对于大型结构体。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 指针与结构体
typedef struct {
int id;
char name[50];
float score;
} Student;

// 使用指针访问结构体
Student student = {1, "张三", 95.5};
Student *student_ptr = &student;

printf("学生信息:\n");
printf("ID: %d\n", student_ptr->id);
printf("姓名: %s\n", student_ptr->name);
printf("分数: %.1f\n", student_ptr->score);

// 修改结构体成员
student_ptr->score = 98.0;
printf("修改后的分数: %.1f\n", student.score);

补充示例:链表的实现

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// 链表的实现
typedef struct Node {
int data;
struct Node *next;
} Node;

// 创建新节点
Node *create_node(int data) {
Node *new_node = (Node *)malloc(sizeof(Node));
if (new_node == NULL) {
return NULL;
}
new_node->data = data;
new_node->next = NULL;
return new_node;
}

// 添加节点到链表尾部
void append_node(Node **head, int data) {
Node *new_node = create_node(data);
if (new_node == NULL) {
return;
}

if (*head == NULL) {
*head = new_node;
return;
}

Node *current = *head;
while (current->next != NULL) {
current = current->next;
}
current->next = new_node;
}

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

// 释放链表
void free_list(Node *head) {
Node *temp;
while (head != NULL) {
temp = head;
head = head->next;
free(temp);
}
}

// 使用链表
Node *head = NULL;
append_node(&head, 10);
append_node(&head, 20);
append_node(&head, 30);
append_node(&head, 40);

printf("链表: ");
print_list(head);

// 释放链表
free_list(head);

指针的常见错误

常见错误及解决方案

  1. 空指针解引用:尝试访问NULL指针指向的内存

    • 解决方案:使用指针前检查是否为NULL
  2. 野指针:使用未初始化的指针

    • 解决方案:总是初始化指针,要么指向有效内存,要么设置为NULL
  3. 内存泄漏:动态分配的内存没有释放

    • 解决方案:使用free函数释放动态分配的内存
  4. 指针越界:访问超出数组范围的内存

    • 解决方案:确保指针操作在有效范围内
  5. 释放后使用:使用已释放的内存

    • 解决方案:释放内存后将指针设置为NULL
  6. 常量指针修改:尝试修改const指针指向的值

    • 解决方案:尊重const修饰符,不要尝试修改const对象
  7. 指针类型不匹配:将一种类型的指针赋值给另一种类型的指针

    • 解决方案:使用正确的类型转换,确保类型安全
  8. 多级指针错误:多级指针的解引用层级错误

    • 解决方案:确保多级指针的解引用层级与指针声明匹配

指针的最佳实践

  1. 始终初始化指针:声明指针时立即初始化,要么指向有效内存,要么设置为NULL
  2. 使用指针前检查是否为NULL:避免空指针解引用
  3. 释放动态内存:使用free函数释放动态分配的内存
  4. 释放内存后将指针设置为NULL:避免野指针
  5. 使用const修饰符:对于不需要修改的指针或指向的值,使用const修饰
  6. 使用有意义的变量名:指针变量名应清晰表达其用途
  7. 避免过多的指针层级:通常不超过2-3级指针
  8. 使用指针算术时要小心:确保指针操作在有效范围内
  9. 使用函数指针提高代码灵活性:对于需要动态选择函数的场景
  10. 学习指针与数组、函数的关系:深入理解C语言的底层机制

综合示例

示例1:使用指针实现字符串反转

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
// 使用指针实现字符串反转
void reverse_string(char *str) {
if (str == NULL) {
return;
}

char *start = str;
char *end = str + strlen(str) - 1;
char temp;

while (start < end) {
// 交换字符
temp = *start;
*start = *end;
*end = temp;

// 移动指针
start++;
end--;
}
}

// 使用字符串反转函数
char str[] = "Hello, World!";
printf("原始字符串: %s\n", str);
reverse_string(str);
printf("反转后: %s\n", str);

示例2:使用指针实现快速排序

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
44
45
46
47
48
49
// 使用指针实现快速排序
void quick_sort(int *arr, int left, int right) {
if (left >= right) {
return;
}

int pivot = arr[left];
int i = left + 1;
int j = right;
int temp;

while (i <= j) {
while (i <= right && arr[i] < pivot) {
i++;
}
while (j >= left && arr[j] > pivot) {
j--;
}
if (i < j) {
// 交换元素
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
} else {
break;
}
}

// 交换pivot和arr[j]
temp = arr[left];
arr[left] = arr[j];
arr[j] = temp;

// 递归排序
quick_sort(arr, left, j - 1);
quick_sort(arr, j + 1, right);
}

// 使用快速排序
int numbers[] = {5, 2, 9, 1, 5, 6};
int size = sizeof(numbers) / sizeof(numbers[0]);

printf("排序前: ");
print_array_with_pointer(numbers, size);

quick_sort(numbers, 0, size - 1);

printf("排序后: ");
print_array_with_pointer(numbers, size);

总结

C语言中的指针是一种强大的特性,通过本文档的学习,我们了解了:

  1. 指针的基本概念:指针是存储内存地址的变量,可以通过指针访问和修改内存中的数据
  2. 指针算术:指针可以进行加减运算,运算结果取决于指针所指向的类型
  3. 指针与数组:数组名可以看作是指向数组首元素的指针,指针可以用于遍历和操作数组
  4. 指针与函数:指针可以作为函数参数和返回值,实现参数的引用传递和动态内存分配
  5. 指针数组和数组指针:指针数组是存储指针的数组,数组指针是指向数组的指针
  6. 多级指针:指向指针的指针,可以用于更复杂的数据结构
  7. const指针:用于保护数据不被修改,提高代码的安全性
  8. 空指针和野指针:空指针指向NULL,野指针是未初始化的指针,应避免使用野指针
  9. 指向函数的指针:可以存储函数的地址,实现函数的动态调用
  10. 指针的高级应用:动态内存分配、链表实现、字符串操作等

掌握指针的使用对于编写高效、灵活的C程序至关重要,但同时也需要小心使用,避免常见的指针错误。通过不断练习和实践,你将能够熟练运用指针来解决各种编程问题。

pointers.c

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
#include <stdio.h>
#include <stdlib.h>

/**
* @brief 指针示例程序
* @details 这个程序演示了C语言中指针的各种用法,包括指针声明、
* 初始化、解引用、指针算术、指针与数组关系、指针与函数关系
* 以及多级指针等高级用法。
* @return 0 表示程序成功执行
*/

// =========================
// 函数原型声明
// =========================
void swap(int *a, int *b); // 使用指针交换两个变量
void print_array_with_pointer(int *arr, int size); // 使用指针遍历数组
void modify_value(int *ptr); // 修改指针指向的值
void pointer_arithmetic(int *arr, int size); // 指针算术示例

/**
* @brief 主函数
* @return 0 表示程序成功执行
*/
int main() {
// =========================
// 指针的基本概念
// =========================
printf("=== 指针的基本概念 ===\n");

int num = 100;
int *ptr; // 声明一个指向int类型的指针

ptr = &num; // 初始化指针,指向变量num的地址

printf("变量num的值: %d\n", num);
printf("变量num的地址: %p\n", &num);
printf("指针ptr存储的地址: %p\n", ptr);
printf("指针ptr解引用得到的值: %d\n", *ptr);

// 通过指针修改变量的值
*ptr = 200;
printf("通过指针修改后num的值: %d\n", num);

printf("\n");

// =========================
// 指针算术
// =========================
printf("=== 指针算术 ===\n");

int arr[] = {10, 20, 30, 40, 50};
int *arr_ptr = arr; // 数组名是指向首元素的指针

printf("arr[0] = %d, *arr_ptr = %d\n", arr[0], *arr_ptr);
printf("arr[1] = %d, *(arr_ptr + 1) = %d\n", arr[1], *(arr_ptr + 1));
printf("arr[2] = %d, *(arr_ptr + 2) = %d\n", arr[2], *(arr_ptr + 2));

// 指针自增
arr_ptr++;
printf("arr_ptr++后, *arr_ptr = %d\n", *arr_ptr);

// 指针自减
arr_ptr--;
printf("arr_ptr--后, *arr_ptr = %d\n", *arr_ptr);

// 指针减法
int *ptr1 = &arr[4];
int *ptr2 = &arr[1];
printf("ptr1 - ptr2 = %ld (相差%d个元素)\n", ptr1 - ptr2, ptr1 - ptr2);

// 调用指针算术函数
pointer_arithmetic(arr, 5);

printf("\n");

// =========================
// 指针与数组
// =========================
printf("=== 指针与数组 ===\n");

int numbers[] = {1, 2, 3, 4, 5};
int *num_ptr = numbers;

printf("使用指针遍历数组: ");
print_array_with_pointer(num_ptr, 5);

// 数组名作为指针
printf("numbers + 0 = %p, &numbers[0] = %p\n", numbers + 0, &numbers[0]);
printf("*(numbers + 2) = %d, numbers[2] = %d\n", *(numbers + 2), numbers[2]);

printf("\n");

// =========================
// 指针与函数
// =========================
printf("=== 指针与函数 ===\n");

int x = 10, y = 20;
printf("交换前: x = %d, y = %d\n", x, y);

// 传递地址给函数,实现交换
swap(&x, &y);
printf("交换后: x = %d, y = %d\n", x, y);

// 修改指针指向的值
int value = 50;
printf("修改前value的值: %d\n", value);
modify_value(&value);
printf("修改后value的值: %d\n", value);

printf("\n");

// =========================
// 指针数组和数组指针
// =========================
printf("=== 指针数组和数组指针 ===\n");

// 指针数组:存储指针的数组
int a = 1, b = 2, c = 3;
int *ptr_array[] = {&a, &b, &c};

printf("指针数组元素值: ");
for (int i = 0; i < 3; i++) {
printf("%d ", *(ptr_array[i]));
}
printf("\n");

// 数组指针:指向数组的指针
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*matrix_ptr)[3] = matrix; // 指向包含3个int元素的数组的指针

printf("数组指针访问二维数组: \n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", *(*(matrix_ptr + i) + j));
}
printf("\n");
}

printf("\n");

// =========================
// 多级指针
// =========================
printf("=== 多级指针 ===\n");

int value2 = 1000;
int *ptr_level1 = &value2;
int **ptr_level2 = &ptr_level1; // 二级指针
int ***ptr_level3 = &ptr_level2; // 三级指针

printf("value2的值: %d\n", value2);
printf("*ptr_level1: %d\n", *ptr_level1);
printf("**ptr_level2: %d\n", **ptr_level2);
printf("***ptr_level3: %d\n", ***ptr_level3);

// 通过三级指针修改原始值
***ptr_level3 = 2000;
printf("通过三级指针修改后value2的值: %d\n", value2);

printf("\n");

// =========================
// const指针和指向const的指针
// =========================
printf("=== const指针和指向const的指针 ===\n");

int const_val = 500;

// 指向const的指针:不能通过指针修改所指对象的值
const int *const_ptr = &const_val;
printf("const_ptr指向的值: %d\n", *const_ptr);
// *const_ptr = 600; // 错误:不能修改const对象的值

// const指针:指针本身的地址不能改变
int mutable_val = 700;
int *const fixed_ptr = &mutable_val;
printf("fixed_ptr指向的值: %d\n", *fixed_ptr);
*fixed_ptr = 800; // 可以修改所指对象的值
printf("修改后fixed_ptr指向的值: %d\n", *fixed_ptr);
// fixed_ptr = &const_val; // 错误:不能改变const指针的指向

// 指向const的const指针:既不能修改指针指向,也不能修改所指对象的值
const int *const const_fixed_ptr = &const_val;
printf("const_fixed_ptr指向的值: %d\n", *const_fixed_ptr);

printf("\n");

// =========================
// 空指针和野指针
// =========================
printf("=== 空指针和野指针 ===\n");

// 空指针:指向NULL的指针
int *null_ptr = NULL;
printf("空指针的值: %p\n", null_ptr);

// 避免使用野指针
int *wild_ptr; // 未初始化的指针,指向随机地址
// printf("*wild_ptr = %d\n", *wild_ptr); // 危险:野指针解引用

// 正确做法:初始化指针或设置为NULL
int *safe_ptr = NULL;

printf("\n");

// =========================
// 指向函数的指针
// =========================
printf("=== 指向函数的指针 ===\n");

// 声明指向函数的指针
int (*func_ptr)(int, int);

// 简单的加法函数
int add(int a, int b) {
return a + b;
}

// 将函数地址赋值给指针
func_ptr = add;

// 使用指针调用函数
int result = func_ptr(10, 20);
printf("使用函数指针调用add(10, 20) = %d\n", result);

return 0;
}

// =========================
// 函数定义
// =========================

/**
* @brief 使用指针交换两个变量的值
* @param a 指向第一个变量的指针
* @param b 指向第二个变量的指针
*/
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}

/**
* @brief 使用指针遍历数组
* @param arr 指向数组首元素的指针
* @param size 数组的大小
*/
void print_array_with_pointer(int *arr, int size) {
for (int i = 0; i < size; i++) {
printf("%d ", *(arr + i));
}
printf("\n");
}

/**
* @brief 修改指针指向的值
* @param ptr 指向要修改的变量的指针
*/
void modify_value(int *ptr) {
*ptr = *ptr * 2;
}

/**
* @brief 演示指针算术操作
* @param arr 指向数组首元素的指针
* @param size 数组的大小
*/
void pointer_arithmetic(int *arr, int size) {
printf("指针算术遍历数组: ");
for (int *p = arr; p < arr + size; p++) {
printf("%d ", *p);
}
printf("\n");
}