在C++中,const 的含义并没有改变,只是对细节进行了一些调整,以下是最主要的两点。

一.C++中的 const 更像编译阶段的 #define

先来看下面的两条语句:

const int m = ;
int n = m;

我们知道,变量是要占用内存的,即使被 const 修饰也不例外。m、n 两个变量占用不同的内存,int n = m;表示将 m 的值赋给 n,这个赋值的过程在C和C++中是有区别的。

在C语言中,编译器会先到 m 所在的内存取出一份数据,再将这份数据赋给 n;而在C++中,编译器会直接将 10 赋给 n,没有读取内存的过程,和int n = 10;的效果一样。C++ 中的常量更类似于#define命令,是一个值替换的过程,只不过#define是在预处理阶段替换,而常量是在编译阶段替换。

C++ 对 const 的处理少了读取内存的过程,优点是提高了程序执行效率,缺点是不能反映内存的变化,一旦 const 变量被修改,C++ 就不能取得最新的值。

有读者提出疑问,const 变量不是禁止被修改吗?对,这种说法没错!不过这只是语法层面上的限制,通过指针仍然可以修改。下面的代码演示了如何通过指针修改 const 变量:

#include <stdio.h>

int main(){
const int n = ;
int *p = (int*)&n; //必须强制类型转换
*p = ; //修改const变量的值
printf("%d\n", n);
return ;
}

注意&n得到的指针的类型是const int *,必须强制转换为int *后才能赋给 p,否则类型是不兼容的。

将代码放到.c文件中,以C语言的方式编译,运行结果为99。再将代码放到.cpp文件中,以C++的方式编译,运行结果就变成了10。这种差异正是由于C和C++对 const 的处理方式不同造成的。

在C语言中,使用 printf 输出 n 时会到内存中获取 n 的值,这个时候 n 所在内存中的数据已经被修改成了 99,所以输出结果也是 99。而在C++中,printf("%d\n", n);语句在编译时就将 n 的值替换成了 10,效果和printf("%d\n", 10);一样,不管 n 所在的内存如何变化,都不会影响输出结果。

当然,这种修改常量的变态代码在实际开发中基本不会出现,本例只是为了说明C和C++对 const 的处理方式的差异:C语言对 const 的处理和普通变量一样,会到内存中读取数据;C++ 对 const 的处理更像是编译时期的#define,是一个值替换的过程。

二.C++中全局 const 变量的可见范围是当前文件

我们知道,普通全局变量的作用域是当前文件,但是在其他文件中也是可见的,使用extern声明后就可以使用,下面是多文件编程的演示代码:

main.c

#include <stdio.h>

int n = ;
void func(); int main(){
func();
printf("main: %d\n", n);
return ;
}

module.c

#include <stdio.h>

extern int n;

void func(){
printf("module: %d\n", n);
}

不管是以C还是C++的方式编译,运行结果都是:

module: 10
main: 10

在C语言中,const 变量和普通变量一样,在其他源文件中也是可见的。修改代码段1,在 n 的定义前面加 const 限制,如下所示:

const int n = ;

修改后的代码仍然能够正确编译,运行结果和上面也是一样的。这说明C语言中的 const 变量在多文件编程时的表现和普通变量一样,除了不能修改,没有其他区别。

  但是如果按照C++的方式编译(将源文件后缀设置为.cpp),修改后的代码就是错误的。这是因为 C++ 对 const 的特性做了调整,C++ 规定,全局 const 变量的作用域仍然是当前文件,但是它在其他文件中是不可见的,这和添加了static关键字的效果类似。虽然在module.c中使用 extern 声明了变量 n,但是在链接时却找不到main.c中的 n。

由于 C++ 中全局 const 变量的可见范围仅限于当前源文件,所以可以将它放在头文件中,这样即使头文件被包含多次也不会出错,请看下面的例子。

module.h

const int n = ;
void func();

module.cpp

#include <stdio.h>
#include "module.h" void func(){
printf("module: %d\n", n);
}

main.cpp

#include <stdio.h>
#include "module.h" int main(){
func();
printf("main: %d\n", n);
return ;
}

运行结果:

module: 10
main: 10

C和C++中全局 const 变量的作用域相同,都是当前文件,不同的是它们的可见范围:C语言中 const 全局变量的可见范围是整个程序,在其他文件中使用 extern 声明后就可以使用;而C++中 const 全局变量的可见范围仅限于当前文件,在其他文件中不可见,所以它可以定义在头文件中,多次引入后也不会出错。

如果你使用的是 GCC,那么可以通过添加 extern 关键字来增大 C++ 全局 const 变量的可见范围,如下所示:

extern const int n = ;

这样 n 的可见范围就变成了整个程序,在其他文件中使用 extern 声明后就可以使用了。不过这种方式只适用于 GCC,不适用于 VS/VC。

总结:

C++ 中的 const 变量虽然也会占用内存,也能使用&获取得它的地址,但是在使用时却更像编译时期的#define#define也是值替换,可见范围也仅限于当前文件。

很多C++教程在对比 const 和 #define 的优缺点时提到,#define 定义的常量仅仅是字符串的替换,不会进行类型检查,而 const 定义的常量是有类型的,编译器会进行类型检查,相对来说比 #define 更安全,所以鼓励大家使用 const 代替 #define。

