在我的上一个专栏里,我讨论了为什么C++在数据存入ROM所使用的规则比C语言所使用的规则稍微复杂原因的其中一个。关于这个话题现在我有更多的内容想说,但是在那之前,我很乐意先回答Phil Baurer先生通过E-mail向我咨询的一个关于小松(日本一个地方)小型采矿系统的问题:
“我们正在对使用带有typedef的const的问题饶有兴趣。我希望您评论下这种用法。我正在思考是不是我们碰巧遇到了一些我们不知道的C语言规则”
“我们正在Hitachi SH-2 32位 RISC微控制器上使用Hitachi C编译器。我们认为下面的代码:
typedef void *VP;
const VP vectorTable[]
= {..<data>..};     (1)
应该等同于:
const void *vectorTable[]
= {..<data>..};     (2)”
“然而,连接器把vectorTable放在(1)中CONSTANT部分,但是(2)中方在了DATA部分。”
“这是合适的做法吗或者还是编译器的一个BUG?”
这是一个恰当的做法,不是BUG。你确实碰巧遇到了一些你显然不知道的C语言规则。不要沮丧,我的朋友,不仅仅是你这样,我相信很多其他的C和C++程序员对这些规则都很困惑,这也正是我这期专栏想要解决的问题。
我在早期专栏里提到过一些这些规则,然而,现在回顾那些文章,我认为我没有充分的解释清楚这个问题的根本所在,现在就让我再解释一次。
声明
这里是第一个观点:
在C和C++中每一个声明符都有两个基本部分组成:一串由个零个或多个字符组成的类型修饰符,以及由一个或多个声明符;中间用逗号隔开。
(Every declaration in C and C++ has two principal parts: a sequence of zero or more declaration specifiers, and a sequence of one or more declarators, separated by commas.)
例如:static unsigned long int       *x[N];
declaration specifiers      declarator
一个声明符就是一个被声明的名字,可能被很多操作符类似如*, [], (), and (在C++中).&连在一起。正如你已经知道的,这个符号“*”代表指针而“[]”意思是数组。这样*x[N]这个声明符就表示X是一个含有N个元素的在声明符中已经制定了的指针数组。例如:static unsigned long int *x[N];表示声明变量对象X是一个指向含有N个unsigned long int型数据的指针数组(在后面将解释关键词static并不在对类型起作用)。
我们怎么知道*x[N]是一个数组指针还是一个指针数组哪?有以下规则:对声明符的操作顺序取决于的修饰符在一个表达式中表现的优先级。
例如,如果你参照下最近无论是C还是C++的优先级列表,你都将看到[]比*优先级高,这样在声明*X[N]中X首先是一个数组,其次它是一个指针。
圆括号在这个声明符中有两种作用:首先,函数调用操作,()与[]有相同的优先级;在分组时,()拥有最高的优先级。例如:*f(int)声明f是一个函数,返回指针类型。相反(*f)(int)是一个指向函数的指针。一个声明符通常包含不止一个标志符。这个声明*x[N]包含两个标志符,x和N。仅仅一个标识符是需要被声明的,其他的必须在前面声明。也就是说x[N]声明的标志符是x。.
一个声明符没有必要包含太多的操作。在一个声明中:int n;没有做除声明n意外的任何操作。
声明指定
一些声明修饰符可以指定一些声明符为int、unsigned等类型,或者一个新类型的标志符,也可以被声明为某个如extern或者static的类型。C++中也可以用inline或者virtual来修饰声明函数标志符。这里有这样一个观点:
类型修饰符为标志符类型的标志ID,其他的修饰符提供了非类型信息但是却直接对标志ID有用。
例如:static unsigned long int *x[N];
声明符x是一个含有N个unsigned long int数据的指针数组,关键词static表明C只有静态分配内存。在你信中提到的例子另我怀疑你可能正在被这样一个事实迷惑:关键词const和valatile是类型修饰符。例如,const在(2)中,并没有直接修饰vectorTable,而是修饰的void 。这表明vectorTable作为一个const void类型的指针数组,似乎你希望它是指向void的一个数组常指针。
这里还有一个重要的观点:在声明修饰符中的顺序与声明的顺序不匹配。
例如:const VP vectorTable[]等同于VP const vectorTable[],而const void *vectorTable[]等同于void const *vectorTable[]。
我们大都把存储器类声明符比如static放在声明符第一位(最左边),但是仅仅是通常情况下,不是语言的需要。声明符const 和volatile与此不同:不仅仅能出现在声明修饰符中,也能用作声明符。例如const在void *const vectorTable[]作为声明符,这种情况下你不能确定关键词的顺序,例如*const void vectorTable[]就是个错误的例子。
一种提倡的代码风格
正如我前面所解释的,声明修饰符的顺序取决于编译器,而与声明的顺序无关,因此const void *vectorTable[] (3)和 void const *vectorTable[] (4)等价。几乎所有的C和C++程序员更喜欢类似(3)式写const和volatile在表达式的最左边,而我更喜欢写它们在类似(4)的最右边,并且强烈推荐这种方式。
尽管C和C++大都是从顶向下和从左自右,然而指针声明符一定意义上说是逆着来。也就是说指针声明符是从右向左的,并且使const出现在最右边。例如声明T const *p;声明了一个指向常T类型的指针,而T *const p;声明了一个指向T类型的常指针。把const写在其他声明符的右边能使const限制符的作用更容易分辨。使用原来在信中的例子:typedef void *VP;
const VP vectorTable[]。
一种解释是替换VP:
const VP vectorTable[]
const void *vectorTable[]
这似乎使得vectorTable是指向一个常void类型指针数组的意义显得更明显。但是这是错误的。正确的理解是替换VP为
const VP vectorTable[]
void *const vectorTable[]
这样,vectorTable一个指向空类型常指针数组,但是一点也不明显。
把const写在声明符的最右边能使它更容易解释:
VP const vectorTable[]
void *const vectorTable[]
现在,我意识到我在建议一种很少人用的风格。仅仅针对每一个用const的在左面的使用者。然而,究竟会有多少C和C++程序员明白他们在使用const的地方到底在做什么。每个人都不这样做是对现在流行的风格的支持的一个争论。只要我还在从事这个行业,我就会义无反顾的支持这种风格。虽然很多C程序员对此没有任何影响,但是许多c++程序员却不幸的养成了这样一个书写习惯。
const int* p;
而不是
const int *p;
这就是说,他们把空格和*作为声明修饰符一起使用而不是声明符。用这种风格写代码的话,我真的相信C++程序员这么做并且互相制造麻烦。的确,空格的位置对编译器来说没有任何区别,但是把空格放在*后面一会造成一种错误的表象在理解声明符的时候。意识到在最后声明修饰符和声明符的分界点是理解声明符一个重要关键之一。用空格分开只能令人产生迷惑的情形。
我希望我的回答能令你满意并且澄清了你的疑惑

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/night_elf_1020/archive/2008/12/06/3460715.aspx

