一、二者的区别
(一)直观的差异
int *p[5] //指针数组,元素为指针
int (*p)[5] //数组指针,指向数组的指针
(二)非直观差异
1)数组指针:待补充,比较好找
2)指针数组:const char* key_status_name[]就是一个以char型指针为元素的数组;
#define ENUM_ITEM(ITEM) ITEM, //使用define获取枚举项,注:逗号是必须的
#define ENUM_STRING(ITEM) #ITEM, //使用define获取枚举项对应的字符串
#define KEY_STATUS_ENUM(STATUS) \
STATUS(KS_RELEASE) /*稳定松开状态*/ \
STATUS(KS_PRESS_SHAKE) /*按下抖动状态*/ \
STATUS(KS_PRESS) /*稳定按下状态*/ \
STATUS(KS_RELEASE_SHAKE) /*松开抖动状态*/ \
STATUS(KS_NUM) /*状态总数(无效状态)*/ \
typedefenum
{
KEY_STATUS_ENUM(ENUM_ITEM)
}KEY_STATUS;
constchar* key_status_name[] = {
KEY_STATUS_ENUM(ENUM_STRING)
};
二、使用数组指针传递地址,作为函数的输入或输出参数
输入型参数:形参里面,凡是只使用值的,我们都称之为输入型参数,目的只是为了传递函数需要用到的数值。如果这些参数里面涉及指针的时候(主要是数组和结构体),必须加入 const 修饰,防止被修改。
输出型参数:如果是希望通过形参返回某个值的时候,我们把这种参数称为输出型参数,而且
这一类的参数,传递的必须是地址(指针),并且不能加 const 修饰。
传递地址遵守两个原则,一是修改原则,二是效率原则。修改原则就是利用传参来实现函数返回值。除了数组和结构体外,一般情况下,如果是为了得到返回值,我们就传递地址,否则就是传递普通值。
但是对于数组和结构体来说,为了提高传参的效率,基本传递的都是地址。数组不用说,不管什么情况只能传地址,对于结构体来说尽量要求传地址。但问题是当传递数、组结构体地址时,有的时候只是使用其内容而不是要修改,但是我们传递了地址后,就一定存在被修改的可能。那么当为了提高效率必须传递地址,但又不能修改它的内容的时候,我们应该怎么做呢?这个时候就可以加入 const 来锁定。如:
void fun(char *dest_str, const char *src_str) {
strcpy(dest_str, src_str);
}
int main(void) {
char dest_str[40] = {0};
char src_str[] = {"hello woeld"};
fun(dest_str, src_str);
return 0;
}
fun 函数传递了两个数组。fun 函数的第一个形参是为了被修改,返回被复制后的字符串,第二个参数是被复制字符串的源,这个源数组是不能被修改的,但是因为传递的是地址,存在被修改的风险,所以加了 const 修饰,表明 src_str 指向的是“常量”,不能被修改。const 在形参里面被使用时,基本都是用在数组和结构体的指针形参上。
三、使用指针数组
使用指针数组,类似指针结构体,元素为函数指针,进一步将函数指针封装为数组形式
(一)以简单的计算器例程为例,使用函数指针作为数组的元素,来抽象化代码
int Add(int x, int y)
{
return x + y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mul(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
void menu()
{
printf("**********************************\n");
printf("***** 1. add 2. sub *****\n");
printf("***** 3. mul 4. div *****\n");
printf("***** 0. exit *****\n");
printf("**********************************\n");
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
int (*pfArr[5])(int, int) = { 0, Add, Sub, Mul, Div };//pfArr是一个函数指针的数组,也叫转移表
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
if (input == 0)
{
printf("退出计算器\n");
break;
}
else if (input >= 1 && input <= 4)
{
printf("输入2个操作数:>");
scanf("%d %d", &x, &y);
ret = pfArr[input](x, y);
printf("ret = %d\n", ret);
}
else
{
printf("选择错误\n");
}
} while (input);
return 0;
}
四、补充:数组名和数组首元素的使用
以int buf[100]={0}为例,讨论下buf、buf[0]、&buf[0]和&buf这四个符号的内涵
1)buf :有两层含义,一是数组名,sizeof(buf)时,buf 就是数组名的含义;二是等价于
&buf[0],表示数组第一个元素的首字节地址,是一个常量值。
2)buf[0] :第一个元素的空间,可以对其进行读写操作,所以就可以作为左值被写,也可以
作为右值被读。
3)&buf[0] :等价于 buf,是一个地址常量,只能作为右值。
4)&buf :表示数组首地址,是一个地址常量,同样只能作为右值。
buf 与 &buf 的值相等,但是含义完全不同。
printf("%p\n", buf) 与 printf("%p\n", &buf) 这两句话的打印结果是相同的,表明它们的值相等,但是 printf("%p\n", buf + 1) 与 printf("%p\n",&buf + 1) 的打印结果完全不同。
因为它们的含义完全不同,buf 表示数组第一个元素的首字节地址,加 1 加的是一个元素空间的大小;&buf 表示的数组首地址,加 1 加的是整个数组空间大小,数组首地址主要用于构建多维数组,对于一维数组来说,数组首地址没有太大的实用意义。