一道指针面试题

int main()
{
 char *c[] = {"ENTER","NEW","POINT","FIRST"};
 char**cp[] = {c+3,c+2,c+1,c};
 char***cpp = cp;


 printf("%s\n", **++cpp);
 printf("%s\n", *--*++cpp+3);
 printf("%s\n", *cpp[-2]+3);
 printf("%s\n", cpp[-1][-1]+1);
 return 0;
}

这一道面试题,可以分为两个部分

第一部分为变量的声明

 char *c[] = {"ENTER","NEW","POINT","FIRST"};
 char**cp[] = {c+3,c+2,c+1,c};
 char***cpp = cp;

第二部分为一些输出

 printf("%s\n", **++cpp);
 printf("%s\n", *--*++cpp+3);
 printf("%s\n", *cpp[-2]+3);
 printf("%s\n", cpp[-1][-1]+1);

第二部分的重点,我觉得是后面的表达式,先不谈后方的表达式。

诺想理解函数后面的表达式,需先将上面的三个指针变量的声明与关系处理明白

指针的声明部分

 char *c[] = {"ENTER","NEW","POINT","FIRST"};

第一条语句是 字符串常量数组的声明

对声明部分进行分析,会稍微有些奇怪。

c是一个数组,数组中存储的元素类型是 char* ,那么理论上数组中存放的应该为字符指针

即字符的地址。而数组后的大括号中却是四个字符串。

其实c数组中存储的其实为 每个字符串的首元素地址

第二天条语句是 二级指针数组的声明

 char**cp[] = {c+3,c+2,c+1,c};

第二条语句的核心是 c变量,只要理解了c变量,整个语句便清晰了。

c变量是什么?

c是数组名,非特殊条件下为数组首元素的地址,即char* c[] 数组中首元素的地址。

首元素为字符串 “ENTER” 的首元素地址,也就是说c是首元素地址的地址,为二级指针。

反观声明,第二条语句声明的也是 二级指针数组 。

指针与正数的加减运算,会影响指向

因c变量是字符指针,进行运算的步长为1个字节。c+1指向的便是 c数组中第二个元素的地址

c+2 c+3 同理

 注:图中的连线紧代表cp中指针的指向

第三条语句是三级指针的声明

 char***cpp = cp;

cp char** cp[]的数组名,即数组的首元素地址

画出三个指针函数间的关系图

 输出部分

第一条输出语句

 printf("%s\n", **++cpp);

第一条输出语句的关键:**++cpp

该如理解这条语句呢?

先从变量名入手,cpp是一个指针,++cpp等于指针前移一个步长,即

 目前而言,指针cpp中存储的是c+2的地址(cp数组中第二个元素的地址)。

cpp进行解引用便得到cp数组中第二个元素

cp中存储的为一些c数组中元素的地址。

按照图中所示关系

*++cpp 等价于  &c[2]

那么**++cpp 等价于 *&c[2] 也就是 c[2]

c中存放都是一些地址,字符串首元素的地址。

c[2]所得到的其实就是"POINT"P的地址

总而言之 就是将 p 的地址 传给了printf函数

第二条输出语句

printf("%s\n", *--*++cpp+3);

这条语句的赋值程度远胜第一条语句,想要理解 *--*++cpp+3

需要先从操作符优先级下手

++(前置) > --(前置) > *  > +(加法)

从变量cpp入手,cpp是指针变量,前置++。先赋值再使用,即cpp前移一个步长

*++cpp的得到 c+1*--*++cpp+3 等价于 *--(c+1)+3

c+1也是一个指针变量, 前置--。先赋值再使用,即c+1后退一个步长

简单表达为 c.

总的来说,变是cp中第三个元素 c+1 便为 c

 故--*++cpp等价于 c

c是什么呢?c是数组char* c[]的数组名。也就是说是数组的首元素地址

故*--*++cpp的到的即是数组c的首元素。

就像上文分析的,数组c中存放的是常量字符串的首元素地址,

也就是说,现在拿到了字符串 ”ENTER"中E的地址。

即 *--*++cpp即为”ENTER"中E的地址。

E的地址是char* 类型的,+3前进三个字节。得到的便是第二个E的地址。

故整个表达式*--*++cpp+3所表示的即为”ENTER"中第二个E的地址。

 第三条输出语句

 printf("%s\n", *cpp[-2]+3);

同样是先从变量入手,*cpp[-2]

[] 的结合优先级高于 * ,而又像之前提到的 a[i] 等价于 *(a+i)

*cpp[-2] 等价于 **(cpp-2);

 由图可知,*(cpp-2)取到的起始是 c+3

那么 **(cpp-2) 等价于 *(c+3) 

*(c+3) 可以转化成 c[3] 

也就是说 *cpp[-2] 也就是 c数组中下标为3的元素

c中存放的均是字符串常量的首字符地址

也要注意,目前*c[-2]是一个字符的地址,也就是字符指针。

字符指针加减运算的步长是1,故*c[-2]+3最后指向的是 “FIRST” 中S的地址

 第四条语句

 printf("%s\n", cpp[-1][-1]+1);

还是从变量入手,与上一条语句同样的分析逻辑,cpp[-1] 等价于 *(cpp-1)

 故*(cpp-1)便取到了c+2

cpp[-1][-1]等价于(c+2)[-1]

同理 (c+2)[-1]等价于*(c+1)

 即*(c+1)便取到了数组c中下标为1的元素

"NEW"中N的地址

综上所述

cpp[-1][-1]便是 N的地址

cpp[-1][-1]+1 也就是 E的地址

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值