C++Primer第五版【学习笔记】——第二章 变量和类型

1.算术类型

C++标准中规定了每种类型的最小位数或精度,不同的编译器的有不同的实现。

表1-1 C++算术类型
TypeMeaningMinimum Size
boolbooleanNA
charcharacter8 bits
wchar_twide character16 bits
char16_tUnicode character16 bits
char32_tUnicode character32 bits
shortshort integer16 bits
intinteger16 bits
longlong integer32 bits
long longlong integer64 bits
floatsingle-precision floating-point6 significant digits
doubledouble-precision floating-point10 significant digits
long doubleextended-precison floating-point10 significant digits













 

 

 

 

如何决定选用哪种类型,可以参考一下几点:

  • 如果提前知道某个数值非负,则使用unsigned类型。虽然我们也可以不使用unsigned类型,但是,unsigned标识可以让我们清楚的知道该类型的取值范围。
  • 不要使用char或bool类型用于算术运算,而只在保存字符或者真值时使用。因为char类型在一些机器中是signed类型,而另一些则是unsigned类型。如果需要保存一个小的整形,则显示的声明是unsigned或者signed。
  • 使用double类型进行浮点运算。float类型往往精度不够,甚至比double运算的更慢。

注意:有unsigned类型和signed类型不要混用,signed类型一般会自动转换成unsigned类型,如果是个负值,这转换后的值会让运算产生意想不到的结果。

2.字面值

字面值(literal)的值即为其自身,而它的类型则由它自身的形式和值来确定。

例如:20是一个十进制的值,024是一个八进制的值,0x14是一个十六进制的值。

十进制字面值的类型由其大小取:int, long, long long中最小的可以容纳该值的类型;八进制和十六进制的值得类型从:int,unsigned int,long, unsigned long,long long ,unsigned long long中去最小的可以容纳该值的类型。也可以通过显式的指定前缀或者后缀来指定类型,见下图。


图2-1:字面值前后缀(来源自C++Primer第五版)

图中对于整形字面值的unsigned前缀u或U,编译器会根据具体的值得大小选择合适的类型,比如:unsigned int, unsigned long 等。

浮点数字面值的默认类型为double,也可以通过指定前后缀自定义。

3.变量

3.1变量初始化

什么是对象(Object)?对象是类型的一个实例,在内存中即为一块具有特定类型的数据块。该类型可能是内置类型,也可能是类类型。

【C++11】变量初始化方法:

  int a = 1;
  int b(1);
  int c = {1};
  int d{1};

其中第四行的int d{1};为C++11新标准,该初始化称为列表初始化。

定义在函数外的变量一般初始化为0,定义在函数体内的变量,如果不显示初始化,对于内置类型则该变量为未定义的,使用未定义的变量是危险的行为,对于类类型,初始化由该类定义。

【建议】尽可能的初始化每一个内置类型变量,因为使用未定义的变量所可能导致的错误是巨大的。

3.2声明和定义

声明使名字被程序知道。定义则创建相关的项。声明指定了变量的类型和名字。变量的定义是声明。除了指定类型和名字,还分配内存空间,也可以为其指定初值。

使用extern关键字来指定声明,如果extern语句中带有初始化,则会被重载为定义。

变量只能被定义一次,但是可以多次声明。

【Static Typing】C++是静态类型语言,意思是:编译器在编译的时候要进行类型检查。对象的类型限制了可以对其进行的操作,编译器会检测我们在对象上的操作是否符合限制。

3.3标识符

标准保留了一个名字集用于标准库。我们自定义的标识符不能以两个连续的下划线开头,不能以下划线开头且后面紧跟大写字母,定义在函数外的不能以下划线开头。

变量名定义惯例:

  • 尽量用变量表明意思
  • 变量名一般为小写——index,而不是Index或INDEX
  • 类名一般以大写字母开头
  • 包含多个单词的变量名需要区分开,比如:student_loan或studentLoan,而不使用studentloan

3.4名字范围

变量名声明的位置不同,则有不同的作用范围。变量的作用范围从声明的位置开始。具有相同变量名的内部变量会隐藏外部变量,左操作符为空的作用域操作符::使作用范围变为全局。

4.复合类型

一般的说,一条声明包括一个基础类型和紧跟的一个声明器(declarator)列表。每个声明器命名了一个变量并赋予该变量与基础类型相关的类型。

4.1引用

引用定义了对象的一个别名。通常,当我们初始化变量的时候,我们将初始化器(initializer)的值拷贝到要创建的对象。而在定义引用时,则是将其与初始化器绑定。一旦初始化后,该引用就会与其最初的对象绑定,没有办法重新绑定到另一个不同的对象。因此引用必须初始化

  int a = 1, &r = a;
  int &r1; // error

对引用的所以操作实际上是对引用绑定对象的操作。当使用引用作为初始化器时,实际上使用的是其绑定的对象。

