文章

C 指针管理

AUTOGEN bacde31b46b24152a7ec72010e43b52c

C 指针管理

1. 指针定义

1
int p;

这是一个普通的整型变量

1
int* p;

首先从P 处开始,先与*结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以P是一个返回整型数据的指针

1
int p[3]; 

首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组

1
int* p[3];

首先从P 处开始,先与[]结合,因为其优先级比高,所以P 是一个数组,然后再与结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组

1
int(*p)[3];

首先从P 处开始,先与*结合,说明P 是一个指针然后再与[]结合(与”()”这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针

1
int** p;

首先从P 开始,先与结合,说是P 是一个指针,然后再与结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.

1
int p(int);

从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据

1
Int(*p)(int);

从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针

1
int* (*p(int))[3];

可以先跳过,不看这个类型,过于复杂从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的结合,说明函数返回的是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数.

2. 指针指向

指针作为一种普通的数据结构,有自己的地址,也存放着值,但是指针的值是一个地址值,可以把地址赋给指针。 下图中,指针p存放的是arr的地址,而arr作为数组来讲,arr的第一个元素的地址也就是arr的首地址; 那么访问p的地址就访问了arr的地址,p的地址值加上4(这个4也就是sizeof int)就访问到了arr的下一个元素。 image-20240804224344058

3. 指向指针的指针

代表这个指针可以指向一个指针,因为指针也有地址,这个地址可以被指向,同理*也成立。

1
2
3
4
5
6
7
8
9
#include <iostream>
using namespace std;
int main() {
	int i = 0;
	int *p = &i;
	int **pp = &p;
	int ***ppp = &pp;
	cout << ***ppp;
}

但是上一级指针只能指向相邻的下一级指针,不能进行跨级指向。 例如***ppp = &p就是错误的。

4. 函数指针

在C语言中,函数指针是一种特殊的指针类型,它存储了函数的地址,从而能够调用该函数。函数指针的使用提供了一种灵活的编程方式,允许动态调用函数和实现回调机制。

函数指针(Function Pointers)

  1. 声明函数指针

    • 声明函数指针时,需要指定它指向的函数的返回类型、名称和参数列表。
    1
    2
    
    c// 假设有一个函数int add(int, int)
    int (*functionPtr)(int, int);
    
  2. 初始化函数指针

    • 将函数指针初始化为一个函数的地址。
    1
    2
    
    c
    functionPtr = add;
    
  3. 调用函数指针

    • 通过函数指针调用函数,就像直接调用函数一样。
    1
    2
    
    c
    int result = functionPtr(3, 4); // 调用add函数
    

函数指针数组(Function Pointer Arrays)

  1. 声明函数指针数组

    • 声明一个数组,其元素都是指向相同类型函数的指针。
    1
    2
    
    c
    int (*functionArray[10])(int, int); // 函数指针数组,可以存储10个函数指针
    
  2. 使用函数指针数组

    • 可以将不同的函数地址赋给数组的元素,并按需调用。
    1
    2
    3
    
    cfunctionArray[0] = add;
    functionArray[1] = subtract; // 假设subtract是另一个函数
    // ...
    
  3. 调用数组中的函数

    • 通过数组索引来选择并调用相应的函数。
    1
    2
    
    c
    int result = functionArray[0](10, 5); // 调用add函数
    

回调函数(Callback Functions)

  1. 回调函数的概念

    • 回调函数是一种通过函数指针传递给其他函数的函数,允许在另一个函数中“回调”或调用它。
  2. 定义回调函数

    • 定义一个符合特定签名的函数,该签名与期望回调的函数指针类型匹配。
    1
    2
    3
    
    cvoid callback(int param) {
        // 处理回调逻辑
    }
    
  3. 使用回调函数

    • 将回调函数的地址作为参数传递给另一个函数,并在该函数内部调用。
    1
    2
    3
    4
    5
    6
    
    cvoid executeWithCallback(void (*callbackFunc)(int), int param) {
        // 执行一些操作
        callbackFunc(param); // 调用回调函数
    }
       
    executeWithCallback(callback, 42); // 传递callback函数的地址
    

示例

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
c#include <stdio.h>

// 定义两个函数,它们具有相同的签名
void greet(const char *message) {
    printf("%s\n", message);
}

void farewell(const char *message) {
    printf("%s\n", message);
}

// 定义一个函数指针类型,指向上述函数
typedef void (*MessageFunc)(const char *);

// 使用函数指针调用函数
void callFunction(MessageFunc func, const char *message) {
    func(message); // 通过函数指针调用函数
}

int main() {
    // 将函数的地址赋给函数指针
    MessageFunc functionPtr = greet;
    functionPtr("Hello, World!"); // 输出: Hello, World!

    // 使用函数指针数组
    MessageFunc functions[] = {greet, farewell};
    functions[1]("Goodbye, World!"); // 输出: Goodbye, World!

    // 使用回调函数
    callFunction(greet, "Welcome to the callback!"); // 输出: Welcome to the callback!

    return 0;
}

函数指针、函数指针数组和回调函数是C语言中非常强大的特性,它们提供了高度的灵活性和动态性。通过这些特性,可以实现复杂的编程模式,如事件处理、多态行为的模拟等。然而,使用函数指针时也需要小心,以避免错误和潜在的内存问题。

本文由作者按照 CC BY 4.0 进行授权