const T 与T const(const T vs.T const的翻译 Dan Saks)相关推荐

  1. 【C 语言】const 关键字用法 ( 常量指针 - const 在 * 左边 - 修饰数据类型 - 内存不变 | 指针常量 - const 在 * 右边 - 修饰变量 - 指针不变 )

    文章目录 一.const 普通用法 二.常量指针 ( 指向 常量 的指针 | 被指向的内存不能被修改 ) 三.指针常量 ( 指针不能被修改 ) 三.指向 常量 的 指针常量 四.const 在 * 左 ...

  2. const int *p说明不能修改_C语言关键字const和指针结合的使用

    C语言中,const 的作用是把变量变为一个只读的变量.与指针结合起来,有以下几种用法,下面分别进行说明. const int p; const int *p; int * const p; cons ...

  3. C++ const关键字的总结(全局/局部变量、修饰指针和引用、成员函数和数据成员、修饰类对象、const与宏定义的区别、Static与Const的区别)

    const关键字 const关键字 1.什么是const 2.使用原理 2.1.const全局/局部变量 2.2.cosnt修饰指针和引用 2.3.const修饰函数参数 2.4.const修饰函数返 ...

  4. 定义const变量是不可以赋值_定义好 const 变量

    我们 a 组组长发了一张图过来,说是为什么之前把 dateModeTransform.js 处理日期的改了,让我改回来,说是要用到 年月日时分秒,还有之前的写法也有很多不妥的地方就是传递的参数太多了, ...

  5. const mysql_mysql – 为什么我在解释查询中读取const表后会注意到“不可能”?

    我在表中有一个像fr(fromid,toid)这样的唯一复合键,当我使用explain运行查询时,我得到以下结果: Impossible WHERE noticed after reading con ...

  6. const 指针_C语言学习日记(11)——const与指针

    对于一个普通指针typet *p.p有三个最基本的能力,第一就是可以通过p = &obj来指向一个type类型对象,并随意切换指向对象.第二就是通过value = *p来读取它指向的对象的值. ...

  7. c语言const 修饰二级指针,C++中const修饰二级指针(从类型‘int**’到类型‘const int**’的转换无效)...

    先上代码: void func(const int ** arg) { } int main(int argc, char **argv) { int **p; func(p); return 0; ...

  8. const关键字在c语言的作用,C语言const关键字作用

    C语言中const代表着"不可变",基本和常量一样不可修改,但是应用场景不一样. 一.应用在变量 const char a='A'; a='B';  //错误,变量a的值不可以修改 ...

  9. 微信小程序var,let,const的区别

    var 用var的方式声明的变量,为全局变量 let 声明块级变量,即局部变量 const 用于声明常量,也具有块级作用域 const PI=3.14;

  10. js中定义变量之②var let const的区别

    var 上一篇文章有讲过,是js定义变量的关键词. 但是在es6中,新添加了两个关键词,用于变量声明的关键词:let 和const 接下来就说一下var let 和const的区别: 首先说var 用 ...

最新文章

  1. Hyper-V 性能加速之VMQ
  2. Http的请求协议请求行介绍
  3. [html] 404页面有什么作用?
  4. 爬取今日头条财经版块新闻
  5. IDEA搭建一个简单的Javaweb项目(二)
  6. python 和scikit安装
  7. JQuery杂项方法
  8. Hyperledger Fabric1.4超详细环境搭建 搭建不出来本博主倒立写代码
  9. 随笔小杂记(一)——更改整个文件夹内的命名
  10. [k8s]容器化node-expolore(9100)+cadvisor(8080)+prometheus(9090) metric搜集,grafana展示
  11. maven 编译出错解决
  12. linux把终端嵌入桌面,在你的Ubuntu Linux桌面上嵌入终端窗口
  13. PFPLD 人脸关键点检测
  14. 软件体系结构——批处理风格
  15. 刷百度权重的方法!!刷百度权重不为人知的内幕!慎用!
  16. 电脑程序在计算机丢失怎么办,电脑中dll文件丢失怎么恢复?计算机中丢失dll文件修复方法...
  17. 数据结构与算法之美笔记-链表(Linked list)
  18. 怎么查看OutputDebugString输出的信息?debugview
  19. chmod 755和chmod +x 区别 | Linux修改文件属性 | 小白笔记
  20. 行云集团独家冠名纪录片《风从东风来》,讲述中国品牌故事

热门文章

  1. [chrome插件] 利滚利计算器 银行存款 基金理财 余额宝收益计算
  2. ps添加的阴影怎么去除_PS怎么把阴影去掉
  3. 学生成绩管理系统 easyx库(半成品
  4. 很老很老的老偏方,小病一扫光
  5. 自学软件测试怎么样,有前景吗?
  6. mysql 连续打卡天数_Sql如何统计连续打卡天数
  7. 图论们,小爷来ak你们啦瓦咔咔~
  8. go 运行代码遇到的问题(同一个包,不同包 之间的引用报错)
  9. Photoshop2019 系统错误 无法启动此程序修复教程
  10. 如何将Caj转Word,免费CAJ转换的方法