浅谈C++指针变量与const关键字

2017-01-11 15:23:49来源:oschina作者:SamYjy人点击

第七城市


#**免责声明:本文的目的是进行计算机编程和一些注意事项的纯技术探讨,对于读者应用本文所述事项进行非法行为探索造成的后果一概不负责任。**#
C/C++语言中,指针是指包含内存地址(通常对应相应的值)的一种变量。值的意义就非常广泛,可以是基本数据类型的常量和变量,或者是对象。指针的定义,通常是数据类型加星号再加变量名的格式,形如:
int /*countPtr, /*vPtr;
double /*xPtr, /*yPtr;
特别地,在C++11中,初始化的指针变量如果不指向任何其他地址变量,应附上初始值nullptr。在C++中,一维指针通常可以用来表示一个数组(一组数据)或单个变量,它们在函数之间的传递属于地址传递。C++中还有一个关键字叫做const,用来修饰变量时表示此变量理论上不宜被修改,若修改会导致编译报错。const也可以修饰指针变量。因此,const变量与指针变量就构成了4中对应关系。
第一种是无const修饰的指针变量,形如上述定义所示。第二种为指向const修饰的变量的指针变量,形如const int /* vPtr。第三种指向变量的定指针,形如int /* const vPtr。最后一种为指向常量的定指针,形如`
const int /* const vPtr。为弄清楚它们运行的机理,博主专门写了一个小代码分别在mingw和Unix上进行了编译。本文结合这个小程序以及它对应的结果,谈谈指针变量和const关键字之间的关系。这个小程序的函数定义和包括的头文件如图所示:
![示例程序头文件和函数定义](/2014th7cj/d/file/p/20170111/dho5o54sekw.png "示例程序头文件和函数定义")
####这里是列表文本小程序中, 四个printArray函数分别对应这四种关系。其中,printArray4这个方法只是用来实现输出传入的数组(由指针变量对应,由描述数组大小的尺寸变量控制数组长度),后面谈到第四点的时候会详细解析。## 一、无const修饰的指针变量
![函数1代码示例](/2014th7cj/d/file/p/20170111/w2vl1q10el3.png "函数1代码示例")
![main函数中对于该函数的调用示例](/2014th7cj/d/file/p/20170111/w1crbn1y5fg.png "main函数中对于该函数的调用示例")
#### 很明显,这个函数由于数组中的每个变量都不是常量,指针也不是定指针,因此对于每个元素加一的操作也没有什么争议。需要注意的是,对于指针变量的操作为地址传递,因此main函数中的原数组每个元素也相应地被修改为每个元素的值增加1.## 二、指向const修饰的变量的指针变量
![函数2代码示例](/2014th7cj/d/file/p/20170111/ievl4unnnd4.png "函数2代码示例")
![main函数中对于该函数的调用示例](/2014th7cj/d/file/p/20170111/ez2ocp0xcyw.png "main函数中对于该函数的调用示例")
#### 此函数中,由于传入指针时对于指针变量所对应的变量用const进行了修饰,因此想通过指针操作对于数组的每个变量进行修改时编译报错(操作(*a)++就是试图修改每个数组变量的操作,注意运算符的优先级++优于/*,因此括号一定要加)。通过备份的指针进行操作,仍然报错。(*aPtr)++是报错的。
#### 此处遍历数组完毕之后需要对指针进行复位,否则在方法四输出时就导致数组越界(此时指针已经指向了数组的最后一个元素,要用备份的那个指向第一个元素的指针对a进行复位)。
## 三、指向变量的定指针
![函数3代码示例](/2014th7cj/d/file/p/20170111/nkh1aygewrt.png "函数3代码示例")
![main函数中对于该函数的调用示例](/2014th7cj/d/file/p/20170111/s1nu1abuypw.png "main函数中对于该函数的调用示例")
#### 此处发生的现象就比较有意思了。首先,传入的函数指针对应的变量没有用const修饰,然而指针变量使用const函数修饰的。因此,理论上指针变量本身是不能被修改的,而指针对应的变量是可以修改的。这在这个函数中也得到了印证。即(*a)++是可以通过编译的,然而a++不行。
#### 然而需要注意的是,将传入指针a用指向变量的指针(未带const修饰符)进行备份之后,对于指针变量的移动居然就实现了。**这也体现了编译器虽然对于指针是否带const修饰符可以直接识别,并且对于直接或简介带const修饰符的变量也可以识别(体现在带const不能被赋值给不是const的变量)但是是无法间接识别的。**为防止编程中出错,这个现象需要格外注意。
这里我们再举一例字符串指针的示例。
![模拟银行账户caller方法中的调用示例](/2014th7cj/d/file/p/20170111/aljzdrt3j1t.png "模拟银行账户caller方法中的调用示例")
![模拟银行账户一个人为失误的callee写法](/2014th7cj/d/file/p/20170111/hgl0o04puw3.png "模拟银行账户一个人为失误的callee写法")
![模拟银行账户运行结果示例](/2014th7cj/d/file/p/20170111/b4c5cg40ykm.png "在这里输入图片标题")
“mingw”这个账户的“密码”和“资金”数量出现问题的原因正式因为mallicious这个方法不恰当地复制了指针并且进行了修改。注意正常情况下只有账户名"mingw"是允许被修改的,其它的理论上都不应该被修改。
## 四、指向常量的定指针
![函数4代码示例版本一](/2014th7cj/d/file/p/20170111/bdyvkgl1vvw.png "函数4代码示例版本一")
![函数3代码示例版本二](/2014th7cj/d/file/p/20170111/d0oi0x5kqx2.png "函数3代码示例版本二")
![main函数中对于该函数的调用示例](/2014th7cj/d/file/p/20170111/jtttmhgva1r.png "main函数中对于该函数的调用示例")
#### 此函数的目的在于输出传入数组。既然是输出,那么原指针地址和每个地址对应的值都没有被修改的理由,这第四种关系的本义也是如此。函数也可以写成版本一。然而,为了再次验证“被备份了的指针可能可以‘篡改’数据和指针变量”这个现象,本程序将这一段写成了版本二,在编译方面也是能够通过的。
## 五、总结
![小程序运行效果](/2014th7cj/d/file/p/20170111/wlmir3jyuv3.png "小程序运行效果")
#### 本文讨论了函数变量与const修饰符可能的四种关系。通过本小程序,我们了解到const修饰符对于一般变量确实具有约束作用,能够起到约束一般变量成为不可修改的常量的作用。该修饰符对于指针变量也具有直接的约束作用,然而没有间接的约束作用。因此,通过备份const指针的方式, 就可以对于原指针对应的内存地址和内存地址对应的数据进行浏览和篡改,这是值得引起我们注意和警惕的一个现象。使用带有const的指针应当注意理论上是限制其变换地址的,而实际上无论mingw还是Unix系统都不对指针是否为定指针进行检查,因此写此类函数要注意尽量不要备份指针,否则可能会产生意想不到的效果。
参考资料:
1. Paul Deitel & Harvey Deitel, C++11程序设计(英文版)(第2版)
1. 知乎:指针可以修改const修饰的变量么?链接:https://www.zhihu.com/question/21792567
1. const在声明指针变量时的三种情况,链接:http://blog.csdn.net/kiss_ben/article/details/8127751
1. const型变量和const型指针,链接:http://www.cnblogs.com/Dezhong-chen/p/4734516.html
第七城市

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台