【C语言】指针的“最后一站”【进阶版】

 欢迎各位看官^_^

目录

1、字符指针

2、指针数组

3、数组指针

3.1数组指针的定义

3.2数组指针的使用 

4、数组指针和指针数组的区别

5、数组参数,指针参数

5.1数组参数定义

5.2指针参数定义 

5.3一维数组传参

5.4二维数组传参

5.5一级指针传参

5.6二级指针传参 

6、函数指针

7、函数指针数组

8、指向函数指针数组的指针

9、函数指针和指针函数的差别

10、回调函数


1、字符指针

        字符指针是指一个指向字符数组(或字符串)的指针变量。 它可以用来指向一个字符串常量或动态分配的字符数组,可以通过指针来访问数组中的每一个字符。

        在C语言中,字符指针通常用于字符串的处理,可以使用字符串函数(如strcpy、strlen、strcat等)对指针所指向的字符串进行处理和操作。

char* str = "Hello";   //定义一个字符指针,指向字符串常量"Hello"
printf("%c\n", *str);  //输出字符'H'

         另一种常见的用法是动态分配一个字符数组,并使用字符指针指向该数组,例如:

char* str = (char*) malloc(10 * sizeof(char));  //动态分配一个大小为10的字符数组
strcpy(str, "Hello");   //将字符串"Hello"复制到数组中
printf("%s\n", str);    //输出字符串"Hello"

2、指针数组

        指针数组是一个数组,其元素都是指针类型。 每个指针元素都可以指向不同的内存地址,从而使整个数组成为一个指向多个不同对象的数组。 指针数组在C/C++编程中很常用,特别是在动态内存分配和字符串处理等方面。 例如,可以通过指针数组来存储多个字符串,每个指针元素都指向一个字符串的起始地址。 

        指针数组的创建方式与普通数组类似,只是数组元素类型为指针类型。 创建指针数组的步骤如下:

1.声明指针数组的变量名和类型:指针数组的类型是由元素类型和数组长度共同决定的,可以使用如下语法声明指针数组:

int* arr[10];

        上述语句表示声明了一个包含10个int指针元素的指针数组,每个数组元素可以指向一个int类型的变量或数组。

2.分配内存空间:指针数组存储的是指针类型的变量,每个数组元素需要单独分配内存空间。 可以使用如下语法来为指针数组分配内存:

for(int i=0; i<10; i++){
    arr[i] = (int*)malloc(sizeof(int));
}

        上述语句使用循环为每个数组元素分配了一个int类型的内存空间,并将其地址赋值给指针数组的对应元素。

3.使用指针数组:指针数组可以像普通数组一样使用,例如可以使用下标操作符[]来访问数组元素,也可以通过指针数组访问存储在其他内存位置的数据。

*arr[0] = 10;  //为第一个元素指向的内存存储赋值
int a = *arr[0]; //获取第一个元素指向的内存存储的值

3、数组指针

3.1数组指针的定义

        数组指针是一种指针类型,它指向一个数组。 它是指向数组第一个元素的指针,而不是指向数组本身。 在定义数组指针时需要指定指针类型和数组类型,并使用“*”符号来标识指针类型。 例如,定义一个int类型的数组指针:

int arr[5];
int *p = arr;

        其中,p是一个指向arr数组的指针,它指向arr[0]元素的地址。 数组指针可以用于访问数组元素,可以使用p[index]的方式来访问数组中的元素,其中index表示要访问的元素的下标。

3.2数组指针的使用 

        数组指针可以用来访问二维数组中的元素。 一个数组指针指向一个一维数组,而一个二维数组可以理解为一系列的一维数组。 因此,通过使用数组指针,可以访问这些一维数组。

例如,假设有一个二维数组:

