《C++Primer》第二章——变量和基本类型

前言:上学期大致浏览过一遍《c++ primer》这本书,确实很厚,对c++有了一个大致的概念后,这学期打算将《c++primer》和《effective c++》结合者一起看,通过边看边通过写博客的形式进行总结归纳,以便以后更好的复习和理解。

第二章:变量和基本类型

2.1 基本内置类型

1.切勿混用带符号类型和无符号类型,比如:当一个算数表达式中既有无符号数又有int值时,那个int值会被转换成无符号数
2.字面值常量:只有内置类型有字面值,类类型不存在字面值
1)整型和浮点型字面值:整型字面值具体数据类型由它的值和符号决定,默认情况下,十进制字面值是带符号数,八进制和十六进制字面值既可能是带符号的也可能是无符号的;默然情况下,浮点型字面值是double类型。
2)字符和字符串字面值:字符串字面值的类型实际上是由常量字符构成的数组,编译器在每个字符串结尾处添加一个空字符’\0’,因此字符串字面值的实际长度比其内容多1。
3)转义序列:转义序列均以反斜线\为开始。
4)指定字面值的类型:通过添加前缀和后缀(p 37),可以改变整型、浮点型、字符型字面值的默认类型。
5)布尔字面值和指针字面值:true和false是布尔类型的字面值;nullptr是指针字面值,它可以被转换成任意其他指针类型。

2.2 变量

1.变量:提供一个具名的、可供程序操作的存储空间,每个变量都有其数据类型,数据类型决定变量所占内存空间的大小和布局方式等
对象:是具有某种数据类型的内存空间,不严格区分是否为内置类型,不区分是否命名或是否只读
在c++中,认为“变量”和“对象”可以互换使用
2.初始化:当对象创建时获得一个特定的值,则称该对象被初始化了
初始化和赋值是完全不同的操作,初始化的含义是创建变量时赋予的初始值,而赋值的含义是将当前对象的值擦除而创建一个新值替代
3.默认初始化:若定义变量时没有指定初值,则变量被默认初始化。
内置类型的变量未被显示初始化,它的值由定义的位置决定:定义于任何函数体之外的变量被初始化为0;定义于函数体内的内置类型的对象若未初始化,则其值未定义
类的对象若没有显示初始化,则其值由类确定
4.变量声明与定义:c++支持分离式编译,因此需将声明和定义区分开来
声明:使得名字为程序所知,一个文件若想用别处定义的名字就必须包含那个名字的声明,eg: extern int i; //声明而非定义
定义:负责创建与名字关联的实体,eg: int j; //声明并定义
但注意,任何包含了显示初始化的声明即变成了定义,eg: extern double pi = 3.14; //定义而非声明
变量只能被定义一次,但是可以被多次声明
5.变量命名规范:
1)标识符要能体现实际含义
2) 变量名一般用小写字母
3)用户自定义的类名一般以大写字母开通
4)若标识符由多个单词组成,则单词应有明显区分
6.名字的作用域:不论程序位于什么位置,使用到的每个名字都指向特定的实体,变量、函数、类型等,但一个名字出现在程序的不同位置可能指向不同的实体
c++中大多数作用域以花括号分隔,同一个名字在不同作用域中可能指向不同的实体,名字的有效区域始于名字的声明语句,以声明语句所在作用域末端为结束
c++允许内层作用域中重新定义外层作用域中已有的名字,但若函数有可能用到某全局变量,则不宜在定义一个同名的局部变量

2.3 复合类型

