引言
指针是C语言最强大也最具挑战性的特性。很多初学者对指针感到恐惧,但一旦掌握了指针,你就真正理解了C语言的精髓。
本文将深入讲解指针的高级应用,包括函数指针、指针数组、多级指针等高级技巧,帮助你写出更灵活、更高效的代码。
一、指针基础回顾
什么是指针?
指针是一个变量,它存储的是另一个变量的内存地址。通过指针,我们可以间接访问和修改该变量的值。
#include <stdio.h>
int main() {
int num = 42;
int* ptr = # // ptr指向num的地址
printf("num的值: %d\n", num);
printf("num的地址: %p\n", (void*)&num);
printf("ptr的值(地址): %p\n", (void*)ptr);
printf("ptr指向的值: %d\n", *ptr);
// 通过指针修改值
*ptr = 100;
printf("修改后num的值: %d\n", num);
return 0;
}
二、指针与数组
2.1 数组名就是指针
// 数组名本质上是指向第一个元素的指针
void arrayPointerDemo() {
int arr[] = {10, 20, 30, 40, 50};
// 以下三种方式等价
printf("arr[0] = %d\n", arr[0]);
printf("*arr = %d\n", *arr);
printf("*(arr + 0) = %d\n", *(arr + 0));
// 指针遍历数组
int* ptr = arr;
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, *(ptr + i));
}
}
2.2 动态数组
// 使用指针创建动态数组
void dynamicArrayDemo() {
int size;
printf("请输入数组大小: ");
scanf("%d", &size);
// 动态分配内存
int* arr = (int*)malloc(size * sizeof(int));
if (!arr) {
printf("内存分配失败\n");
return;
}
// 初始化数组
for (int i = 0; i < size; i++) {
arr[i] = (i + 1) * 10;
}
// 使用数组
printf("动态数组内容: ");
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// 释放内存
free(arr);
}
三、指针数组与数组指针
3.1 指针数组(Array of Pointers)
指针数组是一个数组,其中每个元素都是指针:
// 指针数组示例:字符串数组
void pointerArrayDemo() {
// 指针数组,每个元素指向一个字符串
char* fruits[] = {
"Apple",
"Banana",
"Orange",
"Grape"
};
int count = sizeof(fruits) / sizeof(fruits[0]);
printf("水果列表:\n");
for (int i = 0; i < count; i++) {
printf("[%d] %s\n", i, fruits[i]);
}
}
3.2 数组指针(Pointer to Array)
数组指针是一个指向整个数组的指针:
// 数组指针示例
void arrayPointerDemo2() {
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 数组指针,指向包含4个int的数组
int (*ptr)[4] = arr;
printf("二维数组内容:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%3d ", ptr[i][j]);
}
printf("\n");
}
}
区别记忆:
• int* arr[5] - 指针数组(5个指针)
• int (*arr)[5] - 数组指针(指向含5个int的数组)
四、函数指针
函数指针是指向函数的指针,可以用来实现回调函数、策略模式等高级功能。
4.1 函数指针基础
// 定义几个简单函数
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
int multiply(int a, int b) {
return a * b;
}
void functionPointerBasic() {
// 声明函数指针
int (*operation)(int, int);
// 指向add函数
operation = add;
printf("5 + 3 = %d\n", operation(5, 3));
// 指向subtract函数
operation = subtract;
printf("5 - 3 = %d\n", operation(5, 3));
// 指向multiply函数
operation = multiply;
printf("5 * 3 = %d\n", operation(5, 3));
}
4.2 回调函数应用
// 比较函数类型
typedef int (*CompareFunc)(const void*, const void*);
// 通用排序函数(使用回调)
void bubbleSort(void* arr, int n, int elemSize, CompareFunc compare) {
unsigned char* data = (unsigned char*)arr;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
void* elem1 = data + j * elemSize;
void* elem2 = data + (j + 1) * elemSize;
if (compare(elem1, elem2) > 0) {
// 交换元素
unsigned char temp[elemSize];
memcpy(temp, elem1, elemSize);
memcpy(elem1, elem2, elemSize);
memcpy(elem2, temp, elemSize);
}
}
}
}
// 整数比较函数
int compareInt(const void* a, const void* b) {
return (*(int*)a - *(int*)b);
}
// 字符串比较函数
int compareString(const void* a, const void* b) {
return strcmp(*(char**)a, *(char**)b);
}
void callbackDemo() {
// 排序整数数组
int nums[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(nums) / sizeof(nums[0]);
printf("排序前: ");
for (int i = 0; i < n; i++) {
printf("%d ", nums[i]);
}
printf("\n");
bubbleSort(nums, n, sizeof(int), compareInt);
printf("排序后: ");
for (int i = 0; i < n; i++) {
printf("%d ", nums[i]);
}
printf("\n");
}
4.3 函数指针数组
// 计算器示例:使用函数指针数组
void calculatorDemo() {
// 定义操作函数
double add(double a, double b) { return a + b; }
double sub(double a, double b) { return a - b; }
double mul(double a, double b) { return a * b; }
double div(double a, double b) { return b != 0 ? a / b : 0; }
// 函数指针数组
double (*operations[])(double, double) = {add, sub, mul, div};
char* opNames[] = {"加法", "减法", "乘法", "除法"};
double x = 10.0, y = 3.0;
printf("计算 %.1f 和 %.1f:\n", x, y);
for (int i = 0; i < 4; i++) {
printf("%s: %.2f\n", opNames[i], operations[i](x, y));
}
}
五、多级指针
5.1 二级指针的应用
// 二级指针:在函数中修改指针本身
void modifyPointer(int** ptr, int* newValue) {
*ptr = newValue; // 修改指针指向
}
void doublePointerDemo() {
int a = 10, b = 20;
int* ptr = &a;
printf("初始: *ptr = %d\n", *ptr);
// 通过二级指针修改ptr的指向
modifyPointer(&ptr, &b);
printf("修改后: *ptr = %d\n", *ptr);
}
5.2 动态二维数组
// 使用二级指针创建动态二维数组
int** create2DArray(int rows, int cols) {
// 分配行指针数组
int** arr = (int**)malloc(rows * sizeof(int*));
// 为每行分配空间
for (int i = 0; i < rows; i++) {
arr[i] = (int*)malloc(cols * sizeof(int));
}
return arr;
}
void free2DArray(int** arr, int rows) {
for (int i = 0; i < rows; i++) {
free(arr[i]);
}
free(arr);
}
void dynamic2DArrayDemo() {
int rows = 3, cols = 4;
// 创建动态二维数组
int** matrix = create2DArray(rows, cols);
// 初始化
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j + 1;
}
}
// 打印
printf("动态二维数组:\n");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%3d ", matrix[i][j]);
}
printf("\n");
}
// 释放内存
free2DArray(matrix, rows);
}
六、完整可编译代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// ==================== 指针数组示例 ====================
void pointerArrayDemo() {
char* fruits[] = {"Apple", "Banana", "Orange", "Grape"};
int count = sizeof(fruits) / sizeof(fruits[0]);
printf("【指针数组】水果列表:\n");
for (int i = 0; i < count; i++) {
printf(" [%d] %s\n", i, fruits[i]);
}
}
// ==================== 函数指针基础 ====================
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
void functionPointerBasic() {
int (*operation)(int, int);
printf("\n【函数指针】基本运算:\n");
operation = add;
printf(" 5 + 3 = %d\n", operation(5, 3));
operation = subtract;
printf(" 5 - 3 = %d\n", operation(5, 3));
operation = multiply;
printf(" 5 * 3 = %d\n", operation(5, 3));
}
// ==================== 回调函数示例 ====================
typedef int (*CompareFunc)(const void*, const void*);
void bubbleSort(void* arr, int n, int elemSize, CompareFunc compare) {
unsigned char* data = (unsigned char*)arr;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
void* elem1 = data + j * elemSize;
void* elem2 = data + (j + 1) * elemSize;
if (compare(elem1, elem2) > 0) {
unsigned char temp[elemSize];
memcpy(temp, elem1, elemSize);
memcpy(elem1, elem2, elemSize);
memcpy(elem2, temp, elemSize);
}
}
}
}
int compareInt(const void* a, const void* b) {
return (*(int*)a - *(int*)b);
}
void callbackDemo() {
int nums[] = {64, 34, 25, 12, 22, 11, 90};
int n = sizeof(nums) / sizeof(nums[0]);
printf("\n【回调函数】排序演示:\n");
printf(" 排序前: ");
for (int i = 0; i < n; i++) printf("%d ", nums[i]);
printf("\n");
bubbleSort(nums, n, sizeof(int), compareInt);
printf(" 排序后: ");
for (int i = 0; i < n; i++) printf("%d ", nums[i]);
printf("\n");
}
// ==================== 动态二维数组 ====================
int** create2DArray(int rows, int cols) {
int** arr = (int**)malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++) {
arr[i] = (int*)malloc(cols * sizeof(int));
}
return arr;
}
void free2DArray(int** arr, int rows) {
for (int i = 0; i < rows; i++) {
free(arr[i]);
}
free(arr);
}
void dynamic2DArrayDemo() {
int rows = 3, cols = 4;
int** matrix = create2DArray(rows, cols);
printf("\n【动态二维数组】:\n");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j + 1;
printf("%3d ", matrix[i][j]);
}
printf("\n");
}
free2DArray(matrix, rows);
}
// ==================== 主函数 ====================
int main() {
printf("========================================\n");
printf(" C语言降龙十八掌 - 指针高级应用\n");
printf("========================================\n");
pointerArrayDemo();
functionPointerBasic();
callbackDemo();
dynamic2DArrayDemo();
printf("\n========================================\n");
printf(" 程序运行结束\n");
printf("========================================\n");
return 0;
}
编译说明: 将以上代码保存为 .c 文件,使用 gcc 编译:gcc advanced_pointers.c -o advanced_pointers
七、总结与要点
| 指针类型 | 声明语法 | 用途 |
|---|---|---|
| 普通指针 | int* ptr |
指向单个变量 |
| 指针数组 | int* arr[5] |
多个指针的集合 |
| 数组指针 | int (*ptr)[5] |
指向整个数组 |
| 函数指针 | int (*func)(int, int) |
指向函数,实现回调 |
| 二级指针 | int** ptr |
指向指针的指针 |
学习要点:
- 理解指针本质:指针存储的是地址,通过解引用访问值
- 区分指针类型:指针数组vs数组指针,不要混淆
- 函数指针威力:可以实现回调、策略模式等高级功能
- 内存管理:动态分配的内存必须手动释放
- 避免野指针:指针使用前必须初始化,使用后设为NULL
最佳实践:
- 始终初始化指针:int* ptr = NULL;
- 释放内存后立即置NULL:free(ptr); ptr = NULL;
- 使用typedef简化复杂指针声明
- 绘制内存图帮助理解多级指针
- 谨慎使用指针算术,注意边界检查
C语言降龙十八掌系列教程 | 掌握指针,掌握C语言的灵魂