[译]const T vs. T const ——Dan Saks 【翻译】
http://m.blog.csdn.net/blog/bianbian17556231/5398039
写程序的过程中遇到了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 【翻译】相关推荐
- const int * 、int * const、int const* 、const int a(){ } 和int a()const { }的区别和联系
前言:很多人都把const int * .int * const.int const* 的区别和联系搞混,我自己在学习C++的过程中,也经常性 弄不 清楚,今天特意总结一下,作为学习 ...
- 经过研究发现。c++里面的const意思是说,我是const,成员也是const!!!
class A1{ int i; public : const A1& getA1 () const; }; const A1& A1::getA1 () con ...
- 对于这个函数const int func(const int a) const声明中,三个const分别是什么意思?...
第一个const 函数的返回值类型是const. 这个const修饰没什么意义,你可以想象一下: 既然是函数的 返回值,而且是值传递的形式,是否const有什么意义.如果指针(引用)传递,怎表示返回值 ...
- C++中const引用和非const引用的使用注意
今天学习时突然有疑惑,C++有了指针为何还要设计引用呢?后来看到一篇博客豁然开朗:为什么 C++ 有指针了还要引用? 总结一下: 代码更加简洁好看了 由于引用必须被初始化,并且之后也无法重新绑定其他对 ...
- const、static、const staic理解
Table of Contents 1 static的理解 2 const 新自定义类型 p; 则p不可变 3 不可重入函数 4 类的static成员变量 5 类的const成员变量 6 类的stat ...
- 【转】C++ const用法 尽可能使用const
http://www.cnblogs.com/xudong-bupt/p/3509567.html C++ const 允许指定一个语义约束,编译器会强制实施这个约束,允许程序员告诉编译器某值是保持不 ...
- const char * 和 char const * 和 char * const 区别
我们都用过const 修饰符修饰一个变量,而且我们知道使用const修饰之后,这个变量相当于常量了,他的值在代码其他部分不能再修改,这对于函数调用时候,防止错误的修改原本不应修改的变量起到很大作用,举 ...
- const int 和INT const区别
const int 和INT const区别 2010-04-09 23:26 const int a = 5; int const b = 6; 没区别 指针的时候有区别,引用也有区别 指针的话 1 ...
- const char *p,char const *p, char * const p之间的区别
const char * p 和 char const * p 是一个意思,都是p所指向的变量的值不能改变,例如: const char ch = 'a'; const char* p = & ...
- const成员函数、const类对象、mutable数据成员
1. const成员函数 只是告诉编译器,表明不修改类对象. 但是并不能阻止程序员可能做到的所有修改动作,比如对指针的修改,编译器可能无法检测到 2. 类体外定义的const成员函数,在定义和声明处都 ...
最新文章
- python log函数怎么打_Python的log日志功能及设置方法
- Facebook新推出AL语言,意在简化程序静态分析
- java json格式的转换和读取
- 【网址收藏】Spark History Server配置及使用
- boost::hana::id用法的测试程序
- 22.循环控制.rs
- Linux文件基本操作
- 计算机专业买r7000,2020年双十一有哪些游戏本值得买-7千到1万游戏本排行
- delegate、notification、KVO各优缺点
- 一段实现分页的存储过程
- 证书重新生成_Kubernates证书过期问题的解决
- 微信僵尸粉删除工具 WeTool v4.0.7.0 免费版
- Eclipse使用Ant编译时报错Perhaps JAVA_HOME does not point to the JDK
- 【数学建模】——1992~2019国赛优秀论文
- 动态IP和静态IP的区别如何使用动态IP
- 面试必备:零拷贝详解
- 简单的MediaPlayer+SurfaceView实现视频横竖屏播放
- 计算机软件选修课选什么好,互联网行业,软件工程专业学什么?
- TeamViewer使用心得
- G - 数字转换 LibreOJ - 10155
热门文章
- linux 指令熟悉
- SqlServer使用top 100 PERCENT 无法排序的问题
- 计算机专业综合改革举措,数学与应用数学专业综合改革的举措.docx
- Can not set java.util.Date field *** to java.time.LocalDateTime解决办法
- Android之提示MIME type application/octet-stream cannot be inserted into **/images expected
- 【每日爬虫】:利用线程池爬取百思不得姐段子
- Android qq 登录 界面 圆头像
- 证明:在N维欧式空间中,两两互成钝角的非零向量不多于N+1个
- Android快速开发之appBase——(1).appBase介绍
- raster包—aggregate函数