写程序的过程中遇到了const的用法,上网搜了点资料,都说Dan Saks的这篇文章讲的比较全面,就打算好好看看,可惜是英文的,因此决定自己来翻译一遍,为了更好地理解const的同时也能复习一下英语。原文可以参见:http://blog.csdn.net/bianbian17556231/archive/2010/03/20/5398276.aspx

译文:

在我的上一篇文章中,我讨论了为什么C++中编译器把数据存入ROM的规则比C中的更复杂。关于这个主题,我还想再谈谈,不过在此之前,我想先回答下面这个问题,这个问题是来自Komatsu Mining Systems的Phil Baurer发来的邮件:

“我们现在在使用typedef const的时候遇到了一个有趣的问题,我希望你能评论一下这种情况,我想知道我们是否碰到了C语言的某种不明规则。我们使用的是Hitachi SH-2 32-bit RISC 微控制器的Hitachi C 编译器。我们认为下面的代码:”

typedef void *VP;
const VP vectorTable[] = {..<data>..};                               (1)

应该和下面的相同:

const void *vectorTable[] = {..<data>..};                          (2)

“甚至,连接器把(1)中的vectorTable放在CONSTANT域,而把(2)中的vectorTable放在DATA域。这是正确的还是编译器中的错误?”

这种做法是正确的,并不是错误,你的确碰到了一些你不知道的C语言的规则。不要灰心,你不是唯一一个,我相信很多其他C和C++程序员也对此感到困惑,这也是为什么我要在我的文章中回答这个问题。

我在早期的文章中提过其中的一些规则,但是回顾起来,我觉得,对那些造成你困惑的知识点我强调得并不够,因此我再强调一下。

尽管C和C++基本上是按从头到尾、从左到右的顺序来读,但是指针的声明,从某种意义来讲却是倒着的

声明

这是第一个观点:

C和C++中的每个声明都有两个主要部分:零个或者更多声明说明符,和一个以上用逗号隔开的声明符

例如:

static unsigned long int *x[N];

static unsigned long int :声明说明符
*x[N]                              :声明符

一个声明符就是被声明的名称,可能伴有操作符,比如 *, [], (), 和(C++中的) &. 正如你所知的,声明中的符号*表示“指针”,[]表示 “序列”。因此, *x[N]表明x 是一个“有N个指针元素的序列,分别指向某某”,某某就是声明中指定的类型。例如,

static unsigned long int *x[N];

把x声明为“指向unsigned long int的N个指针元素的序列”的一个对象。(后面会解释,关键词static对这种类型没有意义。)为什么我知道*x[N]是一个指针的序列,而不是指向一个序列的指针? 它遵循以下规则:

在一个表达式中,声明符中的操作符根据他们的优先级来分组

例如,在C或C++中,如果检查最近优先级图表,你会发现[]的优先级比*更高。因此声明符*x[N]表明x是一个优先于指针的序列。圆括号在声明符中有两个作用:第一,作为函数调用的操作符,第二,用来分组。作为函数调用的操作符,()和[]的优先级相同。用作分组时,()的优先级是最高的。

大多数人把存储类说明符(如static)作为第一个(最左边的)声明说明符,但是它只是一个惯例,并非是语言要求。

例如,*f(int)表示f是一个函数,返回一个指针。相反,(*f)(int)表示f是一个指向函数的指针。

一个声明符可能包含不止一个标识符。声明符*x[N] 包含两个标识符,x和N。只有其中一个标识符是被声明的,而且被称为是声明符ID,其余的必须在这之前就被声明过。举例,*x[N] 中的声明符ID是x。

一个声明符可以不包含任何操作符。可以很简单,如:

int n;

这个声明符只有标识符n,没有任何操作符。

声明说明符

一个声明符可以是类型说明符,如int,unsigned,或者类型名称的标识符。他们也可以是存储类说明符,如extern或static。在C++中,他们也可以是函数说明符,如inline或virtual。

这里有另一个观点:

类型说明符表明声明符ID的类型;其他说明符提供直接适用于这个声明符ID的一些类型无关的信息。

举例:

static unsigned long int *x[N];

