【C语言】(15)指针进阶

1. 指针与const

在C语言中,const关键字和指针一起使用时,可以创建对常量的引用,或者创建指向常量的指针。这对于保护重要数据不被意外修改以及提高程序的可读性和运行时的安全性非常有用。

1.1 const的基本用法

const关键字用于声明一个变量为常量,这意味着一旦被初始化之后,它的值就不能被修改。

c 复制代码
const int a = 10; // a是一个常量,不能被修改
// a = 20; // 错误:尝试修改常量的值

1.2 指针与const结合的用法

1.2.1 指向常量的指针

const关键字出现在星号*的左边时,它意味着指针所指向的数据是不可修改的,但指针本身可以修改。

c 复制代码
const int *ptr; // 指向整型常量的指针
int const *ptr; // 同上,与上一行等价
int value = 5;
ptr = &value; // 合法操作
//*ptr = 10; // 非法操作:不能通过ptr修改value的值

1.2.2 常量指针

const关键字出现在星号*的右边时,它意味着指针本身是常量,即不能指向其他地址,但通过指针指向的数据可以被修改。

c 复制代码
int *const ptr; // ptr是一个常量指针
int value1 = 5, value2 = 10;
ptr = &value1; // 非法操作:ptr不能指向其他地址
*ptr = 20; // 合法操作:可以通过ptr修改value1的值

1.2.3 指向常量的常量指针

const关键字同时出现在星号*的左边和右边时,既指针所指向的数据也是常量,指针本身也是常量。

c 复制代码
const int *const ptr = &value; // ptr是一个指向整型常量的常量指针
//ptr = &value2; // 非法操作:ptr不能指向其他地址
//*ptr = 10; // 非法操作:不能通过ptr修改value的值

总结

  • 使用const可以定义不可变的常量。
  • const int *ptr表示ptr可以变,但ptr指向的值不能变。
  • int *const ptr表示ptr指向的值可以变,但ptr本身不能变。
  • const int *const ptr表示ptrptr指向的值都不能变。
  • 适当使用const可以增加程序的安全性和可读性。

2. 指针数组

数组指针是指向数组的指针。理解数组指针对于高效地处理数组和执行复杂的内存操作非常重要。

2.1 数组与指针的基本关系

数组名本身就是一个指向数组第一个元素的指针。

c 复制代码
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // 等同于 int *ptr = &arr[0];

通过指针,我们可以访问和修改数组中的元素。

2.2 数组指针的声明

数组指针通常指向一个整个数组,而不仅仅是数组的第一个元素。声明数组指针的语法如下:

c 复制代码
type (*arrayPtr)[size];

这里,type 是数组元素的类型,size 是数组的大小,arrayPtr 是指向数组的指针变量名。

示例:声明一个指向整型数组的指针

c 复制代码
int (*ptrToArray)[5];
int array[5] = {1, 2, 3, 4, 5};
ptrToArray = &array;

2.3 通过数组指针访问数组元素

使用数组指针访问数组元素时,需要使用解引用操作符(*)和下标。

c 复制代码
// 假设 ptrToArray 指向一个包含 5 个整数的数组
for (int i = 0; i < 5; i++) {
    printf("%d ", (*ptrToArray)[i]);
}

在这个例子中,(*ptrToArray) 解引用数组指针,访问整个数组,然后我们使用下标[i]访问数组的具体元素。

2.4 数组指针与多维数组

数组指针对于处理多维数组尤其有用,因为它可以简化对多维数组的访问和操作。

示例:声明一个指向二维整型数组的指针

c 复制代码
int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*ptrTo2DArray)[3] = arr; // 或者 &arr[0]

使用数组指针访问二维数组元素:

c 复制代码
for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++) {
        printf("%d ", ptrTo2DArray[i][j]);
    }
    printf("\n");
}

在这个例子中,我们没有使用(*ptrTo2DArray)来解引用指针,因为当使用数组指针作为多维数组的指针时,可以直接使用下标来访问元素。

总结

  • 数组指针是指向整个数组的指针。
  • 声明数组指针时,需要指定数组的类型和大小。
  • 使用数组指针可以方便地访问和操作数组元素,特别是在处理多维数组时。
  • 数组指针在高级编程、动态内存分配以及函数参数传递中非常有用。

3. 函数指针

3.1 函数指针的声明

函数指针的声明包含了函数的返回类型、指针名称以及函数的参数列表。基本语法如下:

c 复制代码
返回类型 (*指针变量名)(参数类型列表);

例如,声明一个指向返回类型为 int,参数为两个 int 类型的函数的指针,可以这样写:

c 复制代码
int (*funcPtr)(int, int);

3.2 使用函数指针

一旦声明了函数指针,就可以将它指向具有相应签名的任何函数。例如,如果有一个这样的函数:

c 复制代码
int add(int a, int b) {
    return a + b;
}

