写在前面:

与其说C++中的常量指针和指针常量是一块很有嚼头的语法糖,不如说它是一块相当难啃的骨头。其实本来没什么,这无非是const int *p与int* const p的区别, 但一涉及到起名字,特别是给他们戴上“常量指针”和“指针常量”的中文帽子,由于作者和译者(针对外文书)的不同,就出现了“张冠李戴”和“李冠张戴”的乱像,不知道谁是谁了,弄得人一头雾水,尤其是对于初学者。本文的目的就是针对这一细节,为大家将两者理清楚,同时说明在使用上的区别。

注意:1.const int *p也可写成int const *p,即C++中const int和int const无区别,这使得本来就很乱的局面更加麻烦,本文中我只使用const int,以后不再说明。

2.如果您讨厌啰嗦,只想学“干货”,您可以直接跳到“安能辨我是雄雌——判断方法”一节(在下技术有限,就不设置页面内跳转了)。

在理清楚之前,让 我们先简单看看当前“乱象丛生”的现状吧。

一、const遇上指针——一团乱麻

同样的问题,相反的解释

1.标新立异的少数派

C++ Primer第五版的提法可谓与其它C++书籍背道而驰,它的提

常量指针——int* const p

指向常量的指针——const int *p

在英文版中,int* const p被称为const pointer,于是中文版将其译为常量指针;而const int *p被称为pointer to const,但对应的中文版并未将其译为指针常量,而是译为指向常量的指针。

无独有偶,我们国内的书籍《零起点学通C++》(范磊 著 清华大学出版社 2010年版)的提法和C++ Primer完全一致,将int* const p称为常量指针,而将const int *p称为指向常量的指针。

2.一边倒的大众说辞

更广为流传的提法是

常量指针——const int *p

指针常量——int* const p

明确写有“常量指针”和“指针常量”的书籍在下就见过一本——《C++面向对象程序设计》(郭有强 著 清华大学出版社 2009年版)(从出版年上我们可以大胆推测,相对较老的书会这样提)。更多的书籍的做法是——将int* const p称为指针常量,将const int *p称为指向常量的指针。(可见问题的关键在于int* const p,有的书中将其称为常量指针,如1,有的书中将其称为指针常量,如2)

这里的“一边倒”更主要的是针对网络而言。如果您百度“常量指针和指针常量”,将会看到几乎所有的材料中都采用了2中的提法。如果一个初学者在解决“常量指针和指针常量”的困惑时首先选择了网络途径,那么2的提法应该在他的脑海里根深蒂固了吧。

如果您已经接受了2中的提法,建议您将“常量指针”扩展为“指向常量的指针”,将“指针常量”扩展为“该指针是常量”来帮助您记忆和区别两者的本质。

作者们的拯救

大概一些IT教育者和从业者们也注意到了这一问题,近年来的一些书籍作者们开始用自己的方式来纠正,让我们来看看他们是怎么做的。

1.这样的改变好吗?

《C++程序设计基础》(管建和 著 清华大学出版社 2013年版)中将const int *p称为“常量化指针目标表达式”,而将int* const p称为“常量化指针变量”,试问,这么长的名字,能不能记住?记住了,能不能搞清楚?

2.两个弄不清,干脆叫一个

比较著名的外文书籍《C++高级编程》(这里就中文第二版讲)中在“const指针”的标题下同时介绍了const int *p和int* const p,而没有给他们具体起名字。

在国内,《新标准C++程序设计教程》(郭炜 著 清华大学出版社 2012年版)将const int *p称为常量指针,而在介绍int* const p时写道“还有一种常量指针”。

但这样做仍然不能称之为行之有效,因为这样并没有通过名字将两者从本质上区分,同时“不同的事物,同一个名字”也不便于交流。

3.一种值得推荐的命名法

《C++程序设计教程》(方超昆 著 北京邮电大学出版社 2009年版)和《C++从入门到精通》(人民邮电出版社 2015年版)是这样提的

指向常量的指针变量——const int* p

指向变量的指针常量——int* const p

指向常量的指针常量——const int* const p   //后面会详细说到