int arr[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

        我们可以使用指向整数的指针来定义一个指向第二行的指针:

int *p = arr[1];

        此时,p指向的是数组中的第五个元素(即arr[1][0]),我们可以使用指针运算符来访问其它的元素:

p[0]; // 5
p[1]; // 6
p[2]; // 7
p[3]; // 8

        我们还可以使用指向整型指针的指针来定义一个指向整个二维数组的指针:

int (*p2)[4] = arr;

        此时,p2指向的是整个二维数组的第一个元素,我们可以使用指针运算符来访问其它的元素:

p2[0][0]; // 1
p2[0][1]; // 2
p2[1][0]; // 5
p2[2][3]; // 12

4、数组指针和指针数组的区别

        数组指针和指针数组是两个不同的概念。

        数组指针指向一个数组,它本身是一个指针变量,只不过这个指针变量指向的是一个数组。

        指针数组是一个数组,其中每个元素都是一个指针。它本身是一个数组,只不过这个数组中的每个元素都是指针。

        在使用时,数组指针和指针数组的访问方式也不同。对于数组指针,我们可以使用指针运算符来访问数组中的元素;而对于指针数组,我们需要先访问数组中的某个元素,再使用指针运算符来访问该元素指向的对象。

举个例子,对于一个数组指针,我们可以这样访问它所指向的第一个元素:

int arr[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};
int (*p)[4] = arr;  //定义数组指针p,指向arr的第一行
int *p1 = *p;  //取得p指向的第一行的指针
int val = p1[0];  //访问第一个元素,即arr[0][0]

        而对于一个指针数组,我们可以这样访问它的第一个元素:

int a = 1, b = 2, c = 3;
int *arr[] = {&a, &b, &c};  //定义指针数组arr,包含3个int型指针元素
int *p = arr[0];  //访问arr的第一个元素,即&a
int val = *p;  //val的值为1,即a的值

        因此,数组指针和指针数组虽然有相似的地方,但在定义和使用上还是有较大的区别的。

5、数组参数,指针参数

5.1数组参数定义

        在C语言中,可以将数组作为函数的参数传递。传递数组参数时,可以使用以下两种方法:

1. 将数组名作为指针传递:

void func(int* array, int num_elements) {
    // ...
}

int main() {
    int my_array[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    func(my_array, 10);
    return 0;
}

2. 将数组名作为数组传递:

void func(int array[], int num_elements) {
    // ...
}

int main() {
    int my_array[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    func(my_array, 10);
    return 0;
}

        注意:在传递数组参数时,需要同时传递数组的大小,因为C语言不支持在函数内部获取数组的大小。可以在函数参数中传递数组大小,也可以使用预定义的常量或者宏定义来传递数组大小。

5.2指针参数定义 

        在C语言中,可以通过指针参数来实现函数内部对变量值的修改,也可以通过指针参数来实现函数间的数据共享。指针参数的一般形式为:

void func(int *ptr);

        其中,`int *ptr`表示一个指向整型变量的指针,也可以理解为一个整型变量的地址。

在函数内部,可以通过`*ptr`来访问该地址对应的变量,例如:

void func(int *ptr) {
    *ptr = 100;  // 修改指向的变量的值为100
}

同时,也可以将该指针指向另外一个变量的地址,例如:

void func(int *ptr) {
    int a = 100;
    ptr = &a;   // 将指针指向变量a的地址
}

        但是需要注意的是,这里的指针参数只是函数内部的一个局部变量,对它的修改不会影响函数外部的指针变量。如果需要改变函数外部的指针变量,可以使用双重指针(指向指针的指针)或者引用参数等方法来实现。

5.3一维数组传参

        在C语言中,一维数组可以通过指针来进行传参。具体步骤如下:

1. 定义函数时,需要将数组参数定义为指针类型:

void myFunction(int *arr, int size);

2. 在调用函数时,直接传入数组的名称即可:

int myArray[10];
myFunction(myArray, 10);

3. 在函数中使用指针来访问数组中的元素:

void myFunction(int *arr, int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
}

        注意:数组参数在函数中仍然是指针,因此可以使用指针运算来访问数组元素。另外,由于C语言传参是值传递,因此如果需要修改数组中的元素,需要使用指针来实现。

5.4二维数组传参

        在C语言中,可以通过指针来传递二维数组。由于C语言中二维数组本质上是一维数组的数组,因此可以将其视为指向一维数组的指针。传递二维数组时,需要指定数组的行数和列数。下面是一个示例:

#include <stdio.h>

void printArray(int arr[][3], int rows, int cols)
{
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}

int main()
{
    // 定义一个二维数组
    int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};

    // 调用函数打印数组
    printArray(arr, 2, 3);
    
    return 0;
}

        在上面的示例中,`printArray` 函数接受一个二维数组 `arr` 和数组的行数和列数 `rows` 和 `cols` 作为参数。函数内部使用两个嵌套的循环遍历整个二维数组并打印每个元素的值。

        在 `main` 函数中,定义了一个二维数组 `arr` 并初始化。然后调用 `printArray` 函数传递数组 `arr` 和 `2` 和 `3` 作为行数和列数的参数来打印数组的值。

5.5一级指针传参

        在C语言中,可以通过一级指针来传递参数。一级指针是指指向变量的指针,可以通过指针操作符(*)来间接访问指针所指向的变量。

下面是一个例子,演示了如何在函数中使用一级指针来传参:

#include <stdio.h>

// swap函数用来交换两个整数
void swap(int *a, int *b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

int main() {
    int x = 10, y = 20;

    printf("Before swapping: x = %d, y = %d\n", x, y);

    // 传递指向x和y的指针给swap函数
    swap(&x, &y);

    printf("After swapping: x = %d, y = %d\n", x, y);

    return 0;
}

        在上面的代码中,我们定义了一个函数`swap()`,这个函数接受两个指向整数的指针。我们在`main()`函数中声明了两个整数变量`x`和`y`,然后通过`&`运算符来获取它们的地址,并将这些地址作为参数传递给`swap()`函数。在`swap()`函数中,我们通过指针操作符(*)来获取变量的值,并通过交换它们来实现了两个整数的交换。

        需要注意的是,当我们传递指针作为参数时,如果函数修改了指针所指向的变量的值,那么函数结束后这些修改会被保留下来。 因此,一级指针可以被用来实现类似于返回多个值的操作。

5.6二级指针传参 

        在C语言中,指针是一种非常重要的数据类型,它可以保存变量的内存地址,从而通过指针来操作变量。而二级指针则更为复杂,它可以保存指向指针变量的指针的内存地址。二级指针在传递函数参数时,可以通过该指针来改变函数外部的指针变量。

以下是C语言使用二级指针传参的示例:

#include<stdio.h>
void func(int **pp) {
    int a = 10;
    *pp = &a;
}

int main() {
    int *p;
    func(&p); // 传递指向指针变量的指针
    printf("%d", *p); // 输出a的值,但由于a的生命周期已结束,此处输出的值不确定
    return 0;
}

        在上述示例中,函数`func`接收一个指向指针变量的指针,并将其指向一个局部变量`a`的地址。在`main`函数中,通过传递指向指针变量的指针`&p`来改变指针变量`p`的地址,从而使其指向了`a`的地址。但由于`a`是局部变量,当`func`函数执行完毕后,`a`的生命周期已经结束,因此输出`*p`的值是不确定的。

        需要注意的是,如果在函数`func`中不使用双重指针的方式来修改指针变量的值,则只会影响到函数内部的指针变量,无法改变函数外部的指针变量。因此在需要修改指针变量的值时,需要使用二级指针来传递参数。

6、函数指针

        C语言函数指针是指向函数的指针变量,它可以存储函数的地址并调用该函数。在C语言中,定义函数指针的方法是将函数名作为指针变量的类型,然后在使用指针变量时,用它来调用函数。

        以下是一个简单的函数指针的例子,它声明一个指向函数的指针变量,然后将函数的地址赋值给指针变量并调用该函数:

#include <stdio.h>

int add(int a, int b) {
   return a + b;
}

int main() {
   int (*func_ptr)(int, int) = &add;
   int result = (*func_ptr)(10, 5);
   printf("%d", result);  // 输出 15
   return 0;
}

        在上面的例子中,我们声明了一个指向函数的指针变量 `func_ptr`,并将函数 `add` 的地址赋值给它。然后,我们通过指针变量调用 `add` 函数,并将结果存储在 `result` 变量中。最后,我们打印 `result` 变量的值,输出 15。

7、函数指针数组

        函数指针数组是数组中的每个元素都是函数指针的数据结构。在 C 语言中,函数指针是指向函数的指针变量。函数指针可以被用来调用函数,或者用来作为参数传递给其它函数。

下面是一个例子,展示如何声明和使用函数指针数组:

#include <stdio.h>

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 (*func_ptr[3]) (int, int);
    func_ptr[0] = &add;
    func_ptr[1] = &subtract;
    func_ptr[2] = &multiply;
    
    int a = 10, b = 5;
    for (int i = 0; i < 3; i++) {
        int result = (*func_ptr[i])(a, b);
        printf("The result is: %d\n", result);
    }
    
    return 0;
}

        在上面的例子中,`func_ptr`是一个函数指针数组,其中有三个元素,每个元素都是一个指向函数的指针。在 `main` 函数中,我们将 `add`、`subtract` 和 `multiply` 函数的地址分别赋予 `func_ptr` 数组中的相应元素。

        在循环中,我们通过 `(*func_ptr[i])(a, b)` 的方式来调用函数指针数组中的每个元素。注意到我们使用了 `(*)` 操作符来解引用函数指针,这是因为函数名本身在 C 语言中就是函数的地址,使用 `(*)` 操作符可以将函数指针解引用,得到函数的返回值。

该程序的输出结果是:
The result is: 15
The result is: 5
The result is: 50

        对于这个例子,我们可以看到函数指针数组可以用来简化代码,提高程序的灵活性和可维护性。根据需要,我们可以改变函数指针数组中的元素,来调用不同的函数或者实现不同的功能。

8、指向函数指针数组的指针

在C语言中,指向函数指针数组的指针可以通过以下方式声明:

typedef void (*FuncPtr)(int);  // 声明一个函数指针类型
FuncPtr func[3];               // 声明一个函数指针数组
FuncPtr (*funcP)[3];           // 声明一个指向函数指针数组的指针

        上述代码中,先定义了一个函数指针类型 `FuncPtr`,它可以指向一个具有一个int类型参数和void返回类型的函数。然后定义了一个函数指针数组 `func`,它包含3个元素,每个元素都是一个函数指针。最后,定义了一个指向函数指针数组的指针 `funcP`,它指向包含3个 `FuncPtr` 类型元素的数组。

        由于 `func` 是一个函数指针数组,因此它可以按照常规方式进行初始化:

void func1(int x) { printf("Func1: %d\n", x); }
void func2(int x) { printf("Func2: %d\n", x); }
void func3(int x) { printf("Func3: %d\n", x); }

func[0] = func1;
func[1] = func2;
func[2] = func3;

        然后,将指向函数指针数组的指针 `funcP` 指向 `func` 数组的首地址:

funcP = &func;

        现在,可以使用指针 `funcP` 来调用函数指针数组中的任何一个函数指针:

(*funcP)[0](10);  // 调用func1函数指针,传入参数10
(*funcP)[1](20);  // 调用func2函数指针,传入参数20
(*funcP)[2](30);  // 调用func3函数指针,传入参数30

9、函数指针和指针函数的差别

        函数指针和指针函数是两个不同的概念。虽然它们都涉及指向函数或指向指针的指针,但它们在语法和语义上有不同的含义。

·函数指针是一个指向函数的指针,它存储函数的地址,可以像函数调用一样使用它。函数指针在C语言中被广泛使用,通常用于实现回调函数、动态函数调用和函数指针数组等。
  
        例如,下面的代码定义了一个函数指针`fp`,它指向一个函数,该函数有两个`int`类型的参数并返回一个`int`类型的值:

int (*fp)(int, int);

·指针函数是一个返回指针的函数,它本身是一个函数,但返回值是指针类型。指针函数也在C语言中使用,通常用于分配内存、字符串操作和复杂数据结构的声明等。

         例如,下面的代码定义了一个指针函数`func`,它返回一个指向`int`类型的指针:

int* func();

        所以,函数指针和指针函数虽然都涉及指向指针的指针,但它们的概念和用法是不同的。函数指针是指向函数的指针,指向函数的地址,而指针函数是函数,返回一个指针类型的指针。

10、回调函数

        C语言回调函数是一种函数指针,用于在一段代码中传递一个函数,以供另一段代码调用。

        回调函数通常用于实现事件驱动编程,即当某个事件发生时,调用预定义的回调函数进行处理。例如,当用户按下一个按钮时,回调函数可以执行与该按钮相关的操作。

以下是一个C语言回调函数的示例:

#include <stdio.h>

typedef void (*callback_t)(int);

void process(callback_t callback) {
    printf("Processing...\n");
    callback(42);
}

void callback_function(int value) {
    printf("Callback received value: %d\n", value);
}

int main() {
    process(callback_function);
    return 0;
}

        在上面的示例中,`process`函数接受一个类型为`callback_t`的函数指针作为参数,并在该函数内部调用该函数指针。`callback_function`函数是一个预定义的回调函数,它接受一个`int`类型的参数,并输出该参数的值。

        在`main`函数中,我们将`callback_function`作为参数传递给`process`函数,然后程序输出以下内容:


Processing...
Callback received value: 42
 

        这表明`process`函数成功地调用了我们传递的回调函数,并将参数`42`传递给它。

 🤞❤️🤞❤️🤞❤️指针的知识点总结就到这里啦,如果对博文还满意的话,劳烦各位看官动动“发财的小手”留下您对博文的赞和对博主的关注吧🤞❤️🤞❤️🤞❤️

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十三衙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值