指针,数组,函数那些容易混淆的概念

无论对C还是C++,指针都是其精华所在,但是也是难点所在,本文是为了梳理一些在指针,数组,函数三者中那些容易混淆的概念

  • 指针数组
  • 数组指针
  • 指针函数
  • 函数指针
  • 函数指针数组

指针和数组

指针数组

定义:datatype *arr[length]

指针数组,即在这个数组内,元素都是指针,那么length即数组内包含的指针个数,而指针的类型则跟datatype有关

举个例子,定义一个字符指针数据,然后访问内部元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
   char *arr[] ={ // 指针数组
"computer",
"science",
"and",
"technology"
};

printf("%s\n", arr[0]); // output : computer
printf("%s\n", arr[1]); // output : science
printf("%s\n", arr[2]); // output : and
printf("%s\n", arr[3]); // output : technology

printf("%c\n", *arr[0]);// output : c
printf("%c\n", *(arr[0] + 1));// output : o
printf("%c\n", *(*(arr + 0) + 2));// output : m

在这里能看出来,字符串格式化输出数组元素时,都能打印出对应的字符串,说明指针数组内部元素都是对应类型的指针,并且在下面的代码中,还利用了对应的指针获取对应元素。

数组指针

定义:datatype (*arr)[length]

数组指针,其实就是一个指针,一般用于指向二维数组的首地址,datatype即数组指针的类型,而这里的length可以比作“列数”,即跟指向的二维数组的列数要相等。并且这里添加了一个括号的原因是[]的优先级比*高,所以这里的括号不能省略,否则意义就不同了

举个例子,定义一个二维整型数组和整型数组指针,让数组指针指向该二维数组,并访问数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
   int nums[][3] = {
1, 2, 3,
4, 5, 6,
7, 8, 9
};
int (*arr)[3]; // 数组指针
arr = nums; // 数组指针指向二维数组

printf("%d\n", arr[0]); // output : 10485280
putchar('\n');
printf("%d\n", *(arr[0] + 0)); // output : 1
putchar('\n');
printf("%d\n", arr[0][1]); // output : 2
putchar('\n');
printf("%d\n", *(*(arr + 0) + 2)); // output : 3

从上面代码可以看到,数组指针可以直接指向同类型的二维数组,并且在使用时完全可以像一个二维数组一样获取二维数组的数据

总结

经过上面的分析可以看出数组指针和指针数组的不同了

  • 从定义上,指针数组是一个内部含有若干指针的数组,而数组指针则是一个指针
  • 当有一个二维数组时,同类型的数组指针可以用来直接指向,但是同类型的指针数组则不行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int i;
    int nums[][3] = {
    1, 2, 3,
    4, 5, 6,
    7, 8, 9
    };
    int *p[3];
    int (*q)[3];
    q = nums; // 数组指针指向二维数组

    for(i = 0; i < 3; i++){// 将二维数组的值赋给指针数组
    p[i] = nums[i]; // 指针数组内的每个指针指向二维数组对应行的首地址
    }

指针和函数

指针函数

定义:返回值类型 * 函数名(形参列表)

指针函数:即返回值是指针的函数,本质还是函数,只不过返回值是一个指针

举个例子:创建一个学生结构体,然后创建一个指针函数用来创建学生信息,然后再主函数打印输出学生信息

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
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct{
char name[10];
int age;
} Student;
Student* createInfo(char name[], int age)
{
Student *student = (Student*)malloc(sizeof(Student));
if (NULL == student)
return NULL;
strcpy(student->name, name);
student->age = age;
return student;
}
int main()
{
Student *student = createInfo("ZhangSan", 19);
if (NULL == student)
printf("Create failed");
else
printf("Student's name is %s, age is %d\n", student->name, student->age); // output : Student's name is ZhangSan, age is 19
return 0;
}

从上面代码中,可以看到,在主函数中,传入对应的值并调用createInfo函数,该函数(在开辟空间成功的情况下)就会返回一个带有对应信息的Student类型的指针变量,那么就可以利用该指针变量访问数据

