来源头条作者:蹦跳小蚂蚱happy

C++的算术类型类型、含义和最小尺寸bool 布尔类型 未定义char 字符 8位wchar_t 宽字符 16位char16_t Unicode字符 16位char32_t Unicode字符 32位short 短整型 16位int 整型 16位long 长整型 32位long long 长整型 64位float 单精度浮点数 6位有效数字double 双精度浮点数 10位有效数字long double 扩展精度浮点数 10位有效数字

类型转换

当我们赋给无符号类型一个超出它表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。

当我们赋给带符号类型一个超出它表示范围的值时,结果是未定义的(undefined)。

当一个算术表达式中既有无符号数又有 int 值时,那个 int 值就会转换成无符号数。

如果表达式里既有带符号类型又有无符号类型,当带符号类型取值为负时会出现异常结果,这是因为带符号数会自动地转换成无符号数。

字面值常量

以 0 开头的整数代表八进制数,以 0x 或 0X 开头的代表十六进制数。

浮点型字面值表现为一个小数或以科学计数法表示的指数,其中指数部分用 E 或 e 标识。

C++语言规定的转义序列包括:换行符 \n 横向制表符 \t 报警(响铃)符 \a纵向制表符 \v 退格符 \b 双引号 \"反斜线 \\ 问号 \? 单引号 \'回车符 \r 进纸符 \f

泛化的转义序列,其形式是 \x 后紧跟1个或者多个十六进制数字,或者 \ 后紧跟1个、2个或3个八进制数字,其中数字部分表示的是字符对应的数值。

字符和字符串字面值:前缀 含义 类型u Unicode 16 字符 char16_tU Unicode 32 字符 char32_tL 宽字符 wchar_tu8 UTF-8(仅用于字符串字面常量) char

整型字面值:后缀 最小匹配类型u or U unsignedl or L longll or LL long long

浮点型字面值:后缀 类型f or F floatl or L long double

列表初始化定义一个名为 units_sold 的 int 变量并初始化为 0 ,以下4条语句都可以:

int units_sold = 0; int units_sold = {0}; int units_sold{0}; int units_sold(0);

变量声明和定义的关系

声明 ( declaration ) 使得名字为程序所知,一个文件如果想使用别处定义的名字则必须包含对那个名字的声明。而定义 ( definition ) 负责创建与名字关联的实体。

变量声明规定了变量的类型和名字,而定义还要申请储存空间,也可能会为变量赋一个初始值。

如果想声明一个变量而非定义它,就在变量名前添加关键字 extern ,而且不要显式地初始化变量:

extern int i; // 声明 i 而非定义 i int j; // 声明并定义 j

任何包含了显式初始化的声明即成为定义:

extern double pi = 3.14159; // 定义

在函数内部,如果试图初始化一个由 extern 关键字标记的变量,将引发错误。

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

如果要在多个文件中使用同一个变量,就必须将声明和定义分离。此时,变量的定义必须出现在且只能出现在一个文件中,而其他用到该变量的文件必须对其进行声明,却绝对不能重复定义。

引用

int ival = 1024; int // refVal 指向 ival (是 ival 的另一个名字) int // 报错:引用必须被初始化

定义引用时,程序把引用和它的初始值绑定 ( blind ) 在一起,而不是将初始值拷贝给引用。因为无法令引用重新绑定到另外一个对象,因此引用必须初始化。

引用并非对象,相反的,它只是为一个已经存在的对象所起的另外一个名字,所以不能定义引用的引用。

引用只能绑定在对象上,而不能与字面值或某个表达式的计算结果绑定在一起。

指针

与引用类似,指针也实现了对其他对象的间接访问。然而指针与引用相比又有很多不同点。其一,指针本身就是一个对象,允许对指针赋值和拷贝。其二,指针无须在定义时赋初值。

解引用操作仅适用于那些确实指向了某个对象的有效指针。

几个生成空指针的方法:

int *p1 = nullptr; int *p2 = 0; int *p3 = NULL; // 要首先#include

void* 指针可以用于存放任意对象的地址:

double obj = 3.14, *pd = void *pv = pv = pd;

不能直接操作 void* 指针所指的对象,因为我们并不知道这个对象到底是什么类型,也就无法确定能在这个对象上做哪些操作。

指向指针的指针

**表示指向指针的指针,***表示指向指针的指针的指针,以此类推:

int ival = 1024; int *pi = int **ppi = *pi;

指向指针的引用

指针是对象,所以存在对指针的引用:

int i = 42; int *p; // p 是一个 int 型指针 int * // r 是一个对指针 p 的引用 r = // r 引用了一个指针,因此给 r 赋值 // 解引用 r 得到 i ,也就是 p 指向的对象,将 i 的值改为 0

