`
java-mans
  • 浏览: 11414857 次
文章分类
社区版块
存档分类
最新评论

深入探索 C/C++ 数组与指针的奥秘之六:指针与 const

 
阅读更多

深入探索 C/C++ 数组与指针的奥秘之六:指针与 const

const 一词在字面上来源于常量 constant,const 对象在 C/C++ 中是有不同解析的,如第二章所述,在 C 中常量表达式必须是编译期,运行期的不是常量表达式,因此 C 中的 const 不是常量表达式;但在 C++ 中,由于去掉了编译期的限定,因此是常量表达式。
对于一个指向 const 对象的指针 pointer to const T,由于把 const 视作常量表达式,常常存在如下两种观点:
1。这是一个指向常量的指针,简称常量指针;
2。这个指针指向的内容不可改变。
这是比较粗糙的理解。虽然这个指针的类型是 pointer to const T,但不代表它指向的对象真的是一个常量或者不可改变,例如:


p 指向的对象 i 明显不是常量,虽然 p 指向 i,但 i 的值依然可以改变。对于这个现象,C++ 标准有明确的论述:
7.1.5.1 The cv-qualifiers
a pointer or reference to a cv-qualified type need not actually point or refer to a cv-qualified object, but it is treated as if it does;
其中 cv 指的是 const 和 volatile,const 和 volatile 叫 type qualifier,类型限定词。const T 只是类型假定,并非指出该对象是什么,这个对象也许是 const 限定的,也许不是。既然上述两种看法都是不恰当的,pointer to const T 又应如何看待呢?一种比较好的理解是,将其视作一条访问路径。对一个对象进行取值或者修改操作,可以有很多种方法,每种方法都相当于一条能够对对象进行访问的路径,例如:


通过 *q、*p 和标识符 i 都能访问 i 所代表的整数对象,它们可以视作三条路径,i 和 *q 能够修改该整数对象的值,这两条路径是可写可读的;但 *p 不能写,因为 p 指向的对象被假定为 const,从 p 的角度看来,*p 是只读的,不能通过 p 修改它指向的对象。因此,一个 pointer to const T 指针的确切意义,不是指向常量或者指向的对象不可改变,而是指不能通过这个指针去修改其指向的对象,无论这个对象是否 const,它只指出一条到该对象的只读路径,但存在其它路径可以修改该对象。这种理解,在标准中是有根据的:
7.1.5.1 The cv-qualifiers
a const-qualified access path cannot be used to modify an object even if the object referenced is a non-const object and can be modified through some other access path.
上述条款对访问路径进行了一个清晰的描述。
一个 pointer to T 类型的指针,可以赋值给一个 pointer to const T 类型的指针,这是众所周知的语法规则。笔者曾经一度认为,两者之所以可以赋值,是基于指针的相容性原理,以为两者是相容的,后来翻阅了 C/C++ 的标准,才认识到这种解释其实是错误的,从相容性原理来说,两者恰恰是不相容的。C 标准关于指针的相容性是这样规定的:
6.7.5.1 Pointer declarators
For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.
两个相容的指针,既要有同一的限定修饰词,所指向的类型也要相容的。而两个相容的类型要符合如下规定:
6.2.7 Compatible type and composite type
Two types have compatible type if their types are the same.
两个相同的类型才具有相容性,那么 cont T 和 T 是否两种相同的类型呢?再看如下条款:
6.2.5 Types
The qualified or unqualified versions of a type are distinct types that belong to the same type category and have the same representation and alignment requirements.
一个类型的限定和非限定版本是同一种类类型的具有同一表示范围及对齐需求的不同类型。这就是说,const T 和 T 不是相同的类型,两者不相容,于是,虽然 pointer to const T 与 pointer to T 具有同一的限定修饰(都没有限定词),但所指向的对象类型不是相容的类型,因此 pointer to const T 与 pointer to T 是不相容的指针类型。
既然两者不相容,又是什么原因导致它们可以赋值呢?再查阅 C 标准关于赋值运算符的规定,发现有这么个条款:
6.5.16.1 Simple assignment
Constraints
One of the following shall hold:
………
— both operands are pointers to qualified or unqualified versions of compatible types,
and the type pointed to by the left has all the qualifiers of the type pointed to by the
right;
噢,其实原因在这里!左操作数所指向的类型要包含右操作数所指向类型的所有限定词。pointer to const T 比 pointer to T 多一个 const,因此可以将 pointer to T 赋值给 pointer to const T,但反过来不行。通俗一点说,就是左操作数要比右操作数更严格。C++ 中的规定与 C 有点不同,C++ 标准去掉了这一条款,代之以 more cv-qualified 的概念,一个 pointer to cv1 T 的指针,要转换为一个 pointer to cv2 T 的指针,条件是 cv2 比 cv1 要更 cv 限定化。
要注意的一点是,这条赋值运算符的规则只适用于 pointer to qualified or unqualified type,不能延伸到 pointer to pointer to qualified or unqualified type 及更高级别的指针类型,例如:


A 合法,但 B 不合法。虽然 p1 与 &q 都是 unqualified 的,但 p1 指向的对象类型为 pointer to const int,&q 指向的类型为 pointer to int,如前所述,两者是不相容类型,不符合两操作数必须指向相容类型的规定,因此赋值非法。
根据上述规则,一个 pointer to const T 不能赋予 pointer to T,但是,一个 const pointer 却能赋予 non-const pointer,例如:


A 合法,这种情况并不属于赋值运算符的规则之内,它遵循的是另一个条款:左值转换。一个被限定修饰的左值,在进行左值转换之后,右值具有左值的非限定修饰类型:
6.3.2 Other operands
6.3.2.1 Lvalues, arrays, and function designators
Except when it is the operand of the sizeof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue). If the lvalue has qualified type, the value has the unqualified version of the type of the lvalue; otherwise, the value has the type of the lvalue.
p 的值具有 p 的非限定修饰类型 int*,与 q 类型相容,因此赋值合法。对于 C++,基本上与 C 相同,但有一个例外,就是右值类对象,由于右值类对象仍然是一个对象,C++ 规定右值类对象具有与左值相同的限定修饰词。
指针与 const 的结合能够产生一些比较复杂的声明,例如:


这是一个较为复杂的指针声明符与 const 限定修饰词的组合,声明符部分嵌套了六次,中间还带有两个 const,如何辨认哪一级是 const,哪一级不是呢?一旦明白了其中的原理,其实是非常简单的。第一和最后一个 const 大家都已经很熟悉的了。对于藏在一堆 * 号中的 const,有一个非常简单的原则:const 与左边最后一个声明说明符之间有多少个 * 号,那么就是多少级指针是 const 的。例如从右数起第二个 const,它与 int 之间有 4 个 * 号,那么 p 的四级部分就是 const 的,下面的赋值表达式是非法的:


但下面的赋值是允许的:


从左边数起第二个 const,它与 int 之间有 1 个 *,那么 p 的一级部分是 const 的,也就是 *****p = (int*const***const*)10; 是非法的。
原文链接:http://blog.csdn.net/supermegaboy/archive/2009/11/23/4854974.aspx

分享到:
评论

相关推荐

    C++动态数组类模板

    动态数组类模板Array C++ //数组类模板声明 template class Array { public: Array(int sz = 50); Array(const Array<T> &A); ~Array(void); Array<T>& operator = (const Array<T> &rhs;); //重载=,使数组...

    指针数组和数组指针的区别.doc

    []优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1是错误的,这样赋值也是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],...

    vs code C/C++语法高亮配置文件(C/C++ Themes插件)

    vscode配色插件的c/c++语法高亮配置文件,主题插件为C/C++ Themes。 可以对诸如const、enum、typedef别名、结构体引用等语法高亮进行设置,语言本身的关键字自然不用说了,比one dark pro等热门的插件颜色丰富的多。...

    Lua和C/C++互相调用实例分析

    lua作为小巧精悍的脚本语言,易于嵌入c/c++中 , 广泛应用于游戏AI ,实际上在任何经常变化的逻辑上都可以使用lua实现,配合c/c++实现的底层接口服务,能够大大降低系统的维护成本。下面对lua和c/c++的交互调用做一...

    std::string、char*、const char*转托管byte数组或托管字符串String

    std::string、char*、const char*转托管byte数组或托管字符串String

    c/c++语言中const的用法,const 定义变量,指针的用法和不同

    c/c++语言中const的用法,const 定义变量,指针的用法和不同

    C/C++程序员面试指南.杨国祥(带详细书签).pdf

    面试题28:C++的引用和C语言的指针有什么区别 第4章 预处理、保留字 4.1 预处理 面试题1:简述#ifdef、#else、#endif和#ifndef的作用 面试题2:宏定义和函数 面试题3:用#define声明一个常数 面试题4:写一个“标准...

    简单分析C语言中指针数组与数组指针的区别

    首先来分别看一下,指针数组的一个小例子: #include #include <string> int lookup_keyword(const char*key, const char* table[], const int size) { int ret = -1; int i = 0; for(i=0; i<size; i++)...

    指针数组和数组指针

    识别const到底是修饰指针还是指针所指的对象,还有一个较为简便的方法,也就是沿着*号划一条线:

    C++栈类模板

    C++栈类模板 template class Stack { public: Stack(void); void Push(const T &item;); //将元素item压入栈 T Pop(void); //将栈顶元素弹出栈 void ClearStack(void); T Peek(void)const; //访问栈顶元素 ...

    免费下载:C语言难点分析整理.doc

    49. 深入理解C语言指针的奥秘 236 50. 游戏外挂的编写原理 254 51. 程序实例分析-为什么会陷入死循环 258 52. 空指针究竟指向了内存的哪个地方 260 53. 算术表达式的计算 265 54. 结构体对齐的具体含义 269 55. ...

    数组和指针的关系

    数组名是个常量指针(const pointer)?  你TM在逗我?数组名只是个名字,你能给它个类型?  像下面这些命题:  数组是指针  数组是指针常量  数组是常量指针  数组名是常量指针  数组名是指针常量  ...

    c语言难点分析整理,C语言

    49. 深入理解C语言指针的奥秘 236 50. 游戏外挂的编写原理 254 51. 程序实例分析-为什么会陷入死循环 258 52. 空指针究竟指向了内存的哪个地方 260 53. 算术表达式的计算 265 54. 结构体对齐的具体含义 269 55. ...

    C++ 课程作业 第六章 数组指针与字符串2——vector类(图书管理)

    一、请设计一个Book类(与动态数组类的要求一致): 1、包括私有成员: unsigned int m_ID;//编号 string m_Name;//书名 string m_Introductio//简介 string m_Author;//作者 string m_Date;//日期 unsigned ...

    C语言指针-从底层原理到熟练应用(含源码)

    一、前言 二、变量与指针的本质 1. 内存地址 2. 32位与64位系统 3. 变量 4. 指针变量 5. 操作指针变量 ...3. 指针数组与数组指针 4. 二维数组和指针 5. 结构体指针 6. 函数指针 五、总结

    c/c++ 学习总结 初学者必备

    (C语言里参数传递都是传值,是一个拷贝,修改指针,只是改变了拷贝的指向,原指针指向并没有改变,而修改指针的内容则是可以的。)如果函数的参数是一个指针,不要指望用该指针去申请动态内存。(即上面所说的修改...

    详细分析c++ const 指针与指向const的指针

    最近在复习C++,指针这块真的是重难点,很久了也没有去理会,今晚好好总结一下const指针,好久没有写过博客了,记录一下~ const指针的定义:  const指针是指针变量的值一经初始化,就不可以改变指向,初始化是...

    高质量C/C++编程指南(PDF)

    目 录 前 言6 第1 章 文件结构 1.1 版权和版本的声明. 1.2 头文件的结构. 1.3 定义文件的结构. 1.4 头文件的作用. 1.5 目录结构. 第2 章 程序的版式 2.1 空行. ...附录C :C++/C 试题的答案与评分标准.

    轮转实现c++代码小孩数组

    //建立小孩数组 const int n=7; //小孩个数 int interval=20; //每次第interval个小孩,让该小孩离开,interval的初值为20 int a[n]; //小孩数组

    高质量C++编程指南.PDF

    高质量C/C++编程指南.PDF 作者:林锐 目录: 前 言 第1章 文件结构 1.1 版权和版本的声明 1.2 头文件的结构 1.3 定义文件的结构 1.4 头文件的作用 1.5 目录结构 ...附录C :C++/C试题的答案与评分标准

Global site tag (gtag.js) - Google Analytics