声明x的类型是“指向unsigned long int的N个指针元素的序列”。关键词static表明x有静态分配的存储空间。

你信中的这些例子使我怀疑你可能被这样一个事实困住:const关键词和volat都是类型说明符。

例如,下面声明中的const:

const void *vectorTable[] = {..<data>..};               (2)

没有直接适用于vectorTable,而是适用于void。这句声明表示vectorTable的类型是“指向const void的指针序列”。但是明显你希望它是“指向void的指针的const序列”。

有另外一个重要的观点:

声明说明符在一个声明中出现的顺序并不重要。

所以,比如:

const VP vectorTable[]

等同于:

VP const vectorTable[]

还有,

const void *vectorTable[]

等同于:
void const *vectorTable[]

大多数人把存储类说明符(如static)作为第一个(最左边的)声明说明符,但是它只是一个惯例,并非是语言要求。

声明说明符const和volatile不寻常,因为:

能出现在声明符中的声明说明符只有const和volatie。

例如,下句中的const:

void *const vectorTable[]

出现在声明符中。这种情况,你不能重排关键词的顺序。比如:

*const void vectorTable[]

是错误的。

声明风格

正如我之前解释的一样,声明说明符的顺序对编译器来说并不重要。所以,下面的声明是等同的:

const void *vectorTable[]          (3)
void const *vectorTable[]          (4)

大多数C和C++程序员更喜欢把const和volatile写在其他类型的说明符的左边,同(3)。而我更喜欢把const和volatile写在右边,如(4),而且强烈推荐这样写。

C++基本上是按从头到尾、从左到右的顺序来读,但是指针的声明,从某种意义来讲却是倒着的。指针的声明是从右到左来看。把const放在其他类型说明符的右边,可以严格的从右到左来看指针声明,还可以把const从“右边的”位置提出来,如:

T const *p;

把p声明为“指向const T 的指针”,非常准确,同样:

T *const p;

把p声明为“指向T的const指针”,也能正确的理解。

把const写在其他声明说明符的右边,实际上可以更容易的看出const和类型名称相结合的效果。用信中的原始例子:

typedef void *VP;
const VP vectorTable[]

一种解释是把VP按下面的方法重新放置:

const VP vectorTable[]
const void *vectorTable[]

显然vectorTable的类型是“指向const void 的指针序列”,这是错误的! 正确的理解应该是按下面的方法:

const VP vectorTable[]
void *const vectorTable[]

vectorTable的类型是“指向void的const指针的序列”,但是并不明显。把const写在最右边更容易理解:

VP const vectorTable[]
void *const vectorTable[]

现在,我意识到自己正在推荐一种几乎没人使用的风格。只是因为每个人都把const放在左边。甚至,太少的C和C++程序员真正明白他们在声明中使用const的意义,“其他人都这么做”,这句话对支持这种流行风格的人来说几乎是没有非议。为什么不抵制这种趋势来尝试更清晰的风格?

既然写到这里,我也谈一下相关的风格。尽管大多数C程序员在这方面看起来保留着清白,但是很多C++程序员已经养成了最不好的书写习惯:

const int* p;

而不是

const int *p;

他们用空格把*和声明说明符放在一起,而不是把*和声明符放在一起。我真的认为C++程序员写这种风格的声明是在害人害己。当然,空格对编译器没有影响,但是把空格放在*后面会给人留下对声明结构的错误印象。分清前一个声明说明符和声明符的边界是理解声明的关键之处。用空格打破声明符只会造成混乱。

我希望我已经回答了你的问题,也阐明了一些问题。

