写在前面:

与其说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了。相信通过前面的讲解,这里不用在下多说了。

后记:

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

理清C++常量指针和指针常量这团乱麻的更多相关文章

  1. C++基础之二:常量指针和指针常量

    1.常量指针 定义:具有只能够读取内存中数据,却不能够修改内存中数据的属性的指针,称为指向常量的指针,简称常量指针. 声明:const int * p; 注:可以将一个常量的地址赋值给一个对应类型的常 ...

  2. 【转】const int *p和int * const p的区别(常量指针与指向常量的指针)

    [转]作者:xwdreamer   出处:http://www.cnblogs.com/xwdreamer 对于指针和常量,有以下三种形式都是正确的: const char * myPtr = &am ...

  3. C++引用和const引用、常量指针、指针常量

    1.引用.常量引用 引用主要被用做函数的形式参数--通常将类对象传递给一个函数. 引用在内部存放的是一个对象的地址,它是该对象的别名.引用不占用内存,因为取地址引用的值和被引用变量的地址相同.但是ob ...

  4. C/C++中的常量指针与指针常量(转)

    常量指针 常量指针是指向常量的指针,指针指向的内存地址的内容是不可修改的. 常量指针定义“const int *p=&a;”告诉编译器,*p是常量,不能将*p作为左值进行操作.但这里的指针p还 ...

  5. C语言学习笔记 (001) - 常量指针与指针常量的区别(转帖)

    三个名词虽然非常绕嘴,不过说的非常准确.用中国话的语义分析就可以很方便地把三个概念区分开. 一) 常量指针. 常量是形容词,指针是名词,以指针为中心的一个偏正结构短语.这样看,常量指针本质是指针,常量 ...

  6. 数组/指针/const/字符串常量的使用传值问题

    #include<stdio.h> #include<string.h> int main() { ] = "abcd"; //常指针a指向字符串常量&qu ...

  7. c++中指针常量,常指针,指向常量的常指针区分

    const char * myPtr = &char_A;//指向常量的指针 char * const myPtr = &char_A;//常量的指针 const char * con ...

  8. C++ const 常量和常指针

    常量,该指针所指向的值为只读 ; const int * p = &a; 常指针,该指针的值为只读,不可再指向其他地址 const * const p = &a; 常值,常指针 con ...

  9. 常量指针-指向常量的指针,指针常量-指针本身是常量,常量-不能更改值的常量,数组指针-是指针int (*p)[n] 指针数组-是数组int *p[n]

    1.常量指针 定义:具有只能够读取内存中数据,却不能够修改内存中数据的属性的指针,称为指向常量的指针,简称常量指针. 声明:const int * p; int const * p; 注:可以将一个常 ...

随机推荐

  1. LeetCode Binary Tree Vertical Order Traversal

    原题链接在这里:https://leetcode.com/problems/binary-tree-vertical-order-traversal/ 题目: Given a binary tree, ...

  2. activity 四种启动模式

    前言 Activity的启动模式决定了Activity的启动运行方式 四种模式 Activity启动模式设置: <activity android:name=".MainActivit ...

  3. Centos 6.5安装bugzilla 5.0.2

    1. 下载bugzilla 下载地址:https://ftp.mozilla.org/pub/webtools/ 因为我在github上搜了一下“bugzilla cn”提示有中文语言包就下了5.0. ...

  4. 在Ubuntu上安装有道词典

    4月20日,由有道词典和Deepin团队共同完成的有道词典Linux版终于上线了,首先 推出Deepin和Ubuntu两个系统版本及其他版本的二进制包,估计以后还会有RPM 等版本.有道Linux版界 ...

  5. 刷新或关闭时调用onbeforeunload

    Onunload,onbeforeunload都是在刷新或关闭时调用,可以在<script>脚本中通过window.onunload来指定或者在<body>里指定.区别在于on ...

  6. PhpStorm提高效率的使用方法及设置

    快捷键: CTRL + D  复制当前行到下一行 或 复制选中内容到选中内容之后 CTRL + Y  删除当前行或选中内容所涉及的行 CTRL + R  替换 CTRL + F  查找 ALT + 上 ...

  7. make phpexcel working with XAMPP7.0.9

    Environment XAMPP 7.0.9 (PHP 7.0.9) PHPExcel 1.7.6-1.8.1 not lib_zip.dll Windows 10.1 thinkPHP 5.0.1 ...

  8. 基于bootstrap + php +ajax datatable 插件的使用

    Datatables是一款jquery表格插件.它是一个高度灵活的工具,可以将任何HTML表格添加高级的交互功能. 下面是我学习datatables写的一个服务器端(php)分页例子,该功能包含的功能 ...

  9. HDU 4292:Food(最大流)

    http://acm.hdu.edu.cn/showproblem.php?pid=4292 题意:和奶牛一题差不多,只不过每种食物可以有多种. 思路:因为食物多种,所以源点和汇点的容量要改下.还有D ...

  10. Dom编程

    Dom编程 Dom是一种用于HTML和XML文档的编程接口,是HTML页面的模型,将每个标签都做为一个对象,JavaScript通过调用DOM中的属性.方法就可以对网页中的文本框.层等元素进行编程控制 ...