说起const、static、以及#define大家都知道,我一直以为我也是知道的,直达面试时被问到#define定义一个常量和const定义一个常量有什么不同,const定义的变量的储存位置,以及c与c++中static和const的时候。我发现当想说清楚这几个问题时,我发现自己对const和#define以及static中有些问题还是很模糊,我想这可能就是某位高手说的:“当你可以向别人清楚的解释某个问题时,你才算真正懂了这个问题”。于是乎,赶紧学习了下,理理思路,记下。
编译期常量和运行期常量
两者都是常量,它们的区别, 可以用是否具备内存空间来区分 。
编译期常量,
比如, #define MAX 128
这个 MAX 就是编译期常量, 没有对应的内存空间,
在预编译时候, 所有的 MAX 都被 128 这个值代替
运行期常量,
比如 const int x=128;
就是一个运行期常量, 分配内存空间,
但是其值不允许改变
还有 const int y = ram();//ram()是一个获取随机数的函数,这样y肯定是一个运行期常量,在编译时不知道它的值,而是在运行期获得,但不可改变。
这里我还想多说两句运行时常量,你想程序在经历过预编译阶段后,在编译阶段会生成一条条汇编指令并且生成符号表。 const int x=128这条代码的汇编指令就是 mov x 128,但此时这条代码并没有运行,那么符号x并不会知道128这个值,而且此时x也没有地址。只有当程序经历了链接阶段为符号分配地址,再生成可执行程序后。你运行了这个程序,当CPU执行了这两条指令,才会把128这个值写进x这个符号所在的地址。
内部链接属性和外部链接属性
内部链接和外部链接的区别就是能不能被本工程的外部文件所访问(从编译链接角度分析就是所生产符号时global还是local属性),在c中,const变量默认是外部链接,因此在不同的编译单元中如果有同名const变量(不符合c的强符号与弱符号时),会引发命名冲突,static修饰的变量或函数只能在本文件中可见,const修饰的也默认为内部链接属性,extern修饰为外部链接属性(和const同时修饰时,压过const修饰的属性)。但是c++的const变量默认是内部链接,如果想在外部引用就可以加extern修饰。
常量折叠
所谓“常量折叠”是C++中编译器的一种优化手段,对于上面的const int x = 128;编译器在链接时一般不为x分配内存空间,而是将它保存在目标文件的符号表中,这样导致的结果就是 const int x=128; 类似这样的定义, 产生的结构和 define 一样, 出现 x 的地方直接被 128 这个值代替了
我了解的,当你的程序中出现以下几种const定义的常量会分配内存。
1)当用extern修饰const变量将强制为其分配内存,因为extern表示采用外部链接,因此其必须有某个地址保存其值。
2)等取x的地址时,会强制分配内存。
C中的const
c中的const功能比较单一,较容易理解:
作用:被修饰的内容不可更改。
使用场合: 修饰变量,函数参数,返回值等。(c++中应用场合要丰富的多)
特点: 是运行时const,因此不能取代#define用于成为数组长度等需要编译时常量的情况。同时因为是运行时const,可以只定义而不初始化,而在运行时初始化。如 const int iConst;。 另外,在c中,const变量默认是外部链接,因此在不同的编译单元中如果有同名const变量,会引发命名冲突,编译时报错。
例如:
const int i=10;
int array[i] ;
这个i是运行时常量,它的不可改变是由编译器确定的,因此将 i 作为数组的长度;会出错,数组的长度必须是一个编译期常量(下面解释)。在C++中这样定义却是可以通过的,在C++中有一个常量折叠概念(后面会介绍)。
c++中的const:
(1)非类成员const
*在c++中,const变量(在这里涉及的const都不是类中的const,类中的const专门提出来记录)默认是内部连接的,因此在不同的编译单元中可以有同名的const 变量定义。
*是编译时常量,因此可以像#define一样使用,而且因为上面一点,可以在头文件中定义const变量,包含的不同的cpp文件(编译单元)中使用而不引起命名冲突。
*编译器默认不为const变量分配内存,除非和我上面说的一样:1. 使用 extern 申明, 2:程序中有引用const 变量的地址。
所以 函数参数或者返回值能使用 const & or const * 时,尽量使用const属性,增强程序健全性。
*c++中临时对象/内置变量默认具有const属性
可以使用下面的类型转换来解掉const属性(不安全的):
1: int * = (int *)pConst 2: int * = const_cast<int*> pConst
(2)类中的const
*类中的const与c语言中的const一样,只是运行时常量,不能作为数组维数使用,即不能取代#define。(但是可以在const前加static来达到#define的效果,后面会介绍。在类中使用下面两种方式取代#define: 1:static const… 2: enum{….}//enum 不占存储空间)
*类中的const 变量占用存储空间
*类中的const成员变量需要在构造函数初始化列表中初始化
*const 对象:在该对象生命周期内,必须保证没有任何成员变量被改变。const对象只能调用const成员函数。
*const成员函数: void fun() const … 不仅能被const对象调用,也能被非const对象调用,因此,如果确认一个任何成员函数不改变任何成员变量,应该习惯性将该函数定义成const类型。 如果const成员函数需要改变成员变量,有两种实现方式:
1 ,const_cast<class*> this强制取消this指针的const属性。
2:将被改变的成员变量定义成mutable:mutable int i; //应永远只使用第二种方法,让任何阅读程序的人都知道该变量可能被const函数改变。
*如果一个对象被定义成const,那么该const对象“可能”会被放入到ROM当中,这在嵌入式开发当中有时非常重要。。。。(不能有任何自定义的constructor 和destructor。它的基类或者成员对象不能有自定义的constructor和destructor,不能有任何mutable成员变量)
关于static const
本来还想总结一下 c与c++中的static和c++中的static const呢。但是没有时间了,后面再总结吧。