这样,谁常谁变就一目了然了,而且名字反应了本质。从第一本书的出版年可以看出,其实这样比较清晰的命名方式好几年前就有了,但可惜的是,直到现在,在众多的C++书籍中,这般清晰的命名方式也并不多见。

在下的用意

一定会有人说:“你说了这么多,并没有给我们带来什么实质性的知识。”在下不反对,其实在下在这一部分的开头已经清醒过大家,可以略过。在下写这部分的用意有四:

1.向读者介绍一下现状和常见的提法。

2.告诉读者该问题上存在的不一致现象,当读者阅读不同的书籍时遇到不一致,不要过于困惑。

3.劝读者不要记忆网上的“常量指针”和“指针常量”,因为这样的命名并不好,许多书籍上也并没有同时提到这两者。

4.表达一下自己希望C++的相关命名实现术语化、标准化、大一统的愿望,尽管在下知道这一点很难实现。

在下怎么看

1.不要使用“常量指针”和“指针常量”,特别是后者,因为它连“表述对象是指针”这层意思都没有清晰地表达出来。

2.建议采用“一种值得推荐的命名法”下的名称来记忆。

3.或者采用在下的建议。现在说明在下的提法

指向常量的指针——const int *p

本身是常量的指针——int* const p

所指和本身都是常量的指针——const int* const p

这里采用“定语+主体”的结构,主体“指针”很好地表现了所描述对象是指针这一本质,而定语部分则描述了特性,同时也将“自己”和“他人”区别开,整体通俗易懂。(在下一直认为,有时一个高大上的名称就是故作高深,而通俗易懂本身就是一种真正的高大上。面向初学者,把话说明白,一直是在下文章的宗旨与追求。)唯一的不足可能是名称有点长,但在下相信,只要足够通俗易懂,长不是障碍。

从现在开始,我们在后面的讲述中将一直采用这种提法。

二、安能辨我是雄雌——判断方法

相信有些初学者会有这样的困惑:单提名字知道怎么回事,但一看到代码就不知道叫什么名字了,也就无法想起代码的语法特性。这里,在下给大家介绍一种简单易用的判断方法,一看代码就能分析出语法特性。

相信有些读者看到过所谓的“从右往左读”的办法,在下接下来要说的方法和“从右往左读”本质上一样,只不过无论是认知还是操作上都更加简单(起码在下认为是这样,这里就不谦虚了)。方法表述如下:

先找到*,然后看*的两边,右边是对指针p本身的限定,左边是对p所指向的东西的限定。

注:本文中我们将p的所指笼统地称为“东西”。

例如

1.const int *p

*的右边没有限定成分,表明p就是我们熟悉的普通指针,p的内容(即值,也就是它指向的东西的地址)是可以改变的;*的左边是const int,表明p指向的东西是一个const的int,我们不能通过p来修改这个int,因为它是const的。(关于“不能通过p来修改”,我们后边还会详细解释)

2.int* const p

*的右边是const,表明p本身是const的,我们不能对p的内容进行修改(例如++p;是不可以的),*的左边是int,即p指向的东西是普通的int,我们可以通过p来修改它(例如*p=100;是可以的)。

3.const int* const p

*的右边是const,表明指针p本身是const的,*的左边是const int,表明p指向的int也是const的。即这种情况下,p本身不能修改,同时也不能通过p修改它所指向的那个int。

三、天生我材必有用——语法特性

其实在讲判断方法的时候,已经涉及到了各自的语法特性。这里再针对一些细节深入说一下。

1.const int *p

就是所谓的“指向常量的指针”。这里注意,所谓“指向常量”只是这个指针的“一厢情愿”,只是一种效果上的等价。事实上,const int *p=&a;a既可以是常量(const int a=10;)又可以是变量(int a=10;),但p一厢情愿地认为它所指的就是一个常量,所以它不允许通过自己来修改所指,这就造成一种效果上的等价——从p的角度看,它所指的“的确”是常量。所以,对“指向常量的指针”的最佳理解应为:我们不能通过该指针修改它所指向的东西(常量或者变量)。