1.const的更多相关文章

  1. openssl 1.1.1 reference

    openssl 1.1.1 include/openssl aes.h: # define HEADER_AES_H aes.h: # define AES_ENCRYPT 1 aes.h: # de ...

  2. const,static,extern 简介

    const,static,extern 简介 一.const与宏的区别: const简介:之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量. 执行时刻:宏是预编 ...

  3. C++中的const

    一,C++中const的基本知识 1.C++中const的基本概念 1.const是定义常量的关键字,表示只读,不可以修改. 2.const在定义常量的时候必须要初始化,否则报错,因为常量无法修改,只 ...

  4. const extern static 终极指南

    const extern static 终极指南 不管是从事哪种语言的开发工作,const extern static 这三个关键字的用法和原理都是我们必须明白的.本文将对此做出非常详细的讲解. co ...

  5. const let,console.log('a',a)跟console.log('a'+a)的区别

    const 创建一个只读的常量 let块级作用域 const let重复赋值都会报错 console.log('a',a) a console.log('a'+a) a2 逗号的值会有空格:用加号的值 ...

  6. es6之let和const

    在javascript中,我们都知道使用var来声明变量.javascript是函数级作用域,函数内可以访问函数外的变量,函数外不能访问函数内的变量. 函数级作用域会导致一些问题就是某些代码块内的变量 ...

  7. construction const parameter问题 构造函数const引用参数问题

    工程在window下编译没有任何问题, 但是在linux(CentOS6)下编译就老是报错 C++ 编译器已升级到最新版 6.1.0 错误如下: In file included /bits/stl_ ...

  8. Error:const char* 类型的实参和LPCWSTR类型的形参不兼容的解决方法。

    在C++的Windows 应用程序中经常碰到这种情况. 解决方法: 加入如下转换函数: LPCWSTR stringToLPCWSTR(std::string orig) { size_t origs ...

  9. C#基础知识七之const和readonly关键字

    前言 不知道大家对const和readonly关键字两者的区别了解多少,如果你也不是很清楚的话,那就一起来探讨吧!探讨之前我们先来了解静态常量和动态常量. 静态常量 所谓静态常量就是在编译期间会对变量 ...

  10. const 与 readonly知多少

    原文地址: http://www.cnblogs.com/royenhome/archive/2010/05/22/1741592.html 尽管你写了很多年的C#的代码,但是可能当别人问到你cons ...

随机推荐

  1. 「Baltic2015」Network

    题目描述 原文 The government of Byteland has decided that it is time to connect their little country to th ...

  2. 【数论】【最大公约数】【枚举约数】CODEVS 1012 最大公约数和最小公倍数问题 2001年NOIP全国联赛普及组

    对于一对数(p,q),若它们的gcd为x0,lcm为y0, 则:p*q/x0=y0,即q=x0*y0/p, 由于p.q是正整数,所以p.q都必须是x0*y0的约数. 所以O(sqrt(x0*y0))地 ...

  3. linux-改变文件属主权限-chown

    http://www.cnblogs.com/peida/archive/2012/12/04/2800684.html chown将指定文件的拥有者改为指定的用户或组,用户可以是用户名或者用户ID: ...

  4. 设置MySQL数据库名不区分大小写

    Linux下的MySQL默认是区分表名大小写的,通过如下设置,可以让MySQL不区分表名大小写: 1.用root登录,修改 /etc/my.cnf: 2.在[mysqld]节点下,加入一行: lowe ...

  5. 设计工作-Axure

    1,百度百科 http://baike.baidu.com/view/3332366.htm?fromtitle=axure&fromid=5056136&type=syn 2,官方网 ...

  6. [Python爬虫] 之二十七:Selenium +phantomjs 利用 pyquery抓取今日头条视频

    一.介绍 本例子用Selenium +phantomjs爬取今天头条视频(http://www.tvhome.com/news/)的信息,输入给定关键字抓取图片信息. 给定关键字:视频:融合:电视 二 ...

  7. HA分布式集群一hadoop+zookeeper

    一:HA分布式配置的优势: 1,防止由于一台namenode挂掉,集群失败的情形 2,适合工业生产的需求 二:HA安装步骤: 1,安装虚拟机 1,型号:VMware_workstation_full_ ...

  8. Spark(三) -- Shark与SparkSQL

    首先介绍一下Shark的概念 Shark简单的说就是Spark上的Hive,其底层依赖于Hive引擎的 但是在Spark平台上,Shark的解析速度是Hive的几多倍 它就是Hive在Spark上的体 ...

  9. 数据挖掘算法之关联规则挖掘(二)FPGrowth算法

    之前介绍的apriori算法中因为存在许多的缺陷,例如进行大量的全表扫描和计算量巨大的自然连接,所以现在几乎已经不再使用 在mahout的算法库中使用的是PFP算法,该算法是FPGrowth算法的分布 ...

  10. maven的安装和eclipse的配置以及构建mahout基本项目

    maven介绍在此略过 下载地址: 点击打开链接 windows下载xxx-bin.zip文件 linux下载xxx-bin.tar.gz 这里以windows为例 下载完成直接解压到一个目录下 计算 ...