最近看《C++编程思想》发现自己的基础确实不牢固,也想起了以前写代码时也因为const的事情浪费过时间,这里总结下几个要点。

首先说下内部链接和外部链接。

当一个cpp文件在编译时,预处理器首先递归包含头文件,形成一个含有所有必要信息的单个源文件,这个源文件就是一个编译单元。编译器对每个编译单元(.cpp文件)进行编译生成相应.obj文件。PS:.c文件对应.o文件

接下来关键一步:链接器将所有不相关的.obj文件进行链接,生成最终可执行文件(.exe文件)

// a.cpp:
#include "stdafx.h"
void show(); int main()
{
show();
return 0;
}
// b.cpp
#include "stdafx.h"
#include <iostream>
void show()
{
std::cout << "Hello World" << std::endl;
}

看上述代码,函数的声明和定义可以在不同的cpp文件中,并且声明可以有很多个,定义只能有一个

在某个编译单元中调用show函数,那么必须进行声明。来看下外部链接和内部链接的定义:

外部链接:一个名称对编译单元来说不是局部的,链接的时候其他编译单元可以访问它。

内部链接:一个名词对编译单元来说是局部的,链接的时候其他编译单元无法链接到它且不会与其他编译单元的同样称冲突。

可以看出,show函数是外部链接,a.cpp中的void show();等价于extern void show();

而static和全局的变量或函数都是外部链接,而由于函数的声明跟定义明显有区别(一个后接分号一个后接大括号),所以不用特地加extern区分。

回到主题,C++的const默认是内部链接的!也就是说const仅在const被定义过的文件里才是可见的。除非用extern进行声明,才会使用外部链接。

默认内部链接时,C++编译器不会给const创建存储空间,而是把它的定义放在符号表中。这称之为常量折叠,起到了跟#宏一样的效果。当然,在运行时还是会创建的,见下述例子。

#include <iostream>

int main()
{
const int i = 10;
int* p = const_cast<int*>(&i);
*p = 8; std::cout << *p << " " << i << std::endl; return 0;
}

结果是8 10

说明运行时可以取得const常量的地址,并且可以改变存放的值。但是对于const常量来说,实际只是从符号表中查找值。因此虽然内容发生了变化,但是i输出还是10。

再来看看const的常见用法(下面的int均可换成其他基本数据类型或类名)

1、和指针一起使用

const int*或int const *——不能通过指针改变int的值,但是指针指向的地址可以改变,一般使用前者

int* const——不能改变指针指向的地址,但是可以通过指针改变int的值

const int* const或int const* const——不能通过指针改变int的值,也不能改变指针指向的地址

2、一般要把const int*转换成int*需要强制转换,而字符数组是没有强调const特性的,见下面代码

#include <iostream>

int main()
{
char* pc = "hello world!";
std::cout << pc << std::endl; // 可以输出
pc[2] = 's'; // 运行时错误!
return 0;
}

编译通过,也能输出hello world!,但是运行时出错。这里"hello world!"被编译器当作常量字符数组建立的,所以得到的只是数组在内存里的首地址,对字符数组内任何字符进行修改会导致运行时错误。

要想修改字符串需要把它放在数组中,即把char* pc改成char pc[]

3、作为函数参数和返回值

作为参数:比如void f(const int i){}假如调用f(5);相当于在函数体中const int i = 5;然后再对i进行操作,所以不能修改i。

作为返回值:比如const int f(){ return 1; }假如调用int i = f();相当于const int tmp = f(); int i = tmp;所以没有任何意义,返回值进行值传递,还是能改变。

但是返回的是对象(假如是类A的对象)时,比如const A f(); 返回值不能作为左值!(左值右值以后再详述)

虽然A g();中g()的返回值可以作为左值,但是也只是临时对象,表达式被编译过后临时对象也会被清除

