前言:
结合 | 含义 |
---|---|
普通函数 | 返回类型为基本类型(int、float、char等)、构造类型(struct、enum等)、空类型(void)的函数 |
指针函数 | 返回类型为指针类型((int *)、(float *)、(char *)、(struct XX *)、(void *)等)的函数 |
函数指针 | 指向函数的指针,格式为:type (*func)(参数列表) |
普通数组 | 由基本类型(int、float、char等)、构造类型(struct、enum等)、空类型(void)构成的数组 |
指针数组 | 由指针类型((int *)、(float *)、(char *)、(struct XX *)等)构成的数组 |
数组指针 | 指向数组的指针,类型格式为:type (*array)[ ] |
函数指针数组 | 有函数指针构成的数组,类型格式为:type (*func[ ])(参数列表) |
指向函数指针数组的指针 | 类型格式为:type (* (*func)[ ])(参数列表) |
参考链接:
1.详解C语言指针函数、函数指针、函数指针数组:
https://blog.csdn.net/u010183728/article/details/80967283
2.深入理解const char *p,char const *p,char *const p,const char **p,char const **p,char *const *p,char **const p:
https://www.cnblogs.com/rushuizhijing/archive/2011/08/26/2154737.html
1. 指针数组、数组指针:
使用一个例子,讲解 普通数组、指针数组、数组指针。
#include <stdio.h>
int main(int argc, char **argv)
{
int i = 0;
int iArray[4] = {1, 2, 3, 4}; // 普通数组
int *ipArray[4]; // 指针数组
int (*pArray)[4]; // 数组指针,该指针指向4个int大小的数组
// 给 数组指针、指针数组 赋值
pArray = &iArray;
for(i = 0; i < 4; i++)
{
ipArray[i] = &iArray[i];
}
for(i = 0; i < 4; i++)
{
printf("普通数组 iArray[%d] = %d \n", i, iArray[i]);
}
for(i = 0; i < 4; i++)
{
printf("指针数组 *ipArray[%d] = %d \n", i, *ipArray[i]);
}
for(i = 0; i < 4; i++)
{
printf("数组指针 (*pArray)[%d] = %d \n", i, (*pArray)[i]);
}
return 0;
}
2. 函数指针、函数指针数组、指向函数指针数组的指针:
使用一个例子,讲解 函数指针、函数指针数组、指向函数指针数组的指针。
笔者注:下面的预处理指令(#if 1 #else #endif)的开关(#if)可以同时为1或同时为0。程序中的fun_c数组和pfunarr数组本质上是一样的。
#include <stdio.h>
void fun1(int x, int y)
{
printf("调用函数fun1, x = %d \n", x);
}
void fun2(int x, int y)
{
printf("调用函数fun2, y = %d \n", y);
}
void fun3(int x, int y)
{
printf("调用函数fun3, x + y = %d \n", (x + y));
}
// 定义fun类型为void (*fun)(int, int)类型
typedef void (*fun)(int x, int y);
int main(int argc, char **argv)
{
// 使用typedef定义的类型去定义1个函数指针数组,用于存放3个函数指针
fun fun_c[3];
#if 1
fun_c[0] = fun1;
fun_c[1] = fun2;
fun_c[2] = fun3;
#else
fun_c[0] = &fun1;
fun_c[1] = &fun2;
fun_c[2] = &fun3;
#endif
// 定义3个函数指针
void (*pfun1)(int, int) = &fun1;
void (*pfun2)(int, int) = &fun2;
void (*pfun3)(int, int) = &fun3;
// 定义1个函数指针数组,用于存放3个函数指针
void (*pfunarr[3])(int, int);
// 定义1个指向函数指针数组的指针,用于指向pfunarr
void (*(*ppfunarr)[3])(int, int);
// 给pfunarr赋值的两种方式
#if 1
pfunarr[0] = pfun1;
pfunarr[1] = pfun2;
pfunarr[2] = pfun3;
#else
pfunarr[0] = &fun1;
pfunarr[1] = &fun2;
pfunarr[2] = &fun3;
#endif
// 运行使用typedef定义的类型数组
printf("Running 使用typedef定义的类型数组 \n");
#if 1
fun_c[0](1, 2);
fun_c[1](1, 2);
fun_c[2](1, 2);
#else
(*fun_c[0])(1, 2);
(*fun_c[1])(1, 2);
(*fun_c[2])(1, 2);
#endif
// 运行pfunarr指向的函数
printf("Running pfunarr指向的函数 \n");
#if 1
pfunarr[0](1, 2);
pfunarr[1](1, 2);
pfunarr[2](1, 2);
#else
(*pfunarr[0])(1, 2);
(*pfunarr[1])(1, 2);
(*pfunarr[2])(1, 2);
#endif
// 运行将ppfunarr指向pfunarr,并运行ppfunarr指向的函数
printf("Running 将ppfunarr指向pfunarr,并运行ppfunarr指向的函数 \n");
ppfunarr = &pfunarr;
#if 1
(*ppfunarr)[0](1, 2);
(*ppfunarr)[1](1, 2);
(*ppfunarr)[2](1, 2);
#else
(*(*ppfunarr)[0])(1, 2);
(*(*ppfunarr)[1])(1, 2);
(*(*ppfunarr)[2])(1, 2);
#endif
char c = 0;
printf("请按任意键继续 ... \n");
scanf("%c", &c);
return 0;
}
3. “const”、“char”、“*”、“p”的组合:
3.1 初步理解
int a = 0;
int const *p = &a;
*p = 100; // 不合法,会有报错 error: assignment of read-only location ‘*p’
p = NULL; // 合法
int *const p = &a;
*p = 100; // 合法
p = NULL; // 不合法,会有报错 error: assignment of read-only variable ‘p’
const int *const p = &a;
*p = 100; // 不合法,会有报错 error: assignment of read-only location ‘*p’
p = NULL; // 不合法,会有报错 error: assignment of read-only variable ‘p’
3.2 解析:
可以配合下图帮助理解
序号 | 结合类型 | 含义 |
---|---|---|
1 | char *p | 对“p”没有限制(对“任意p”“读写”操作) |
2 | const char *p | *p是常量,*p是常量 可对“任意p”“只读”操作 |
3 | char const *p | *p是常量 同上 |
4 | char *const p | p是常量,p是常量 可对“指定p”“读写”操作 |
5 | const char **p | **p是常量,这里涉及到“p”、“*p”指针 可对“任意 *p”“只读”操作 对“p”没有限制(对“任意p”“读写”操作) |
6 | char const **p | **p是常量,这里涉及到“p”、“*p”指针 同上 |
7 | char *const *p | *p是常量,这里涉及到“p”、“*p”指针 可对“任意p”“只读”操作 对“指定 *p”“读写”操作 |
8 | char **const p | p是常量,这里涉及到“p”、“*p”指针 可对“指定p”“读写操作” 对“*p”没有限制(对“任意 *p”“读写”操作) |
笔者注:
1.上面表格的[“任意p” “指定p” “任意*p” “指定 *p”]是将其[“p” “*p”]当做地址;
2.上面表格的[“读写”、“只读”]是指其地址[“p” “*p”]空间的值[“*p” “**p”]为变量、常量;
3.“const char”和“char const”是一样的,但必须“char”和“const”相依着;
4.主要看const后面跟着的是“p”/“*p”/“**p”,则将后面这部分当做常量。
3.2 关于“count ”、 “char”、2个“*”相结合的类型相容性问题
3.2.2 “count ”、 “char”、2个“*”相结合的问题
// 1 合法
// p1是一个指向(char *)类型的指针
// p2是一个指向(char *)类型的指针
char **p1;
char **p2 = p1;
// 2 不合法,会有警告 initialization from incompatible pointer type
// p1是一个指向(char *)类型的指针,定义改为“const char **p1;”为合法
// p2是一个指向(const char*)类型的指针
char **p1;
const char **p2 = p1;
// 3 不合法,会有警告 initialization from incompatible pointer type
// p1是一个指向(char *)类型的指针,定义改为“char const **p1;”为合法
// p2是一个指向(char const*)类型的指针
char **p1;
char const **p2 = p1;
// 4 合法
// p1是一个指向(char *)类型的指针
// p2是一个指向[“带有const限定”的](char*)类型的指针
char **p1;
char *const *p2 = p1;
// 5 合法
// p1是一个指向(char *)类型的指针
// p2是一个指向[“带有const限定”的](char*)类型的指针
char **p1;
char **const p2 = p1;
// 6 不合法,会有警告 initialization from incompatible pointer type
// p1是一个指向(char *)类型的指针,定义改为“const char **p1;”为合法
// p2是一个指向[“带有const限定”的](const char*)类型的指针
char **p1;
const char *const *p2 = p1;
// 7 不合法,会有警告 initialization from incompatible pointer type
// p1是一个指向(char *)类型的指针,定义改为“const char **p1;”为合法
// p2是一个指向[“带有const限定”的](char const*)类型的指针
char **p1;
char const *const *p2 = p1;
3.2.3 判断规则
明确const修饰的对象,对于指针p1、p2,若要使得p2 = p1成立,则可理解为:
p1是指向X类型的指针;p2是指向“带有const限定”的X类型的指针。只要二者的X类型一样,就是合法的。
判断不出X类型时,使用gcc编译后,查看编译信息中有无警告“initialization from incompatible pointer type”,无警告为合法,有警告为不合法。