const 、#define、constexpr
-
#define
define是C语言中的宏,拿来进行文本替换。如,
#define number 100
,在程序的预处理阶段,会将程序中,所有出现number的地方,使用100来进行替换。缺点:这个是在预处理阶段就完成的文本替换,与编译阶段是不同阶段,不会进行类型检查。
-
const
i. 编译时的值替代 --- const限定的变量,在编译阶段,编译器会进行常量折叠的一些优化操作。所谓常量折叠,就是化简常量表达式,即当遇到const限定的变量时,会用相应的值进行替换,使其变为一个立即数。当然,不是所有情况都能进行常量折叠,如对常量进行取地址操作。
eg:int main(){ const int a = 2; int *ptr = const_cast<int *>(&a);//改变常量属性 *ptr = 5; printf("a = %d\n" ,a);//此处进行常量折叠 printf("&a = %x\n", &a); printf("*ptr = %d\n",*ptr); printf("ptr = %x\n",ptr); printf("a = %d\n" ,a);////此处进行常量折叠 printf("&a = %x\n", &a); return 0; }
运行结果:
分析:
按&a,ptr,&a顺序输出的地址都不变,即说明处理的一直都是常量区的那块内存。当强行改变常量属性,对内存进行写操作时,再次输出常量 a 的值,还是原来的 2 ,而不是 3 ,此处说明,这里已经是被编译器优化了,编译期进行了值替代。与volatile一起使用时,编译器不会做常量折叠等优化 volatile不允许编译器对数据做出假定,必须要重重读数据,而不是进行优化 eg: int main(){ volatile const int a = 1; printf("change before: %d", a); int *ptr = const_cast<int*>(&a); *ptr = 4; printf("change after: %d", a); return 0; }
运行结果
ii. 运行时常量 -- 如果能确定一个变量,在其生存期之间,一直保持值不变,也可以用const来限定。
eg:int main(){ const int a = cin.get();//get()取单个字符 cout <<a; return 0; }
运行结果:
分析:
说明可以是运行时的const变量
iii. const可以用来限定集合,如数组。编译器在编译阶段,不能把集合放在它的符号表中,而是必须分配内存,表明“不能改变的一块存储”。在编译时期,集合中的值,是不可使用的。
(所谓分配内存,是指静态分配空间,是指在编译时期,就可以计算出其大小的,将这些信息写到可执行文件一些区,在执行时,不必执行额外的代码来为其分配内存,而是程序直接将这些数据,与物理地址建立映射关系。如,静态变量与全局变量)
eg:int main(){ const int array[3]={0,1,2}; float temp[array[0]];//此种行为编译器会报错 //因为array数组中的值在编译时不能使用,是运行时的存储值 }
Tips:
C++中const与C中的const的区别:
const默认内部连接,作用域仅在本文件,而C中,默认外部连接。 -
constexpr
constexpr是C++11提出的,目的是让编译器在编译时期判断表达式是否是常量表达式,若是,则可以在编译时就进行一些优化工作,提高效率。如常量折叠等;若不是,即报错。
i. constexpr修饰变量constexpr int a = 1;//相当于const限定的编译时可确定值的用法 constexpr int b = cin.get();//编译器报错,此为运行时确定的存储
ii. constexpr修饰函数
当constexpr修饰的函数返回值用作常量表达式时,如用作数组声明中的维度,要求该函数中,定义的所有变量均为编译时可决定的值,且不能进行取地址等复杂操作,并且要求传入的参数也必须是常量表达式。
若返回结果不用做常量表达式,即权当普通函数使用。
eg:返回值作为常量表达式使用:程序可编译执行 constexpr int test(int a) { int b = 2;//个人猜测当返回值用作常量表达式时,b会当做constexpr变量使用 //printf("b = %d, &b = %x\n", b, &b);//若注释去掉,编译器报错,constexpr函数无法生成常数表达式 return b; } int main() { int a = 1; int b = 1; //int array[test(a+b)]; //若注释去掉,编译器报错,变量a的值不可用作常量 int array[test(1)]; return 0; }
返回值不做常量表达式使用: 传参不做要求(在以上程序加test(a+b);进行测试,通过) 返回值是否为常数表达式不做要求
- 一个constexpr修饰的指针的初始值必须是nullptr或者0,或者是存储于某个固定地址中的对象(如函数体外定义的变量或函数体内的static变量),且constexpr修饰的是指针,而不是指针指向的对象。(即xxx *const ptr;)