指针是 C 语言的一大特色,也是其最强大和灵活的部分之一。指针的本质是一个变量,它存储的是另一个变量的内存地址。通过指针,可以间接访问和操作内存中的数据。
本节将全面讲解 C 语言中的指针,包括基础知识、常见用法、高级技巧以及注意事项。
指针是一个变量,它的值是另一个变量的地址(内存位置)。
数据类型 *指针变量名;
*
表示这是一个指针变量。#include
int main() {
int x = 10; // 定义一个普通变量
int *p = &x; // 定义一个指针变量,存储 x 的地址
printf("x = %d\n", x); // 输出变量 x 的值
printf("&x = %p\n", &x); // 输出变量 x 的地址
printf("p = %p\n", p); // 输出指针 p 的值(即 x 的地址)
printf("*p = %d\n", *p); // 输出指针 p 指向的值(即 x 的值)
return 0;
}
输出:
x = 10
&x = 0x7ffeeab0c8ac // x 的地址(示例地址)
p = 0x7ffeeab0c8ac // 指针 p 的值(指向 x 的地址)
*p = 10 // 指针 p 指向的值(x 的值)
取地址运算符 &
用于获取变量的地址。
int x = 10;
int *p = &x; // p 存储 x 的地址
解引用运算符 *
用于访问指针指向的地址所存储的值。
int x = 10;
int *p = &x;
printf("%d\n", *p); // 输出 x 的值
通过指针传递参数,可以让函数直接修改调用者提供的变量。
#include
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 5, y = 10;
printf("Before swap: x = %d, y = %d\n", x, y);
swap(&x, &y); // 传递变量的地址
printf("After swap: x = %d, y = %d\n", x, y);
return 0;
}
输出:
Before swap: x = 5, y = 10
After swap: x = 10, y = 5
#include
int main() {
int arr[] = {1, 2, 3, 4, 5};
int *p = arr; // 指针指向数组首元素
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d, *(p + %d) = %d\n", i, arr[i], i, *(p + i));
}
return 0;
}
输出:
arr[0] = 1, *(p + 0) = 1
arr[1] = 2, *(p + 1) = 2
arr[2] = 3, *(p + 2) = 3
arr[3] = 4, *(p + 3) = 4
arr[4] = 5, *(p + 4) = 5
字符串可以用字符数组表示,也可以用指针操作。
#include
int main() {
char str[] = "Hello, World!";
char *p = str;
while (*p != '\0') { // 遍历字符串,直到遇到结束符 '\0'
printf("%c", *p);
p++;
}
printf("\n");
return 0;
}
输出:
Hello, World!
指针是动态内存管理的核心,使用 malloc
、calloc
和 free
等函数操作堆内存。
#include
#include
int main() {
int n = 5;
int *arr = (int *)malloc(n * sizeof(int)); // 动态分配内存
if (arr == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// 初始化数组
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}
// 打印数组
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr); // 释放内存
return 0;
}
输出:
1 2 3 4 5
指针可以指向另一个指针,形成多级指针。
#include
int main() {
int x = 10;
int *p = &x; // 指针指向 x
int **pp = &p; // 二级指针指向 p
printf("x = %d\n", x);
printf("*p = %d\n", *p); // 指针 p 的值
printf("**pp = %d\n", **pp); // 二级指针 pp 的值
return 0;
}
输出:
x = 10
*p = 10
**pp = 10
函数也有地址,可以通过指针调用函数。
#include
int add(int a, int b) {
return a + b;
}
int main() {
int (*funcPtr)(int, int) = add; // 定义函数指针并赋值
int result = funcPtr(5, 10); // 通过函数指针调用函数
printf("Result = %d\n", result);
return 0;
}
输出:
Result = 15
指针初始化:
int *p = NULL;
指针越界:
悬空指针:
free
后),指针仍然存储旧地址,此时称为悬空指针。NULL
。类型匹配:
概念/操作 | 描述 |
---|---|
取地址符 & |
获取变量的地址。 |
解引用符 * |
访问指针指向的地址中的值。 |
指针与数组 | 数组名是指向数组首元素的指针,指针可以操作数组元素。 |
函数参数传递 | 使用指针传递参数,可以直接修改调用者的变量。 |
动态内存分配 | 使用 malloc 、calloc 和 free 操作堆内存。 |
多级指针 | 指针可以指向另一个指针,形成多级指针。 |
函数指针 | 函数有地址,可以用指针调用函数。 |
指针是 C 语言高效操作内存的核心工具,但由于其灵活性,也容易引发错误。在实际开发中,使用指针时应特别注意初始化、边界检查和内存管理,以避免潜在的安全问题。
在前面我们已经讲解了 C 语言中指针的基础知识及常见用法。接下来我们将继续探讨指针的高级特性、实际开发中的使用技巧,以及各种指针相关的问题与解决方案。
数组名在 C 中本质上是一个指针,但多维数组的指针操作稍复杂。对于二维数组,指针可以用于访问具体的行和列。
#include
int main() {
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
int (*p)[3] = matrix; // 定义一个指向二维数组的指针
printf("Using pointer arithmetic:\n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", *(*(p + i) + j)); // 通过指针访问二维数组元素
}
printf("\n");
}
return 0;
}
输出:
1 2 3
4 5 6
关键点:
p
是一个指向二维数组的指针。p + i
表示第 i
行,*(p + i) + j
表示第 i
行第 j
列。动态分配二维数组是指在程序运行时分配内存空间,而不是使用静态数组。
#include
#include
int main() {
int rows = 2, cols = 3;
// 动态分配二维数组
int **matrix = (int **)malloc(rows * sizeof(int *));
for (int i = 0; i < rows; i++) {
matrix[i] = (int *)malloc(cols * sizeof(int));
}
// 初始化数组
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix[i][j] = i * cols + j + 1;
}
}
// 打印数组
printf("Dynamic 2D array:\n");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
// 释放内存
for (int i = 0; i < rows; i++) {
free(matrix[i]);
}
free(matrix);
return 0;
}
输出:
Dynamic 2D array:
1 2 3
4 5 6
注意事项:
free
释放每行的内存后,还需释放数组本身的内存。对于三维或更高维数组,指针可以通过多级解引用或偏移量访问特定元素。
#include
int main() {
int cube[2][2][3] = {
{
{1, 2, 3},
{4, 5, 6}
},
{
{7, 8, 9},
{10, 11, 12}
}
};
int (*p)[2][3] = cube; // 指向三维数组的指针
printf("Accessing 3D array using pointer:\n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 3; k++) {
printf("%d ", *(*(*(p + i) + j) + k)); // 通过多级解引用访问元素
}
printf("\n");
}
printf("\n");
}
return 0;
}
输出:
1 2 3
4 5 6
7 8 9
10 11 12
我们可以定义一个函数指针数组,用于存储多个函数的地址。
#include
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;
}
int main() {
// 定义函数指针数组
int (*operations[3])(int, int) = {add, subtract, multiply};
int x = 10, y = 5;
printf("Add: %d\n", operations[0](x, y)); // 调用 add 函数
printf("Subtract: %d\n", operations[1](x, y)); // 调用 subtract 函数
printf("Multiply: %d\n", operations[2](x, y)); // 调用 multiply 函数
return 0;
}
输出:
Add: 15
Subtract: 5
Multiply: 50
回调函数是通过函数指针实现的一种机制,允许一个函数在另一个函数的上下文中被调用。
#include
// 回调函数类型
void operation(int a, int b, void (*callback)(int)) {
int result = a + b;
callback(result); // 调用回调函数
}
void printResult(int result) {
printf("Result: %d\n", result);
}
int main() {
operation(10, 20, printResult); // 将 printResult 作为回调函数传递
return 0;
}
输出:
Result: 30