C/C++指针和“右左右左”秘籍

指针是C/C++里非常重要的概念,它是内存地址的抽象。指针必须和实际的类型结合起来才有意义,因此,我们会看到指向各种类型的指针,包括数据类型的指针、数组的指针以及函数的指针。这有时让初学者感到困惑。

希望这篇文章可以或多或少地减少一些大家在指针学习过程中的困惑。

“右左右左”秘籍

口诀如下:
找名字,定类型,先往右,后往左,遇括号,换方向,找不到,即掉头。

普通指针

int *a;

一目了然,这里不需要用秘籍就能看出a是一个指向int类型的指针。我们也可以试着用一下秘籍。

  1. 找到名称 a
  2. 往右看,无
  3. 往左看,a是一个指针
  4. 往右看,无(可直接跳过)
  5. 往左看,指针a指向int类型地址

常量指针和指针常量

更容易看懂的描述:指向常量的指针和指针常量

const int x = 1;
const int y = 3;
const int *a = &x;
a = &y;
// *a = 1; error: read-only variable is not assignable

int u = 2;
int v = 4;
int* const b = &u;
*b = 2;
// b = &v; error: cannot assign to variable 'b' with const-qualified type 'int *const'

以上,a是指向常量的指针,b是指针常量。
大家可以看到每段代码中的最后一行注释,被注释的代码在编译时会报错,错误信息来自clang++ apple llvm 8.1.0,用其他编译器也会有类似报错,描述信息可能会不同。

第一个报错信息说明给指针a指向的内容赋值是不行的,因为指针a指向的是一个常量,这是很显然的,因为定义x和y的时候,定义的就是常量。

修改第一段代码,去掉常量x和y的const修饰,编译时依然会报错。

int x = 1;
int y = 3;
const int *a = &x;
a = &y;
// *a = 1; error: read-only variable is not assignable

因为指针a指向的类型是常量,不会受实际指向的变量或常量的类型影响。

第二个报错信息说明不能给常量b赋值。
到这里大家应该能注意到我在描述a和b的命名的区别了:指针a常量b
来吧,我们使用秘籍验证一下

const int *a = &x;
  1. 找到名称 a
  2. 往右看,无(忽略赋值)
  3. 往左看,a是一个指针
  4. 往左看,指针a指向const int类型地址(可分成两步,指针a指向一个int类型地址,这个地址中的数据是个常量)
int* const b = &u;
  1. 找到名称 b
  2. 往右看,无(忽略赋值)
  3. 往左看,b是一个常量
  4. 往左看,b这个常量存的是一个指针
  5. 往左看,b这个常量存的指针指向一个int类型地址

再来看下面这个定义

const int* const b1 = &u;

可以接上面秘籍第5步,再往左看,b1这个常量存的指针指向一个int类型地址,这个地址中的数据是一个常量
再变一下

int const* const b2 = &u;

头晕了没,这个新的定义和上面那个是一样的。但如果硬要从字面上看区别,可以体会下面两段描述的区别。
b1是一个常量,存了一个指针,指针指向了一个存储int类型的地址,这个地址里存的数据是一个常量
b2是一个常量,存了一个指针,指针指向了一个存储常量的地址,这个地址里存的是一个int类型数据。

数组指针和指针数组

更容易看懂的描述:指向数组的指针和存储指针的数组

int *c[10];
int (*d)[10];

来猜猜,c和d分别是什么?10秒后往下看秘籍

int *c[10];
  1. 找到名称 c
  2. 往右看,c是一个数组,这个数组有10个元素
  3. 往左看,c这个数组存的是指针
  4. 往左看,c这个数组存的指针指向int类型地址
int (*d)[10];
  1. 找到名称 d
  2. 往右看,遇到小括号返回
  3. 往左看,d是一个指针
  4. 往右看,d这个指针指向一个有10个元素的数组
  5. 往左看,d指向的这个10元素的数组存储int类型数据

运用两次秘籍以后,c和d是什么就很明显了。

函数指针和返回指针的函数

int *f(int i);
int (*fp)(int i);

经过之前的介绍,这里就直接上秘籍吧,看看f和fp分别是什么

int *f(int i);
  1. 找到名称 f
  2. 往右看,f是一个函数,函数有一个int类型的参数
  3. 往左看,f的返回类型是指针
  4. 往左看,f返回的指针指向int类型的地址
int (*fp)(int i);
  1. 找到名称 fp
  2. 往右看,遇到小括号返回
  3. 往左看,fp是一个指针
  4. 往右看,fp指向一个函数,函数有一个int类型的参数
  5. 往左看,fp指向的函数返回值是int类型

来两个复杂一点的例子

int (*f(int i))(bool b)

一眼看不出f是什么,祭出秘籍

  1. 找到名称 f
  2. 往右看,f是一个函数,函数有一个int类型的参数
  3. 往左看,f的返回类型是指针
  4. 往右看,f返回的指针指向一个函数,函数有一个bool类型的参数
  5. 往左看,f返回的函数指针指向的函数返回值是int类型
int (*(*fp)(int i))(bool b);
  1. 找到名称 fp
  2. 往右看,遇到小括号返回
  3. 往左看,fp是一个指针
  4. 往右看,fp指向一个函数,函数有一个int类型的参数
  5. 往左看,fp指向的函数返回值是一个指针
  6. 往右看,fp指向的函数返回的指针指向一个函数,这个函数有一个bool类型的参数
  7. 往左看,fp指向的函数返回的函数指针指向的函数返回值是int类型

看到这里是不是已经感觉开始绕口令了?
对于C/C++,我们可以使用typedef把这类复杂的定义表达得清晰一些

typedef int (*FP1)(bool b);
typedef FP1 (*FP2)(int i);
FP2 fp;

虽然敲的代码变多了,但定义变得更清晰了

最后

int (*(*(*(*fppp)(int i))(bool b))(char c))(long l);

看到这个是不是有点想骂人的感觉,这次我们不用秘籍,用typedef一层层地把这个定义分析清楚。但分析的步骤不是从最内层的名称开始,而是从最外层的返回值开始。

第一层

typedef int (*FP1)(long l);

把FP1代换回原定义

FP1 (*(*(*fppp)(int i))(bool b))(char c);

第二层

typedef FP1 (*FP2)(char c);

把FP2代换回原定义

FP2 (*(*fppp)(int i))(bool b);

第三层

typedef FP2 (*FP3)(bool b);

把FP3代换回原定义

FP3 (*fppp)(int i);

到这里,fppp是什么已经比较清楚了。它是一个函数指针,指向的函数有一个int类型参数,返回值是函数指针,这个函数指针指向的函数有一个bool类型的参数,返回值是函数指针,这个函数指针指向的函数有一个char类型的参数,返回一个函数指针,这个函数指针指向的函数有一个long类型的参数,返回值是int类型。

最后的最后

在实际代码中,前面描述的这种N层函数指针的定义很少被用到。但在支持CurryingPartial Application的语言里这种返回函数指针的函数是非常平常的。比如用Haskell的伪代码,上面那个复杂的定义就能写得很清晰。

int->bool->char->long->int
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容