1.复合类型:指基于其他类型定义的类型,引用和指针就是复合类型。
2.引用:为对象起了另外一个名字,在定义引用时,程序将引用和它的初始值绑定在一起,而不是将初始值拷贝给引用,一旦初始化完成,引用和它的初始值对象就一直绑定在一起,而无法重新绑定到另外一个对象,因此引用必须初始化。
1)引用本身并非对象,因此不能定义引用的引用
2)除特殊情况,引用的类型要和与之绑定的对象一定要严格匹配,而且引用只能绑定在对象上,而不能绑定在字面值上
3.指针:指向另外一种类型的复合类型。指针同引用一样,以实现了对其他对象的间接访问,但1)指针本身就是一个对象,可以对指针赋值和拷贝,可以更改指向的对象,2)指针无须定义时赋初值,与其他内置类型一样,在块作用域内定义的指针若未初始化则拥有一个不确定的值。
空指针:不指向任何对象,在新标准下,最好使用 nullptr 字面值定义空指针,而非预定义变量NULL
void指针:一种特殊指针类型,可用于存放任意对象的地址,但并不知道地址中存放的是什么类型的对象,而且不能直接操作void所指对象,因为不知道对象是什么类型也就无法确定可以对这个对象做什么操作。
总的来说,void*的视角看内存空间仅仅是内存空间,无法方位内存空间中所存的对象。
4.定义多个变量:

int* p1, p2;		//p1为指向int的指针,而p2是int类型,*仅仅修饰了p1而已
int *p1,*p2;	    //将修饰符和变量标识符写在一起,强调变量具有复合类型

5.指向指针的引用:引用本身不是对象,因此不能定义指向引用的指针,但是指针是对象,因此存在对指针的引用

int i = 42;
int *p;
int *&r = p;	//从右向左阅读r,&表示r是一个引用,int *说明r引用的是一个int类型指针
r = &i;		//因为r是对指针p的引用,因此这条语句表明令p指向i
*r = 0;		//将i的值改为0

面对一条比较复杂的指针或引用的声明语句时,从右向左阅读有助于弄清真实含义

2.4 const限定符

1.const:const对象一旦创建后,就不能再改变
默认状态下,const对象仅在文件内有效,当多个文件出现同名const变量时,等同于在不同文件中分别定义独立的变量,若想在多个文件之间共享const对象,则无论是定义还是声明都要添加extern关键字(一般变量,定义时无需添加关键字,声明时添加即可)
2.const的引用:将引用绑定到const对象上,称为对常量的引用,不能修改它所绑定的对象
在2.3中的引用曾说过引用的类型必须与其所引用对象的类型一致,但这里有一个意外:初始化常量引用时允许任意表达式作为初始值
因此允许常量引用绑定非常量的对象、字面值或一般表达式

int i = 42;
const int &r1 = i;		//正确
const int &r2 = 42;		//正确
const int &r3 = r1 * 2;	//正确

double dval = 3.14;
const int &ri = dval;	//正确

对const的引用可能引用非const对象,但这仅对引用可参与的操作作了限定,对于引用的对象本身是不是常量未作限定,对象可以是非常量

int i = 42;
int &r1 = i;		//引用r1绑定对象i
const int &r2 = i;	//r2也绑定对象i,但不允许通过r2修改i的值

3.顶层const/底层const
指针本身是对象,并且指向另一个对象,因此,指针本身是不是常量以及指针所指是不是常量是两个相互独立的问题
顶层const:表示指针本身是个常量
底层const:表示指针所指对象是个常量
更一般的推广,
顶层const:表示任意的对象是常量,对任意数据类型都使用,算数类型、类、指针等
底层const:与指针和引用等符合类型的基本类型部分有关

int i = 0;
int *const p1 = &i;			//顶层const,p1的值不能修改
const int ci = 42;			//顶层const,ci的值不能修改
const int *p2 = &ci;		//底层const,p2的值可以修改,但p2所指向的值ci不能修改
const int *const p3 = p2;	//靠右的cosnt是顶层const,靠左的是底层cosnt
const int &r = ci;			//不能通过r修改ci,用于声明引用的const都是底层const,因为引用不是对象,不存在顶层const

4.常量表达式:1)值不会发生改变,2)编译过程就能得到计算结果的表达式
一个对象(或表达式)是不是常量表达式由数据类型初始值共同决定

