指针、数组、函数的组合,“const”、“char”、“*”、“p”的组合

前言:

结合含义
普通函数返回类型为基本类型(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 解析:

可以配合下图帮助理解
指针符号与寻址符号

序号结合类型含义
1char *p对“p”没有限制(对“任意p”“读写”操作)
2const char *p*p是常量,*p是常量 可对“任意p”“只读”操作
3char const *p*p是常量 同上
4char *const pp是常量,p是常量 可对“指定p”“读写”操作
5const char **p**p是常量,这里涉及到“p”、“*p”指针 可对“任意 *p”“只读”操作 对“p”没有限制(对“任意p”“读写”操作)
6char const **p**p是常量,这里涉及到“p”、“*p”指针 同上
7char *const *p*p是常量,这里涉及到“p”、“*p”指针 可对“任意p”“只读”操作 对“指定 *p”“读写”操作
8char **const pp是常量,这里涉及到“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”,无警告为合法,有警告为不合法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值