要理解 r 的类型到底是什么,最简单的办法是从右向左阅读 r 的定义。离变量名最近的符号(此例中是 const int // 正确:引用及其对应的对象都是常量 r1 = 42; // 错误:r1 是对常量的引用 int // 错误:试图让一个非常量引用指向一个常量对象

引用的类型必须与其所引用对象的类型一致,但一种特例是:在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结果能转换成引用的类型即可:

int i = 42; const int // 允许将 const int // 正确:r1 是一个常量引用 const int // 正确:r3 是一个常量引用 int // 错误:r4 是一个普通的非常量引用

其原理是:

double dval = 3.14; const int

编译器把上述代码变成如下形式:

const int temp = dval; // 临时量(temporary)对象 const int

对 const 的引用可能引用一个并非 const 的对象。

指针和 const

指向常量的指针(pointer to const)不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针:

const double pi = 3.14159; double *ptr = π // 错误:ptr 是一个普通指针 const double *cptr = π // 正确:cptr 可以指向一个双精度常量 *cptr = 42; // 错误:不能给 *cptr 赋值

允许令一个指向常量的指针指向一个非常量对象。

const 指针

就像其他对象类型一样,允许把指针本身定为常量。常量指针(const pointer)必须初始化,而且一旦初始化完成,则它的值(也就是存放在指针中的那个地址)就不能再改变了。把 * 放在 const 关键字之前用于说明指针是一个常量:

int errNumb = 0; int *const curErr = // curErr 将一直指向 errNumb const double pi = 3.14159; const double *const pip = π // pip 是一个指向常量对象的常量指针

指针本身是一个常量并不意味着不能通过指针修改其所指对象的值。

顶层 const

用名词顶层 const(top-level const)表示指针本身是个常量,而用名词底层 const (low-level const)表示指针所指的对象是一个常量。指针类型既可以是顶层 const 也可以是底层 const :

int i =0; int *const p1 = // 不能改变 p1 的值,这是一个顶层 const const int ci = 42; // 不能改变 ci 的值,这是一个顶层 const const int *p2 = // 允许改变 p2 的值,这是一个底层 const const int *const p3 = p2; // 靠右的 const 是顶层 const,靠左的是底层 const const int // 用于声明引用的 const 都是底层 const

当执行对象的拷贝操作时,常量是顶层 const 不受什么影响。而底层 const 的限制却不能忽视。当执行对象的拷贝操作时,拷入和拷出的对象必须具有相同的底层 const 资格,或者两个对象的数据类型必须能转换。一般来说,非常量可以转换成常量,反之则不行。

类型别名

有两种方法可用于定义类型别名。传统的方法是使用关键字 typedef:

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

这里的声明符也可以包含类型修饰,从而也能由基本数据类型构造出复合类型来。

新标准规定了一种新的方法,使用别名声明(alias declaration)来定义类型的别名:

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

指针、常量和类型别名

如果某个类型别名指代的是复合类型或变量,那么把它用到声明语句里就会产生意想不到的后果。例如下面的声明语句用到了类型 pstring,它实际上是类型 char* 的别名:

typedef char *pstring; const pstring cstr = 0; const pstring *ps;

此例中 const pstring 就是指向 char 的常量指针,而非指向常量字符的指针。

auto 类型说明符

C++11新标准引入了 auto 类型说明符,用它就能让编译器替我们去分析表达式所属的类型。显然,auto 定义的变量必须有初始值:

auto item = val1 + val2;

使用 auto 也能在一条语句中声明多个变量。因为一条声明语句只能有一个基本数据类型,所以该语句中所有变量的初始基本数据类型必须一样。

复合类型、常量和 auto

编译器以引用对象的类型作为 auto 的类型:

int i = 0, auto a = r; // a 是一个整数

其次,auto 一般会忽略掉顶层 const,同时底层 const 则会保留下来:

const int ci = i, auto b = ci; // b 是一个整数(ci 的顶层 const 特性被忽略掉了) auto c = cr; // c 是一个整数(cr 是 ci 的别名,ci 本身是一个顶层 const) auto d = // d 是一个整型指针(整数的地址就是指向整数的指针) auto e = // e 是一个指向整数常量的指针(对常量对象取地址是一种底层 const)

如果希望推断出的 auto 类型是一个顶层 const,需要明确指出:

const auto f = ci;

还可以将引用的类型设为 auto。

decltype 类型指示符

C++11新标准引入了第二种类型说明符 decltype,它的作用是选择并返回操作数的数据类型。在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值:

decltype(f()) sum = x; // sum 的类型就是函数 f 的返回类型

如果 decltype 使用的表达式是一个变量,则 decltype 返回该变量的类型(包括顶层 const 和引用在内)。

decltype 和引用

如果 decltype 使用的表达式不是一个变量,则 decltype 返回表达式结果对应的类型。例如:

int i = 42, *p = decltype(r + 0) b; // 正确:加法的结果是 int ,因此 b 是一个(未初始化的)int decltype(*p) c; // 错误:c 是 int // 错误:d 是 int // 正确:e 是一个(未初始化的)int

评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。