第二章 变量和基本类型
基本内置类型
基本算数类型:
类型 | 含义 | 最小尺寸 |
---|---|---|
bool | 布尔类型 | 8bits |
char | 字符 | 8bits |
wchar_t | 宽字符 | 16bits |
char16_t | Unicode字符 | 16bits |
char32_t | Unicode字符 | 32bits |
short | 短整型 | 16bits |
int | 整型 | 16bits (在32位机器中是32bits) |
long | 长整型 | 32bits |
long long | 长整型 | 64bits (是在C++11中新定义的) |
float | 单精度浮点数 | 6位有效数字 |
double | 双精度浮点数 | 10位有效数字 |
long double | 扩展精度浮点数 | 10位有效数字 |
如何选择类型
- 1.当明确知晓数值不可能是负数时,选用无符号类型;
- 2.使用
int
执行整数运算。一般long
的大小和int
一样,而short
常常显得太小。除非超过了int
的范围,选择long long
。 - 3.算术表达式中不要使用
char
或bool
。 - 4.浮点运算选用
double
。 - 5.不要混用符号类型和无符号类型。
类型转换
- 非布尔型赋给布尔型,初始值为0则结果为false,否则为true。
- 布尔型赋给非布尔型,初始值为false结果为0,初始值为true结果为1。
字面值常量
- 一个形如
42
的值被称作字面值常量(literal)。- 整型和浮点型字面值。
- 字符和字符串字面值。
- 使用空格连接,继承自C。
- 字符字面值:单引号,
'a'
- 字符串字面值:双引号,
"Hello World""
- 转义序列。
\n
、\t
等。 - 布尔字面值。
true
,false
。 - 指针字面值。
nullptr
变量
变量提供一个具名的、可供程序操作的存储空间。 C++
中变量和对象一般可以互换使用。
变量定义(define)
- 定义形式:类型说明符(type specifier) + 一个或多个变量名组成的列表。如
int sum = 0, value, units_sold = 0;
- 初始化(initialize):对象在创建时获得了一个特定的值。
- 初始化不是赋值!:
- 初始化 = 创建变量 + 赋予初始值
- 赋值 = 擦除对象的当前值 + 用新值代替
- 列表初始化:使用花括号
{}
,如int units_sold{0};
- 默认初始化:定义时没有指定初始值会被默认初始化;在函数体内部的内置类型变量将不会被初始化。
- 建议初始化每一个内置类型的变量。
变量的声明(declaration) vs 定义(define)
- 为了支持分离式编译,
C++
将声明和定义区分开。声明使得名字为程序所知。定义负责创建与名字关联的实体。 - extern:只是说明变量定义在其他地方。
- 只声明而不定义: 在变量名前添加关键字
extern
,如extern int i;
。但如果包含了初始值,就变成了定义:extern double pi = 3.14;
- 变量只能被定义一次,但是可以多次声明。
- 名字的作用域(namescope)
左值和右值
- 左值(l-value)可以出现在赋值语句的左边或者右边,比如变量;
- 右值(r-value)只能出现在赋值语句的右边,比如常量。
变量
变量定义 (1)基本形式:
类型说明符,随后紧跟着一个或者多个变量名组成的列表,其中变量名以逗号分隔,最后以分号结束。
(2)初始值
在C++中,初始化和赋值是2个完全不同的操作。初始化的含义是创建变量的时候赋予一个初始值,而赋值的含义是把对象的当前值擦除,用一个新值来替代。两者区别很小。
(3)列表初始化
用花括号来初始化变量的方式,称为列表初始化。
(4)默认初始化
如果定义变量没有指定初始值,则变量被默认初始化。
提示
例外情况:
定义在函数体内部的内置类型变量将不被初始化,其值未定义。
建议初始化每个内置类型的变量。 :::
变量声明和定义的关系 变量声明:规定了变量的类型和名字。
变量定义:除声明之外,还需要申请存储空间。
如果想声明一个变量,而非定义它,需要使用extern关键词。
extern int i; // 声明i而非定义i int j; // 声明并定义j
变量只能被定义一次,但可以被多次声明。 :::
名字的作用域 作用域:C++中大多数作用域都用花括号分隔。
作用域中一旦声明了某个名字,它所嵌套的所有作用域都能访问该名字。同时,允许在内层作用域中重新定义外层作用域中有的名字。
如果函数有可能用到某全局变量,则不宜再定义一个同名的局部变量。 :::
复合类型
引用
- 引用:引用是一个对象的别名,引用类型引用(refer to)另外一种类型。如
int &refVal = val;
。 - 引用必须初始化。
- 引用和它的初始值是绑定bind在一起的,而不是拷贝。
复合类型
定义: 复合类型是基于其他类型定义的类型。 引用 引用:为对象起另外一个名字。
引用必须被初始化。 引用本身不是对象,所以不能定义引用的引用。 引用要和绑定的对象严格匹配。 引用类型的初始值,必须是一个对象。 :::
指针
- 是一种
"指向(point to)"
另外一种类型的复合类型。 - 定义指针类型:
int *ip1;
,从右向左读,ip1
是指向int
类型的指针。 - 指针存放某个对象的地址。
- 获取对象的地址:
int i=42; int *p = &i;
。&
是取地址符。 - 指针的值的四种状态:
- 1.指向一个对象;
- 2.指向紧邻对象的下一个位置;
- 3.空指针;
- 4.无效指针。
- 指针访问对象:
cout << *p;
,*
是解引用符。 - 空指针不指向任何对象。
void*
指针可以存放任意对象的地址。- 其他指针类型必须要与所指对象严格匹配。
- 两个指针相减的类型是
ptrdiff_t
。 - 建议:初始化所有指针。
指针
指针:本身就是一个对象。允许对指针赋值和拷贝。指针无须在定义的时候赋值。
(1)利用指针访问对象
如果指针指向了一个对象,则允许使用解引用符(*)来访问该对象。
(2)void* 指针
理解复合类型的声明 (1)指向指针的指针
** 表示指向指针的指针
*** 表示指向指针的指针的指针
(2)指向指针的引用
不能定义指向引用的指针。但指针是对象,所以存在对指针的引用。
const限定符
- 动机:希望定义一些不能被改变值的变量。
初始化和const
- const对象必须初始化,且不能被改变。
- const变量默认不能被其他文件访问,非要访问,必须在指定const前加extern。
const的引用
- reference to const(对常量的引用):指向const对象的引用,如
const int ival=1; const int &refVal = ival;
,可以读取但不能修改refVal
。 - 临时量(temporary)对象:当编译器需要一个空间来暂存表达式的求值结果时,临时创建的一个未命名的对象。
- 对临时量的引用是非法行为。
指针和const
- pointer to const(指向常量的指针):不能用于改变其所指对象的值, 如
const double pi = 3.14; const double *cptr = π
。 - const pointer:指针本身是常量,如
int i = 0; int *const ptr = &i;
顶层const
顶层const
:指针本身是个常量。底层const
:指针指向的对象是个常量。拷贝时严格要求相同的底层const资格。
constexpr
和常量表达式
- 常量表达式:指值不会改变,且在编译过程中就能得到计算结果的表达式。
C++11
新标准规定,允许将变量声明为constexpr
类型以便由编译器来验证变量的值是否是一个常量的表达式。
const限定符 定义:const用于定义一个变量,它的值不能被改变。const对象必须初始化。
提示
默认状态下,const对象仅在文件内有效。当多个文件出现了同名的const变量时,等同于在不同文件中分别定义了独立的变量。
如果想让const变量在文件间共享,则使用extern修饰。
(1)const的引用
允许为一个常量引用绑定非常量的对象、字面值,甚至是个一般表达式。
一般,引用的类型必须与其所引用对象的类型一致,特殊情况是表达式。
(2)指针和const
弄清楚类型,可以从右边往左边阅读。
(3)顶层const
top-level const 表示指针本身是个常量
low-level const表示指针所指的对象是一个常量。
(4)constexpr和常量表达式
C++新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式。
处理类型
类型别名
- 传统别名:使用typedef来定义类型的同义词。
typedef double wages;
- 新标准别名:别名声明(alias declaration):
using SI = Sales_item;
(C++11)
auto类型说明符
- auto类型说明符:让编译器自动推断类型。
int i = 0, &r = i; auto a = r;
推断a
的类型是int
。- 会忽略
顶层const
。 const int ci = 1; const auto f = ci;
推断类型是int
,需要自己加const
C++11
decltype类型指示符
- 从表达式的类型推断出要定义的变量的类型。
- decltype:选择并返回操作数的数据类型。
decltype(f()) sum = x;
推断sum
的类型是函数f
的返回类型。- 不会忽略
顶层const
。 C++11
自定义数据结构
struct
- 类可以以关键字
struct
开始,紧跟类名和类体。 - 类数据成员:类体定义类的成员。
C++11
:可以为类数据成员提供一个类内初始值(in-class initializer)。
编写自己的头文件
- 头文件通常包含哪些只能被定义一次的实体:类、
const
和constexpr
变量。
预处理器概述:
- 预处理器(preprocessor):确保头文件多次包含仍能安全工作。
- 当预处理器看到
#include
标记时,会用指定的头文件内容代替#include
- 头文件保护符(header guard):头文件保护符依赖于预处理变量的状态:已定义和未定义。
#ifndef SALES_DATA_H
#define SALES_DATA_H
strct Sale_data{
...
}
#endif
处理类型 类型别名 两种方法用于定义类型别名:
(1)使用关键词typedef
typedef double wages; //wages是double的同义词 typedef wages p; // p是double的同义词 (2)别名声明
using SI = Sales_item; // SI是Sales_item的同义词 auto类型说明符:让编译器通过初始值来推算变量的类型。
decltype类型指示符:选择并返回操作符的数据类型。只得到类型,不实际计算表达式的值。
自定义数据结构 (1)类
数据结构是把一组相关的数据元素组织起来,然后使用它们的策略和方法。
类一般不定义在函数体内,为了确保各个文件中类的定义一致,类通常被定义在头文件中,而且类所在头文件的名字应该与类的名字一样。
头文件通常包含那些被定义一次的实体。
(2)预处理器
ifndef SALES_DATA_H
define SALES_DATA_H
endif
一般把预处理变量的名字全部大写。
术语 空指针 :值为0的指针,空指针合法但是不指向任何对象。nullPtr是表示空指针的字面值常量。
void*:可以指向任意非常量的指针类型,不能执行解引用操作。