可以这样将函数 add 赋值给之前声明的 funcPtr

c 复制代码
funcPtr = add;

3.3 通过函数指针调用函数

通过函数指针调用函数的语法与直接调用函数类似,只不过函数名被替换为函数指针变量名。例如,使用 funcPtr 调用 add 函数:

c 复制代码
int result = funcPtr(3, 5); // 调用add函数,参数为3和5

3.4 函数指针作为参数

函数指针最强大的用途之一是作为其他函数的参数。这允许你在运行时动态地改变函数的行为。例如:

c 复制代码
void printOperationResult(int (*operation)(int, int), int a, int b) {
    int result = operation(a, b);
    printf("Result: %d\n", result);
}

这个 printOperationResult 函数接受一个函数指针 operation 作为参数,并用 ab 作为参数调用它,然后打印结果。

3.5 返回函数指针的函数

函数也可以返回函数指针。声明这样的函数时,需要在返回类型前加上 (*) 和参数列表。例如:

c 复制代码
int (*getOperation(char op))(int, int) {
    switch(op) {
        case '+':
            return add;
        // 可以添加更多的case来返回不同的函数指针
        default:
            return NULL;
    }
}

这个 getOperation 函数根据传入的操作符返回相应的函数指针。

函数指针数组是C语言中的一个高级特性,它允许你在一个数组中存储多个指向函数的指针。这种技术非常适合于实现函数表、回调函数列表或者状态机等。下面是一个关于如何使用函数指针数组的详细教程。

3.6 函数指针数组

3.6.1 函数指针数组的声明

函数指针数组的声明类似于普通数组的声明,但数组的元素类型是函数指针。基本语法如下:

c 复制代码
返回类型 (*数组名[])(参数类型列表);

例如,声明一个指向返回类型为 int,参数为两个 int 类型的函数的函数指针数组:

c 复制代码
int (*operationArray[])(int, int);

3.6.2 初始化函数指针数组

在声明函数指针数组的同时,你可以初始化它,将数组中的每个元素指向具体的函数。例如:

c 复制代码
int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

// 初始化函数指针数组
int (*operationArray[])(int, int) = {add, subtract};

3.6.3 通过索引调用数组中的函数

你可以通过索引来访问函数指针数组中的元素,并通过这些函数指针调用函数。例如:

c 复制代码
int result1 = operationArray[0](5, 3); // 调用add函数
int result2 = operationArray[1](5, 3); // 调用subtract函数

3.6.4. 遍历函数指针数组

你也可以遍历函数指针数组,对数组中的每个函数指针进行操作。例如:

c 复制代码
int operations(int a, int b, int (*opsArray[])(int, int), int size) {
    for (int i = 0; i < size; i++) {
        printf("Operation %d result: %d\n", i, opsArray[i](a, b));
    }
}

int main() {
    int (*operationArray[])(int, int) = {add, subtract};
    int size = sizeof(operationArray) / sizeof(operationArray[0]);
    operations(10, 5, operationArray, size);
    return 0;
}

3.6.5 函数指针数组作为函数参数

函数指针数组可以作为参数传递给其他函数,这在实现插件系统、事件处理系统时特别有用。传递函数指针数组作为参数的方法与传递普通数组类似。

3.7 回调函数

回调函数是一种在软件开发中常用的技术,它允许一个函数的行为被另一个函数在特定事件或条件发生时动态指定或"回调"。在C语言中,回调函数通常通过函数指针来实现。这种机制允许编写更通用、更灵活的代码,特别是在事件处理、异步编程和接口设计中非常有用。

3.7.1 定义回调函数

定义一个回调函数就是定义一个普通的函数,然后将这个函数的地址(通过函数名)传递给另一个函数,后者在适当的时刻调用这个回调函数。

例如,我们定义一个回调函数和一个接受这个回调函数作为参数的函数:

c 复制代码
// 回调函数
void myCallback(int a) {
    printf("Callback called with %d\n", a);
}

// 接受回调函数作为参数的函数
void performActionWithCallback(void (*callbackFunc)(int), int value) {
    // 在适当的时候调用回调函数
    callbackFunc(value);
}
3.7.2 使用回调函数

使用回调函数时,只需要将回调函数作为参数传递给另一个函数即可。例如:

c 复制代码
int main() {
    // 调用performActionWithCallback,传递myCallback作为回调函数
    performActionWithCallback(myCallback, 5);
    return 0;
}
相关推荐
xiaoshiguang32 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡3 小时前
【C语言】判断回文
c语言·学习·算法
别NULL3 小时前
机试题——疯长的草
数据结构·c++·算法
TT哇3 小时前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯
yuanbenshidiaos4 小时前
C++----------函数的调用机制
java·c++·算法
唐叔在学习4 小时前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA4 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
chengooooooo4 小时前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc5 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
FeboReigns5 小时前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++