C++中const使用注意要点(一)的更多相关文章

  1. C++中const使用注意要点(二)

    当const修饰类的成员变量 1.const修饰类的非静态成员时必须在构造函数初始化列表上初始化: 在构造函数内会提示表达式必须是可修改的左值,因为在构造函数内并不是初始化,仅仅是赋值,而const类 ...

  2. C、C++中const的区别

    C语言中: 被const修饰的变量,仍然是变量.虽然不能用C语法给这个变量改变值,但他本质上还是变量. C编译器会给它分配空间. C中,const默认使用的是外部链接. C++中: 被const修饰的 ...

  3. JavaScript中const、var和let区别浅析

    在JavaScript中有三种声明变量的方式:var.let.const.下文给大家介绍js中三种定义变量的方式const, var, let的区别. 1.const定义的变量不可以修改,而且必须初始 ...

  4. C/C++中const的用法 分类: C/C++ 2015-07-05 00:43 85人阅读 评论(0) 收藏

    const是C语言的关键字,经C++进行扩充,变得功能强大,用法复杂.const用于定义一个常变量(只读变量),当const与指针,引用,函数等结合起来使用时,情况会变得复杂的多.下面将从五个方面总结 ...

  5. C++中const 的各种用法

    C++中const 关键字的用法 const修饰变量 const 主要用于把一个对象转换成一个常量,例如: ; size = ; // error: assignment of read-only v ...

  6. (转) C/C++中const关键字详解

    文章转自  http://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777416.html 为什么使用const?采用符号常量写出的代码更容易维 ...

  7. 实例讲述PHP面向对象的特性;;;php中const与define的使用区别

    php中const与define的使用区别 1.const:类成员变量定义,一旦定义且不能改变其值. define:定义全局常量,在任何地方都可以访问.2.define:不能在类中定义,而const可 ...

  8. C++中const简介及用法

    1.const简介 C++中的const关键字的用法非常灵活,而使用const将大大改善程序的健壮性,本人根据各方面查到的资料进行总结如下,期望对朋友们有所帮助. Const 是C++中常用的类型修饰 ...

  9. C/C++ 中 const 修饰符用法总结

    C/C++ 中 const 修饰符用法总结 在这篇文章中,我总结了一些C/C++语言中的 const 修饰符的常见用法,供大家参考. const 的用法,也是技术性面试中常见的基础问题,希望能够帮大家 ...

随机推荐

  1. [Android]Adb connection Error:远程主机强迫关闭了一个现有的连接

    目前,针对Android手机研发的刷机软件和手机助手软件,都会调用Google开发的adb工具与手机进行通信. 有的刷机软件或者手机助手软件,在系统里会常驻一个服务,独占adb的端口(5037),这样 ...

  2. FMDB给表添加新的字段

    1.首先判断添加的字段是否存在,如果不存在就添加. 2.代码演示: (1)判断是否存在,判断之前先导入头文件确保可以调用FMDB的api(#import “FMDatabaseAdditions.h” ...

  3. Error:java: Compilation failed: internal java compiler error(转)

    set中java complier 设置的问题  ,项目中有人用jdk1.6  有人用jdk1.7  版本不一样  会一起这个错误 进行如下操作: 原文链接:http://blog.csdn.net/ ...

  4. 辛星笔记——VIM学习篇(推荐阅读)

    转载自:辛星和您一起学vim脚本第一节 如本文侵犯了您的版权,请联系windeal12@qq.com 这几天在网上看了辛星的一些vim教程博文,觉得很有收获,也很实用,适合入门,所以转载其中一篇留个网 ...

  5. vue.js 源代码学习笔记 ----- core array.js

    /* * not type checking this file because flow doesn't play well with * dynamically accessing methods ...

  6. JQ上传预览+存数据库

    因为之前老师讲的方法有不少BUG 现在经过完善已经修复 之前老是讲的方法是每一张都会被传到后台文件夹里面去 导致在预览过程中如果刷新页面 那么预览的图片不能从后台文件夹中删除  这个方法实现在本地预览 ...

  7. d3.js(v5.7)完整地画一个柱状图

    一.首先定义画布大小以及绘画区域的位置(总不能顶着屏幕边沿画吧) 代码: 图示: 二.横.纵向坐标轴 代码: 图示: 三.添加矩形个文本以及上色 图示:

  8. Device-Dependent Bitmaps (DDB) 设备相关位图

    设备相关的位图(DDB)使用单一结构BITMAP结构描述.该结构的成员指定矩形区域的宽度和高度,以像素为单位;将条目从设备调色板映射到像素的数组的宽度;以及器件的颜色格式,在每个像素的颜色平面和位数方 ...

  9. Lua基础---迭代器

    官方的文档说: 迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址 在Lua中迭代器是一种支持指针类型的结构,它可以遍历集合的每 ...

  10. EasyDarwin如何支持点播和RTMP/HLS直播?EasyDSS!

    2017年很长很长一段时间没有更新EasyDarwin开源项目了,虽然心里有很多EasyDarwin功能扩展的计划:比如同步录像.同步RTMP/HLS直播输出.拉模式转发优化.Onvif接入.GB28 ...