注意,const int *p=&a;只是说不能通过p来修改a,如果a本身不是const的,通过其它方式修改a自然是可以的(例如直接++a)。

另外一点,由于p本身只是一个普通的指针,所以允许在声明时不初始化。但需要注意的是,我们只是说可以,但并不提倡这样做。在任何时候都不应该让指针无所指,如果在声明一个指针时还不知道让它指向谁,就先将其初始化为nullptr或NULL(nullptr是C++11新特性,用它比用NULL更安全些,这里不详细介绍)。

2.int* const p

就是所谓的“本身是常量的指针”。关于“p本身不能修改但可以通过p修改其所指”这一点,我们在讲判断方法时已经说过,这里主要再说一下p的初始化。

由于p本身是const的,所以在编译的时候必须知道p的值(即p所指向的东西的地址),所以在声明p的同时必须初始化p。但要注意,对于 int* const p=&a,我们只要求a的地址是确定的,但a的值可以不确定。比如下面的代码是可行的

#include<iostream>using namespace std;int GetData(int num) { return num; } int main() { int a; cin >> a; int b = GetData(a); int* const p = &b; cout << *p << endl; return 0; }

由于声明了int b,所以b的地址在编译时是确定的,但很显然,b的值只要在程序运行时才能确定。

另外注意,用nullptr或NULL初始化int* const p没有问题,因为nullptr和NULL都代表有效地址。

3.const int* const p

就是所谓的“所指和本身都是常量的指针”。它的语法特性就是前两者的结合,这里不再赘述。

四、番外篇——说说引用和const引用

文章至此本该结束了,但就C++而言,谈到了指针,似乎自然应该再谈谈引用。于是,在下接着再来简单说说引用和const引用。所谓“简单说说”,是指在下只是做一个简明扼要的介绍,对一些原理和细节不做深入探讨。为什么要在这里说?因为我们的讲述要会用到上面的概念。另外还要注意,我们这里只讨论左值引用,即大家所熟悉的一般引用,对于C++11新特性右值引用,不在讨论之列。

相信大家都知道引用的这些特性

1.如果一个引用和一个东西(这里再次使用“东西”这个词)绑定,那么它就永远只能是这个东西的“别名”,不能再说“其他人”的“别名”,即引用本身不能修改。但是,我们可以通过引用来修改它所引用的东西的值。

2.引用声明时必须同时初始化 ,且必须用左值初始化。(左值:就是可以用&求地址的量,换言之,就是有确定地址的量,而不是所谓的临时量)

对这些特性大家是不是似曾相识呢?没错,这些特性和“本身是常量的指针”(int* const p)的特性一样。事实上,我们完全可以借用“本身是常量的指针”来理解甚至定义引用:

引用是一个指向不可变的、被编译器自动解引用的指针,即,引用是一个被编译器自动解引用的“本身是常量的指针”。

看下面的代码

int a = 10;
int &ra = a;
ra = 11;

在上面的代码中,编译器将int &ra=a转化为int* const ra=&a,而将ra=11转化为*ra=11,将ra自动转化为*ra的过程,就是上面定义中所说的“自动解引用”。

那么,什么是const引用(即我们说的常量引用,但我希望大家称其为const引用而不是常量引用)呢?很显然,const int &ra=a就相当于const int* const ra=&a了。相信通过前面的讲解,这里不用在下多说了。

后记:

在下能力有限,尽管尽最大努力做到严谨,但错误疏漏之处仍在所难免,恳请大家批评指正。您的帮助,就是在下前进的不竭动力。

转载于:https://www.cnblogs.com/zpcdbky/p/4902688.html