const int max_files = 20;			//max_files是常量表达式
const int limit = max_files + 1;	//limit是常量表达式
int staff_size = 27;				//staff_size不是常量表达式,不满足第一个条件,即值不会发生改变
const int sz = get_size();			//sz不是常量表达式,因为具体值运行时才能得到,不满足第二个条件,即编译过程就能得到结果
  1. constexpr变量:声明为constexpr的变量一定是一个常量,而且必须用常量表达式初始化
    一般来说,若你认定变量是一个常量表达式,那就把它声明为constexpr类型
    6.字面值类型:常量表达式需在编译时计算,因此对声明constexpr时用到的类型必须限制,这些类型比较简单、易得到,称为字面值类型,算数类型、引用和指针都属于字面值类型;自定的类、IO库、string类型则不属于字面值。
    7.指针和constexpr:若constexpr声明中定义了一个指针,限定符constexpr仅对指针有效,与指针所指对象无关
    constexpr指针的初始值必须为nullptr、0或者存储于固定地址的对象
    注:函数体内定义的变量一般不存放于固定地址中,因此constexpr指针不能指向这样的变量,而定义于函数体之外的对象地址固定不变,能用来初始化constexpr指针
const int *p = nullptr;		//p是一个指向整型常量的指针,底层const
constexpr int *q = nullptr;	//q是一个指向整数的常量指针,顶层cosnt

//i和j都必须定义在函数体之外,即有固定地址
constexpr const int *p = &i;	//p是常量指针,指向整型常量i,相当于既有顶层const又有底层const
constexpr int *pi = &j;		//p1是常量指针,指向整数j

2.5 处理类型

1.类型别名
1)使用关键字typedef
2)使用别名声明,关键字using,其后紧跟别名和等号,作用是把等号左侧的名字规定称右侧类型的别名

typedef double wages;	//wages是double的同义词
typedef wages base, *p;	//base是double的同义词,p是double *的同义词

using SI = Sales_item;	//SI是Sales_item的同义词

typedef char *pstring;
const pstring cstr = 0;	//pstring是指向char的指针,基本数据类型是指针,则const pstring即表示指向char的常量指针
const char *cstr = 0;	//const char是基本数据类型,cstr是指向const char的指针,类型别名替换后两种声明含义截然不同

2.auto类型说明符:让编译器去分析表达式所属的类型,因此,auto定义的变量一定要有初值。
1)编译器以引用对象类型作为auto的类型
2)auto一般会忽略掉顶层const,而底层const则会保留下来
3)一条语句定义多个变量时,切记&和*仅属于某个声明符,而非基本数据类型,因此初始值一定要是同一类型

//1)
int i = 0, &r = i;
auto a = r;		//a是一个整数(r是i的别名,而i是一个整数)
//2)
const int ci = i, &cr = ci;
auto b = ci;	//ci的顶层const被忽略了,因此b是一个整数
auto c = cr;	//cr是ci的别名,ci本身的顶层const被忽略,因此c是一个整数
auto e = &ci;	//对常数对象取址,很明显是个底层const(指向的是个cosnt,而不是本身是个const),因此e是个指向整型常量的指针
//3)
auto k = ci, &l = i;	//k是整数,l是整型引用

3. decltype类型指示符:选择并返回操作数的数据类型,编译器分析表达式并得到其类型但并不实际计算表达式的值
1)decltype返回变量的类型(包括顶层const和引用在内,这与auto很不同)
2)若decltype使用的表达式不是一个变量,则返回表达式结果对应的类型

//1)
const int ci = 0, &cj = ci;
decltype(ci) x = 0;		//x的类型是const int
decltype(cj) y = x;		//y的类型是const int&,y绑定到x
2int i = 42, *p = &i, &r = i;
decltype(r + 0) b;		//加法结果为int,因此b是一个未初始化的int
decltype(*p) c;			//c是int&,必须初始化,解引用指针可得到指针所指对象并赋值,因此c也应具有这种性质,所以是引用

**//decltype((variable))的结果永远是引用**
//d是一个int&,必须初始化,加上一层或多层括号,编译器会认为是一个表达式,而变量是一种可作为赋值语句左值的特殊表达式,d也应具有该性质
decltype((i)) d;								    
decltype(i) e;			//e是一个未初始化的int
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值