在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. Android中的MVC,MVP和MVVM

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha MVC,MVP,MVVM的区别 #MVC 软件可以分为三部分 视图(View):用户界面 ...

  2. 【博弈论】【SG函数】hdu1848 Fibonacci again and again

    某个状态的SG函数被定义为 除该状态能一步转移到的状态的SG值以外的最小非负整数. 有如下性质:从SG值为x的状态出发,可以转移到SG值为0,1,...,x-1的状态. 不论SG值增加与否,我们都可以 ...

  3. 【点分治】bzoj1468 Tree

    同poj1741. 换了个更快的姿势,不会重复统计然后再减掉什么的啦~ #include<cstdio> #include<algorithm> #include<cst ...

  4. 十. 图形界面(GUI)设计8.选择框和单选按钮

    选择框.单选框和单选按钮都是选择组件,选择组件有两种状态,一种是选中(on),另一种是未选中(off),它们提供一种简单的 “on/off”选择功能,让用户在一组选择项目中作选择. 选择框 选择框(J ...

  5. Windows 系统下设置Nodejs NPM全局路径和环境变量配置

    在nodejs的安装目录中找到node_modules\npm\.npmrc文件 修改如下即可: prefix = D:\tool\nodejs\node_globalcache = D:\tool\ ...

  6. golang设计模式-成员变量赋值

    常见golang的struct赋值有两种: 1)定义变量同时初始化 val := &Options{ UID:int(1), } 2)先定义变量,再赋值 val := new(Options) ...

  7. iOS 文字渐变色

    // 创建UILabel UILabel *label = [[UILabel alloc] init]; label.text = @"我是渐变的label"; [label s ...

  8. ntp流量放大攻击分析

    最近,听说挂在网络上的设备进行时间同步成功率低,YS需要架设自己的NTP服务器,这玩意第一时间能让人想到NTP流量放大攻击,这也是一种比较古老的攻击方式,检测了一下发现所使用的OS默认已经进行了加固, ...

  9. Word中插入带公式的Visio注意事项

    有时候发现,有的公式显示的间距特别大,那么在word中右键打开Visio,改好后,保存了,word里还是那样. 因为你需要吧改好的另存为原来的visio文件(名字.位置要一样,就是说替换原来的文件), ...

  10. android 电话薄先10位匹配,若是无法匹配,则换成7位匹配

    案例 1: 假设您保存的有:A:04165191666. B:5191666.  来电号码是:04165191666   由于是7位匹配,所以A和B都能够匹配到.可是最佳匹配还是A,最后显示A: 来电 ...