C/C++常量与变量的使用
常量:
定义时设置初值,编译器将其放置一个只读的内存区域。
用处:
- 程序多出使用一个常数值时,可以用常量代替。C采用define来进行定义,eg:#define PI 3.1415;而C++采用const来定义,eg:const float PI = 3.14159;
- 在函数体中,不需要修改的参数定义常量。
变量:
分配内存,一般需要进行初始化,变量定义分为如下两步:
- 变量声明:告知编译器变量的名称和数据类型
- 变量定义:为变量分配存储区域
C++中的左值和右值
- 左值:C语言中用出现在等号左边的为左值,C++中广泛认为:可以取地址的,有名字的就是左值
- 右值:C语言中用出现在等号右边的为右值,C++中广泛认为:可以不能取地址的,有没有名字的就是右值。右值由两个概念组成:将亡值和纯右值。纯右值:临时变量(eg:非引用函数的return语句、一些运算表达式)、不跟对象关联的字面量值(eg:2,’a’,true)将忙值:将要被移动对象(移动为他用)
左值引用和右值引用
无论声明左值引用还是右值引用,都为引用类型,故必须立即初始化。
- 左值引用:C具有名字的变量名、能去地址的操作变量的别名
- 右值引用:C匿名(没有名字)的变量的别名 T&& a=ReturnRvalue();有一个临时变量来存放ReturnRvalue()返回的值,T a=ReturnRvalue()此形式赋值一结束,则临时变量的生存期也结束。而T&& a=ReturnRvalue()延长了临时变量的生存期,(优点:减少了a对象的构造以及临时对象的析构)
注意:右值的引用是不能绑定左值的,以及左值的引用是不能绑定右值的。
int c;
int && d=c;
常量左值引用是一个万能的引用,它能够接受非常量左值、常量左值、右值来对其初始化。
const T& f=ReturnRvalue()
变量存储四种类型
- extern:声明而不定义变量,用于不同文件之间的通信(与全局变量之间通信)全局变量:函数体之外的变量,有着全局作用域。
- static: 静态变量,全局生存期(即从文件执行开始到文件执行结束),局部变量:函数体之内的变量,有着局部作用域。
- register: 变量放置在CPU中的寄存器中,只能作用于局部变量。常用于频繁访问的局部变量。
- auto:动态变量,即根据表达式的结果类型来确定变量的类型,此变量的内存为动态分配(即随着程序运行而分配内存)
代码存储方式
- 代码区:存放类成员函数、其它函数代码等。
- 全局数据区:存放全局变量、静态变量、常量等。
- 栈区:(随时更新数据),存放局部变量、函数参数、返回数据等。
- 堆区:自由存储区
数据存储方式
- 栈存储:存储空间小,生命周期短的数据,eg:局部变量、函数参数等。
- 堆存储:存储占用空间大,生命周期长的数据,eg:静态变量,全局变量等。
- new操作:在堆中开辟一个空间,使变量存储在堆中。故手动分配内存需要手动释放内存,否则造成内存泄漏。释放对象:delete p;释放对象数组:delete[] p。小窍门:释放内存时,把该指针设为nullptr,可以避免访问删除的指针错误。针对经常忘记释放内存,引入了智能指针:unique_ptr、shared_ptr以及weak_ptr。
常见错误
- 函数返回值为地址或引用时:不要将局部自动变量返回过来,由于生存期引起的一系列问题
- 实数之间的比较是否相等:不要用:==/!=。由于计算机中的实数(除开整数)都是按浮点数形式存储的,一般比较相等,定义一个精度,两者之差的绝对值小于此精度,则表示相等。
- 实数之间相加:float、double存储实数都是有有效数字的限制的。一般float有效数字为6~7位,eg:float f=86041238.78,则此变量在计算机存储之后显示结果为:86041240.00。从而不要用一个较大的数加上一个非常小的数,会造成结果不变。
深入const常量
常量的值可以通过指针来修改,但输出值时const在C和C++中是有区别的(内存中的值实质修改了)。
- C语言中,const修饰的变量为只读变量(会分配存储空间),访问时,会去它的地址读取内容。
- C++语言中,const修饰的变量表示常量,而这种修饰也分内置类型和非内置类型。内置类型:const做的是一个替换工作(不会分配存储空间,只是把他们保存在符号表中,使得它们成为编译期间的常量,没有存储和读内存的操作,使得它的效率非常高);非内置类型:会去它的地址读取内容
const int i=10;
int *p=(int *)(&i);
*p=20;
printf("i=%d *p=%d \n",i,*p);
C++中输出结果为10,20,i为10的原因:并没有去读内存中的内容;而C中输出结果为20,20。
volatile修饰的变量
volatile提醒编译器它后面所定义的变量随时都有可能改变。如果没有volatile关键字,则编译器可能优化读取和存储。
- 告诉编译器不能做任何优化
- 用volatile 定义的变量会在程序外被改变,每次都必须从内存中读取,而不能重复使用放在cache或寄存器中的备份。