再论指针数组与数组指针

一、二者的区别

(一)直观的差异

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 加的是整个数组空间大小,数组首地址主要用于构建多维数组,对于一维数组来说,数组首地址没有太大的实用意义。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值