引用绑定的对象不能是字面值,且类型必须匹配。

  int &r2 = 10; // error
  double d = 3.14;
  int &r3 = d; // error

4.2指针

指针是指向其他类型的复合类型。与引用一样,指针用来间接访问其他对象。与引用不同,指针自身也是一种类型,可以被赋值和拷贝;指针在其生命期内可以指向不同的对象。指针在定义的时候可以不用初始化。

由于引用不是对象(见3.1),所以指针不能指向一个引用。指针的类型与其指向对象的类型必须一致。

【指针的值】

  1. 指针可以指向一个对象;
  2. 指针可以指向刚刚超过一个对象后面的位置;
  3. 指针可以是NULL,不指向任何对象;
  4. 可以是不合法的,即不是前三种的情况。

如果一个指针没有初始化,编译器是检测不出来的。使用没有初始化的指针往往会导致运行时错误(run-time error)。如果可以的话,尽量将指针的定义紧跟在其指向对象的后面

【C++11】null pointer

初始化指针最直接的方式是使用字母值nullptr,其在新标准中引入。nullptr字母值是一种可以转换成其他任何指针类型的特殊类型。

也可以用NULL来初始化一个指针为空。当我们时候预定义的变量时,预处理器会自动将其替换为对应的值。因此,将一个指针初始化为NULL,等价为初始化为0。最新的C++程序应该避免使用NULL,而是使用nullptr。

void* 指针

void* 是可以保存任何对象地址的特殊指针类型。我们可以将其与另一个指针笔记,可以作为参数传递给函数,或者作为函数的返回值。但是不能操作其指向的对象,因为我们不知道其指向对象的类型

4.3复杂定义

指针的定义风格不是固定的:

int *p1;
int* p2,p3;
第一种定义方式指针修饰符紧跟变量,基础类型为int;第二种定义类型往往会将p3也误解指针类型;

int i1 = 40;
int *pi;
int *&r = pi;
因为指针是在内存中占有空间的对象,所以可以定义指针的引用。要理解上面的变量r,可以从右往左看。引用修饰符离变量r最近,其对r的作用最强,所以首先r是一个引用。然后指针修饰符说明r绑定的对象是一个指针,最后基础类型int说明r是绑定到指向int的指针的引用类型。
要理解复杂的修饰符定义,可以从右往左看

5. const修饰符

有时我们需要定义一个不会改变的变量(即常量),比如使用一个变量表示数组大小。我们使用const修饰符来定义一个常量。常量必须初始化:
const int ciSize = bufferLength();	// 运行时初始化
const int ciValue = 100;		// 编译时初始化
const int ciNo;				// 错误
如果const变量在编译时初始化,则编译器在编译时会将变量替换为相应的值。
如果一个引用绑定到const对象,则该引用不能改变这个对象,定义为:
const int iValue = 38;
const int &ri = iValue;
int &ri1 = iValue // error
如果一个对象与两个引用绑定,且其中一个引用声明为const:
int v1 = 10;
int &r1 = v1;
const int &r2 = v1;
r1 = 20;
r2 = 30; //error
cout << "r1 = " << r1 << endl;
cout << "r2 = " << r2 << endl;
我们不能通过r2来改变v1,但是可以通过r1来改变v1。
声明为const的引用可以绑定到非const 对象,或是绑定到一个类型不同的对象。对于这种情况,编译器会生成一个const类型的临时变量:
double d1 = 3.14;
const int &rd1 = d1;
等价于:
double d1 = 3.14;
const int temp = d1;
const int &rd1 = d1;
const指针
与引用不同,指针本身是对象,所以它可以声明为const对象。
int v1 = 10;
int *const cpv1 = &v1;
从右往左读就很好理解了:cpv1本身是const对象,而不是其指向的对象。cpv1会一直指向v1,不会改变。
Top-Level const
顶级const即对象本身是const,相对的low-level const是指指针和引用复类型所关联的对象是const类型。
const float f1 = 2.78; // top-level const
const float *pcf = &f1; // low-level const,we can change pf
const float *const cpcf1 = pcf; // both top-level and low-level const, we can't change pf1
当拷贝对象时,top-level const可以忽略掉。我们可以将一个const对象拷贝到非const对象,或者相反。拷贝操作不会改变源对象的值。
但是low-level const在拷贝时不能忽略。我们不可以将一个指向const对象的指针或引用,拷贝到指向普通对象的指针或引用。但是相反的话可以。
float *pf = pcf; // error
const float *pcf1 = cpcf; // ok

const 表达式

const表达式是指其值不能改变,且其值可以在编译的时候计算出来。字面值(literal)是const表达式,使用const表达式初始化的const对象,是const表达式。

【c++11】constexpr

在新标准中,我们可以用constexpr显式的声明一个表达式为const表达式。编译器可以验证该表达式是否是const表达式。只有literal type可以使用constexpr声明。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

superbin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值