【C++关键字】const 用法 深入 分析。

【不想探究深入机制的可以直接看第7部分】

1.  最初的用途: 代替预编译指令 #define,可以缩小程序size,从而节省载入到内存运行时的内存开销。

举例:(网上的例子多半都是CV党,不懂的人看了会更加不懂,更怕C++)百度百科给的例子就是错的,或者说不恰当的,解释的方法不对。

比如:

/--------------------------------------------------------define--------------------------------------------------------------/

#define tbwood 12

int a=tbwood;

int b=tbwood;

编译后为(汇编语句:mov语句是把 后面一个数(或者变量的值) 移动到 前面一个变量中):

mov a 12;  //立即数寻址

mov b 12;

/--------------------------------------------------------const--------------------------------------------------------------/

再看const

const int tbwood=12;

int a=tbwood;

int b=tbwood;

汇编后代码为:

mov a [tbwood] //直接寻址,把tbwood对应的内存块数据移动到a变量中

mov b [tbwood]


那么从目前来看,我们发现const方式比define 甚至还要差一点,因为两种代码编译后的大小是一模一样的,都是 2*sizeof(mov instruction)+4*sizeof(int),但是const方式是直接寻址,比立即寻址稍微慢了点。 这就是“空间换时间”的好例子。

但是如果我们想,假如现在我们定义的常量是 一个长整数,或者是很长的字符串 长度 >sizeof(int)   字节,设为 M (M>sizeof(int) )

那么这时候

define 方式下编译的代码 长度为: 2*sizeof(mov instruction)+2*sizeof(int)+ 2*M 

const   方式下编译的代码长度为: 2*sizeof(mov instruction)+4*sizeof(int)

这时候我们发现,const比define节省了  2*(M-sizeof(int)) 的字节空间。

上面我们只是使用了两次,如果程序中常量出现了K次,那么节省的空间为:

K*(M-sizeof(int))    (M为常量的字节数长度)

如果长度大于4字节(整数的长度 -32位程序)的常量在一个程序中很常见,那么可以节省很大的空间。

第一点与 define的比较总结下就是: 节省程序空间。


2. 以上说的是const的最初设计的目的。其实上述特点普通变量就可以拥有, 归结一下就是: 变量是运行时分配,立即数是编译时分配的。const 方法只不过是用了个变量名而已。

比如:

int tbwood =12;

int a=tbwood;

int b=tbwood;

那为什么不直接用int 而用const int 呢。或者说,后者比前者的优势在哪。

其实没啥神奇的,就是const int 定义的变量是不可修改的,而int直接定义会被修改。减小程序员出错的概率,或者软件运行时被入侵,导致安全问题。

如果const 定义的变量在运行时被入侵或者篡改,那么会导致错误,或者无法修改,因为常量放在常量数据段中,有写保护。

所以第二点与普通变量方式的比较总结下就是: 更加安全。


3. const 变量 作为函数参数的好处: 防止在函数内部不小心修改到该参数,这是编程时的好处。


4. const int * p 和 int * const p 的区别。

(1)首先 const int *p 和  int const * p是一样的。都是表示  *p 是常量,p是指向常量的指针变量。 这样的情况,尝试 用 *p=2 就是错误的。

(2)int *const p 和 const (int*) p 是一样的。但是后者在C/C++编译器里是不支持的,所以一般这么写: typedef int* ptbint; const ptbint p。 这种写法和int *const p是一样的,          语义为定义一个静态的指针变量p。这种情况下,尝试 p++ 或者p=2 是错误的。


5. const 引用

 (1)严格的const引用 是指: const int a=10;  int &b=a;  b是const引用。【Cited from C++ Primer】

 (2)这里我们说的const引用是:

  那么我们这里的const 引用具有哪些特点呢?

  int a=10; 

  const int & b=a;

 很简单: a 和 b 指向的内存单元(现在存放的是10)只能由a来修改,b不能修改。不能使用 b=11;

 那么我们会有一个疑问,程序运行的时候,计算机怎么知道 这个地址 是const 还是普通呢,同一个地址,而且是普通变量a的,总不能一个变量同时放在常量段和变量段吧。(答案是放在变量段,因为这个引用指向的是一个普通变量,如果是(1)的情况,那么a b都放在常量段,虽然用b去修改这个值编译不会出错,但是运行时仍然不会修改)

