C语言进阶知识点详解
一、指针的高级应用
(一)指针与数组
1. 数组名的本质
在C语言中,数组名在大多数情况下会被隐式地转换为指向数组首元素的指针。例如,对于一个整型数组int arr[5];
,arr
在表达式中(除了作为sizeof
运算符的操作数和&
运算符的操作数)会被转换为指向数组第一个元素(arr[0]
)的指针,其类型是int*
。这使得我们可以用指针的方式来访问数组元素。比如*(arr + i)
和arr[i]
是等价的,都是访问数组的第i
个元素。
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5}; // 定义一个整型数组
int* ptr = arr; // 将数组名赋给指针变量,数组名隐式转换为指向首元素的指针
// 使用指针访问数组元素
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, *(ptr + i)); // *(ptr + i)等价于arr[i]
}
return 0;
}
输出结果:
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
由于数组名是地址常量,不能对数组名进行赋值操作来改变它的指向。例如,arr = arr + 1;
是非法的。
2. 多维数组与指针
对于多维数组,如int arr[3][4];
,数组名arr
是一个指向数组第一行的指针,其类型是int (*)[4]
。这意味着arr
指向的是一个包含4个整数的数组。我们可以通过*(arr + i)
来访问第i
行,而*(arr + i) + j
可以用来访问第i
行第j
列的元素。
#include <stdio.h>
int main() {
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
}; // 定义一个二维数组
// 使用指针访问二维数组元素
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("arr[%d][%d] = %d\n", i, j, *(*(arr + i) + j));
}
}
return 0;
}
输出结果:
arr[0][0] = 1
arr[0][1] = 2
arr[0][2] = 3
arr[0][3] = 4
arr[1][0] = 5
arr[1][1] = 6
arr[1][2] = 7
arr[1][3] = 8
arr[2][0] = 9
arr[2][1] = 10
arr[2][2] = 11
arr[2][3] = 12
当我们将多维数组传递到函数中时,函数的参数声明可以利用指针来实现。例如,如果要传递一个二维数组到函数中,函数原型可以写为void func(int (*p)[4])
,其中p
是一个指向包含4个整数的数组的指针。在函数体内,可以通过(*p)[i][j]
或者p[i][j]
来访问数组元素。
#include <stdio.h>
// 函数声明,参数是一个指向包含4个整数的数组的指针
void printArray(int (*p)[4], int rows) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", p[i][j]); // 访问二维数组元素
}
printf("\n");
}
}
int main() {
int arr[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
printArray(arr, 3); // 调用函数,传递二维数组
return 0;
}
输出结果:
1 2 3 4
5 6 7 8
9 10 11 12
(二)指针与函数
1. 函数指针
函数指针是一种特殊的指针,它指向的是函数的入口地址。函数指针的声明方式是返回值类型 (*指针变量名)(参数列表)
。例如,int (*func_ptr)(int, int);
声明了一个函数指针func_ptr
,它可以指向一个返回整型值并且有两个整型参数的函数。
#include <stdio.h>
// 定义一个函数,返回两个整数的和
int add(int a, int b) {
return a + b;
}
// 定义一个函数指针,指向返回整型值并且有两个整型参数的函数
int (*func_ptr)(int, int);
int main() {
func_ptr = add; // 将函数add的地址赋给函数指针func_ptr
int result = func_ptr(3, 4); // 通过函数指针调用函数
printf("Result: %d\n", result);
return 0;
}
输出结果:
Result: 7
函数指针可以用于实现回调函数。例如,在一个图形用户界面程序中,当用户点击按钮时,可以将一个函数指针作为参数传递给按钮的事件处理函数,这样当按钮被点击时,就可以调用这个函数指针指向的函数来处理事件。
函数指针还可以用于函数数组。如果有一组功能相似但具体实现不同的函数,可以将它们的地址存储在一个函数指针数组中,然后通过索引调用不同的函数。例如,int (*func_array[5])(int);
声明了一个包含5个函数指针的数组,每个函数指针都指向一个返回整型值并且有一个整型参数的函数。
#include <stdio.h>
// 定义几个功能不同的函数
int addOne(int a) {
return a + 1;
}
int subtractOne(int a) {
return a - 1;
}
int multiplyByTwo(int a) {
return a * 2;
}
int divideByTwo(int a) {
return a / 2;
}
int square(int a) {
return a * a;
}
int main() {
// 定义一个函数指针数组
int (*func_array[5])(int) = {addOne, subtractOne, multiplyByTwo, divideByTwo, square};
int num = 10;
for (int i = 0; i < 5; i++) {
printf("Function %d result: %d\n", i + 1, func_array[i](num));
}
return 0;
}
输出结果:
Function 1 result: 11
Function 2 result: 9
Function 3 result: 20
Function 4 result: 5
Function 5 result: 100
2. 指针作为函数参数
当指针作为函数参数时,函数可以通过指针直接修改调用者传入的变量。这与值传递不同,值传递只是将变量的副本传递给函数,函数内部对参数的修改不会影响调用者的变量。
例如,有一个函数void swap(int* a, int* b)
,它的作用是交换两个整数变量的值。在函数体内,可以通过*a
和*b
来访问和修改传入的变量。当调用swap(&x, &y)
时,x
和y
的值就会被交换。这种方式可以避免返回多个值时使用结构体或者数组来传递返回值,提高了代码的简洁性。
#include <stdio.h>
// 定义一个函数,交换两个整数变量的值
void swap(int* a, int* b) {
int temp = *a; // 通过指针访问变量的值
*a = *b; // 修改变量的值
*b