函数指针

定义:类型 (* 指针名)(形参列表)

函数指针:仍然是一个指针,只不过是一个指向函数入口地址的指针

这里要说明的是,每一个函数无论是否带返回值或者形参列表,它都有一个对应的入口地址,当用函数指针指向函数时,两者的函数原型必须相一致,即返回值类型和形参列表都必须一致

例如我们需要一个返回值为整型的,参数为两个整型的函数指针时,只需做出如下声明即可:
int (*fun)(int x, int y);
当我们需要让函数指针指向特定函数时,

1
2
3
4
int do(int x, int y){ ... } // do函数,函数原型与fun函数指针相一致
... // 代码省略
fun = do; // 让函数指针fun指向do函数的入口地址时,两者都不需要括号或者形参列表
...

当函数指针指向特定函数时,便可以做对应函数的操作了

举个例子:现在输入一个算式,二元加减运算,中间不带空格,然后输出运算结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
int plus(int x, int y)
{
return x + y;
}
int minus(int x, int y)
{
return x - y;
}
int main()
{
int (*calc)(int x, int y); // 函数指针
int x, y, result;
char o;
scanf("%d%c%d", &x, &o, &y);//输入算式,eg:1+2,4-3...
if(o == '+')
calc = plus;
else if (o == '-')
calc = minus;
result = (*calc)(x, y);
printf("= %d\n", result);
return 0;
}

输入输出
1+2
= 3
4-3
= 1

在使用时,一定要注意函数指针和所指向的函数,这两者的函数原型要一致

总结

  • 指针函数是一个返回值是指针的函数,函数指针则是指向函数的指针

指针,数组,函数

函数指针数组

定义:类型 (*函数指针数组名[元素个数])(形参列表)

函数指针数组:一句话解释就是数组元素全是函数指针的数组

比如,定义一个类型为整型,有两个整型参数的函数指针数组
int (*fun[])(int x, int y);
相比于函数指针就是在名字的后面多了[]
那么如何初始化呢?
其实跟普通数组的初始化是一样的,这里先假设有加,减,乘三个方法,并且函数原型与函数指针数组相一致

  • 先声明后赋值
    1
    2
    3
    4
     int (*fun[3])(int x, int y); // 声明函数指针数组
    fun[0] = plus; // 加法函数
    fun[1] = minus;// 减法函数
    fun[2] = multiply;// 乘法函数
  • 声明的同时赋值
    1
    2
    3
    4
    5
    int (*fun[])(int x, int y){
    plus, // 加法函数
    minus, // 减法函数
    multiply // 乘法函数
    };

在使用的时候要记得带上下标,以及参数列表

1
fun[0](x, y); // 使用函数指针函数数组中的第一个函数

下面接着举个例子:有三个函数分别为返回值为整型,有两个整型参数的加,减,乘函数,然后定义一个函数指针数组并初始化,最后计算6和4的加,减,乘的结果并输出

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
#include <stdio.h>
int plus(int x, int y)
{
return x + y;
}
int minus(int x, int y)
{
return x - y;
}
int multiply(int x, int y)
{
return x * y;
}
int main()
{
int (*fun[])(int x, int y){
plus, // 加法函数
minus, // 减法函数
multiply // 乘法函数
};
int x = 6, y = 4;
printf("6 + 4 = %d\n", fun[0](x, y)); // output : 6 + 4 = 10
printf("6 - 4 = %d\n", fun[1](x, y)); // output : 6 - 4 = 2
printf("6 * 4 = %d\n", fun[2](x, y)); // output : 6 * 4 = 24
return 0;
}

END.

Author: Inno Fang
Link: http://innofang.github.io/2017/02/21/%E6%8C%87%E9%92%88,%E6%95%B0%E7%BB%84,%E5%87%BD%E6%95%B0%E9%82%A3%E4%BA%9B%E5%AE%B9%E6%98%93%E6%B7%B7%E6%B7%86%E7%9A%84%E6%A6%82%E5%BF%B5/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-ND 4.0 unless stating additionally.