理清C++常量指针和指针常量这团乱麻相关推荐

  1. C++ 笔记(27)— 指针变量、数组和指针、指针数组、数组指针、指针常量与常量指针

    1. 什么是指针变量? 指针变量是专门有一个变量来存放指针. int main(int argc, char *argv[]) {int a = 10;int *p = &a; //通过取地址 ...

  2. 常引用、常量指针、指针常量、指向常量的常指针、空指针与野指针解释

    1.一.基础知识 引用并非对象 引用必须初始化 引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起 类型要严格匹配 int &a = 10; //错误:引用类型的初始值必须是 ...

  3. 常量指针与指向常量的指针

    这两个概念经常很容易混淆,下面简单分析一下 1.常量指针: int * const p   : const p 就是一个常量,然后再看*,可以看出是一个常量类型的指针,即int类型的常量指针.不能修改 ...

  4. [C++] 指向常量的指针 VS 指针类型的常量

    指向常量的指针 VS 指针类型的常量 const 修饰指针时的位置不同,作用也不相同. 1. 指向常量的指针 不能通过指向常量的指针改变所指对象的值,但指针本身可以改变,可以指向另外的对象. 例: i ...

  5. [C++基础]018_常量指针和指向常量的指针

    先来看一下什么是常量指针,什么是指向常量的指针吧! 1. 常量指针定义 1 int * const ptr = new int(); 2. 指向常量的指针 1 const int* ptr; 上面已经 ...

  6. 【错误记录】C 语言中通过指针操作字符串常量出错记录 ( 只有 栈内存 或 堆内存 中的数据才能通过指针修改 | 不要通过指针修改常量区的字符串 )

    文章目录 一.报错记录 二.修改方案 一.报错记录 执行下面的代码 , 报错如下 : 执行的错误代码 : #include <stdio.h> #include <stdlib.h& ...

  7. 【C 语言】字符串 一级指针 内存模型 ( 指定大小字符数组 | 未指定大小字符数组 | 指向常量字符串的指针 | 指向堆内存的指针 )

    文章目录 一.字符串 一级指针 内存模型 1.指定大小字符数组 2.未指定大小字符数组 3.指向常量字符串的指针 4.指向堆内存的指针 一.字符串 一级指针 内存模型 #include <std ...

  8. C++ 指向常量的指针与指针类型的常量

    (1)声明指向常量的指针时,不能通过指针来改变所指对象的值,但指针本身可以改变,可以指向另外的对象. #include<iostream>using namespace std;int m ...

  9. 常指针、指向常量的指针、指向常量的常指针

    三者的区分 简而言之- 常指针:地址不可变,内容可变 指向常量的指针:地址可变,内容不可变 指向常量的常指针:地址不可变,内容不可变 样例 代码如下: #include <iostream> ...

最新文章

  1. python 按键获取_Python中按键来获取指定的值
  2. 微信小程序实现图书管理系统
  3. 为什么要做scan extraction?
  4. Python3 函数(方法)
  5. 学习使用Whally GraalVM!
  6. 开始学习python标准库---os
  7. Qt中SQL语句update同时更新多字段及设置字段值为空的方法
  8. 任意阶幻方解法及c++实现
  9. JS 运算、判断优化
  10. 【详细】MySQL之游标
  11. 浏览器userAgent大全
  12. cocos2dx基础篇(20)——基本动画CCAnimation/CCAnimate
  13. Inverting Visual Representations with Convolutional Networks
  14. 给爱车安装一台GPS,贴身“保镖”保护爱车
  15. 和字节跳动大佬的技术面谈,醍醐灌顶!
  16. c语言做绘图软件,大佬们,小菜鸟想问一问用vc编译器做简易画图软件
  17. 华为设备常用软件包名
  18. 计算机视觉期末复习(上)
  19. python数据分析与应用第五章实训 2_第五章实训(二)
  20. python支持接口编程的好处_面向对象、接口编程的重要性 python 为什么引入接口interface...

热门文章

  1. ARM裸机开发篇3:ARM汇编语言程序设计
  2. Oracle12C--触发器(52)
  3. 【Java基础】包、类、接口、常量、方法、变量的命名规则
  4. CST shape tools
  5. USACO4.1 篱笆回路Fence Loops
  6. 基于英飞凌TLE8242-2L的开关/比例电磁阀驱动电路
  7. (免费分享)基于springboot论坛bbs系统
  8. 蒙特卡罗MCNP学习汇总(五)---高级几何建模
  9. 对于电脑命令你了解多少?电脑命令大全
  10. php实现html转word