其实是想多了,这个错误是在编译的时候就会提示了,所以压根不会编译成功。所以这种引用方式的作用还是:“防止程序员编码出错”。

那么这种const应该用在哪里呢?

一般用在函数的参数中,比如:

void func(const int &a)

{

  .....

}

这样的好处是,(a)防止程序员在写实现implement的时候不小心“显式的(就是很明显的)”修改到a。如果修改了,那么编译不会通过。

还有哪些好处呢?(b)const int & a 具有 int&a的特性,传名方式,我们知道,好处就是快、省,不需要开辟临时参数。

       (c)也有些说法说可以完全保护a不被修改,下个部分总结的时候我会否认这个好处。



6. 总结:

const 在编译时的作用有: 

(1)节省程序大小。

(2)防止用户显式的修改变量(换句话说,防止程序员犯简单错误)。

const 在运行时的作用有:

(3)答:对于直接定义的const 变量,运行时也会受保护。比如const int a=10;那么 不管你通过什么方式修改,a都不会变。

这里需要注意 使用 const int  & a 时,

如果直接用立即数: const int & a=40; 那么 a不会放到常量区,而是普通变量区。

如果使用变量赋值: const int & a=b; 那么如果b放在常量区,那么a也是常量区地址,如果b放在普通区,那么a也是普通数据区地址。

如果把立即数40也看成是“非常量区地址”(在代码区),那么可以得到,如果右值是常量区地址,那么该变量也是常量区地址,否则是普通数据区地址。

我的中文名是 王小二,英文名是 Dr Wang,那么如果王小二是博士, Dr Wang肯定也是博士。

其实这个和   int & a = 右值     一个道理。

之所以加上const 是为了防止下面用户不小心修改a。但是这种“防止修改”是十分弱,还是要看右值。

如果右值 是常量区,那么代码中不管怎么修改a都不会变。

比如:

const int b=40;

const int & a=b;

*((int*)&a)=10;

这时候a的值还是40。因为a在常量区。


如果右值不在常量区,在普通数据区或者代码区立即数,那么a还是可以改变的。

比如:

const int & a=40;

*((int*)&a)=10;

这时候a的值就是10了。修改了,很轻松吧。

这个例子里的右值其实就是 func(const int & a ) 里的实际参数

一个道理,所以最终能不能“彻底的”保护这个变量,还是要看传递进来的参数地址是在常量区 还是在非常量区。

另一个知识点:

(4)const int & a  还可以用来防止程序员犯语法错误【C++ primer 中文版上写的含糊其辞,但是我觉得原版就是下面这个意思】

比如:

double a=3.14;

int const & b=a; //或者去掉const 也一样

这个时候 a 和b 会存放在不同的地址内存中。

编译的汇编伪代码为:

mov temp, a;

get interger part from temp;

set b to point to temp;


C++程序员如果直接修改b,那么会被提示错误。

而不加const 的时候,编译会通过,给程序员带来十分困惑的烦恼。




7. 关于const,作为一个合格的C++程序员,需要记住什么。

说实在的,要把这么多细节记住,除非是不想学其他东西了,人的大脑记忆量是有限的。

关于const 我们只要记住:

(1)那些被定义成const的变量 都会被放在 常量区(const type & a 不算),从而无法修改,就算编译通过,也无法修改。

(2) const type a 的好处是节省程序空间,防止用户修改。

(3)const type& a 的好处是:

            (a)  防止用户显式的修改a,用在函数形参时还可以提高速度,避免创建内存。

            (b)  如果把 不同类型的右值rv 赋值给 a, 那么a会另外分配地址,const的作用只是为了提醒程序员  不要尝试用a来改变rv。

(4)还有就是 type * const a 和 const type * a的区别:前者定义一个静态指针变量,其指向的地址不可变;后者是定义了一个指针,指向一个不可变的内存数据。内存分配如图:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值