const T vs. T const ——Dan Saks 【翻译】相关推荐

  1. const int * 、int * const、int const* 、const int a(){ } 和int a()const { }的区别和联系

    前言:很多人都把const int * .int * const.int const* 的区别和联系搞混,我自己在学习C++的过程中,也经常性          弄不 清楚,今天特意总结一下,作为学习 ...

  2. 经过研究发现。c++里面的const意思是说,我是const,成员也是const!!!

    class A1{  int i; public :    const  A1&  getA1 () const; };    const A1&  A1::getA1  () con ...

  3. 对于这个函数const int func(const int a) const声明中,三个const分别是什么意思?...

    第一个const 函数的返回值类型是const. 这个const修饰没什么意义,你可以想象一下: 既然是函数的 返回值,而且是值传递的形式,是否const有什么意义.如果指针(引用)传递,怎表示返回值 ...

  4. C++中const引用和非const引用的使用注意

    今天学习时突然有疑惑,C++有了指针为何还要设计引用呢?后来看到一篇博客豁然开朗:为什么 C++ 有指针了还要引用? 总结一下: 代码更加简洁好看了 由于引用必须被初始化,并且之后也无法重新绑定其他对 ...

  5. const、static、const staic理解

    Table of Contents 1 static的理解 2 const 新自定义类型 p; 则p不可变 3 不可重入函数 4 类的static成员变量 5 类的const成员变量 6 类的stat ...

  6. 【转】C++ const用法 尽可能使用const

    http://www.cnblogs.com/xudong-bupt/p/3509567.html C++ const 允许指定一个语义约束,编译器会强制实施这个约束,允许程序员告诉编译器某值是保持不 ...

  7. const char * 和 char const * 和 char * const 区别

    我们都用过const 修饰符修饰一个变量,而且我们知道使用const修饰之后,这个变量相当于常量了,他的值在代码其他部分不能再修改,这对于函数调用时候,防止错误的修改原本不应修改的变量起到很大作用,举 ...

  8. const int 和INT const区别

    const int 和INT const区别 2010-04-09 23:26 const int a = 5; int const b = 6; 没区别 指针的时候有区别,引用也有区别 指针的话 1 ...

  9. const char *p,char const *p, char * const p之间的区别

    const char * p 和 char const * p 是一个意思,都是p所指向的变量的值不能改变,例如: const char ch = 'a'; const char* p = & ...

  10. const成员函数、const类对象、mutable数据成员

    1. const成员函数 只是告诉编译器,表明不修改类对象. 但是并不能阻止程序员可能做到的所有修改动作,比如对指针的修改,编译器可能无法检测到 2. 类体外定义的const成员函数,在定义和声明处都 ...

最新文章

  1. treeview托拽和动态添加节点以及treeview和xml的交互的实现
  2. 如何批量删除会计科目、供应商、客户的主数据
  3. dynamic programming for knapsack with repeated items algorithm demonstration
  4. 【NLP】发现一篇专门吐槽 NLP 内卷现状的 ACL 论文 ...
  5. LeetCode 01. 两数之和
  6. MVC应用程序播放FLV视频,部分视图可多地方重复引用
  7. ctk介绍、安装、使用详细说明pdf文档(中文).rar_Minio 安装和使用详解,还有对.net api进行了二次封装...
  8. 小学期实践心得(1)
  9. SVD在推荐系统中的推导及应用-简单明了
  10. 图像分割各种评测标准
  11. DELPHI 编程(一) __快速认识Delphi
  12. Sketch 52 破解版 最佳Mac产品原型设计工具
  13. MyEclipse 10 破解教程
  14. 计算机原理 复位信号解释,复位电路原理
  15. 【VMware】NAT模式、桥接模式、仅主机模式
  16. 如何提升自己写代码的能力
  17. Elasticsearch Field Options Norms
  18. 护眼灯对孩子眼睛好吗?盘点最好的儿童护眼灯品牌
  19. spring boot 配置默认数据连接池 HiKariCP
  20. 基于Sphinx的中文全文检索引擎Coreseek的安装

热门文章

  1. 用python做网站的步骤_Python建网站的步骤
  2. BUUCTF:小易的U盘
  3. 求解一元二次方程,包含复数解
  4. 牛客刷题系列(C++)——详解MGJ8 链表合并(目前内存开销最小)
  5. 划分子网(计算机网络)
  6. MEION:锁相环锁定状态仿真与板载测试情况分析
  7. 集线器、交换机、路由器、猫
  8. iOS开发--AVPlayer实现音乐播放器
  9. Shared_ptr循环引用解决(weak_ptr的作用)
  10. 手机显示DNS服务器异常,DNS服务器异常?