类型与声明
类型
字符类型
- char 默认类型,通常占8位, 是否带符号以来于实现。
- signed char 可以存放正值也可以存放负值
- unsigned char 不带符号
- wchar_t 用于存放unicode 等更大的字符集。wchar_t 的尺寸依赖实现
- char16_t 用于存放UTF-16的16位字符集
- char32_t 用于存放UTF-32的32位字符集
对于char, signed char, unsigned char, 这三个名字代表的类型各不相同。我们不能混用这三个类型的指针。
编译会报类似下面的错误: invalid conversion from ‘char*’ to ‘unsigned char*’ [-fpermissive]
- 单引号的char 字符: 把多个字符放在一对单引号内,比如’ab’这个结果完全依赖实现,尽量避免使用
- 双引号的char 字符:
- char v1[] = “a\xah\129”; 6个字符‘a’ ‘\xa’ ‘h’ \12’ ‘9’ ‘\0’
- char v2[] = “a\xah\127”; 5个字符‘a’ ‘\xa’ ‘h’ \127’ ‘\0’
- char v3[] = “a\xad\127”; 4个字符‘a’ ‘\xad’ \127’ ‘\0’
- char v4[] = “a\xad\0127”; 5个字符‘a’ ‘\xad’ \012’ ‘\7’ ‘\0’
整数类型
- 如果需要精细的控制整数的尺寸,可以使用 中定义的别名, 包括int64_t 等
- 后缀U 显示的指定unsigned 字面值常量
- 后缀L 显示的指定long 字面值常量
声明
- 声明的结构, 包含5个部分
-
可选的前置修饰符(比如static 和 virtual)
- 修饰符指的是声明语句中最开始的关键字,如virtual,extern, constexptr 等,作用是指定所声明对象的某些非类型属性
-
基本类型(比如 vector 和 const int)
-
可选的声明符,可包含一个名字(比如p[7], n 和 ()[]) 由一个名字和一些可选的声明运算符组成
声明运算符 前缀 * 指针 前缀 *const 常量指针 前缀 *volatile volatile 指针 前缀 & 左值引用 前缀 && 右值引用 前缀 auto 函数(使用后置返回类型 后缀 [] 数组 后缀 () 函数 后缀 -> 从函数返回 后缀声明符的绑定效果比前缀的声明符更加紧密, 所以char* kings[] 是char 指针的数组, 而char (*kings)[] 是 指向char 数组的指针。
如果需要声明“数组的指针” 或者 “函数的指针”, 则必须使用括号加以限定。 -
可选的后缀函数修饰符(比如const 和 noexcept)
-
可选的初始化器或函数体(比如 = {7,5,3})
-
声明,看似简单,但是掺杂了前缀和后缀后变得很难理解
声明多个名字
- 允许在同一条语句中声明多个名字,其中包含逗号隔开的多个声明符
int x,y;
- 但是需要注意在声明语句中,运算符只作用于紧邻的一个名字,对于后续的其它名字是无效的。
int* p,y; 声明的是int* p; int y
int v[10], *pv; 声明的是int v[10], int *pv;
- 良好的编码习惯尤为重要,一个良好的编码不会出现上面的情况。
名字
- 起名字犹如艺术家的创作,看似简单,实则每个都要深思熟虑才行
- 对于以前的C 编码规范,习惯于把类型缩写放在变量明称前面,或者加上g+ …, str…, 这个方式不提倡
初始化
初始化器有四种可能的形式:
(1)X a1 {v};
(2)X a2 = {v};
(3) X a3 = v;
(4) X a4(v);
- (1) 列表初始化 : 形式不受任何限制,提倡使用的方法 能够防止窄化转换
- 窄化转化 的概念
- 如果一个整型存不下另一个整型, 则后者不会被转化为前者。例如允许char 到int 的转化,但是不允许int 到char 的转化
- 如果一个浮点型装不下另一个浮点型的值, 则后者不会被转化为前者。允许float 到double 的转化,但是不允许double 到float 的转化
- 浮点型不能转化为整形
- 整型值不能转化为浮点型
- 使用auto 关键字从初始化器推断变量类型时,不能使用列表初始化器,初始化的类型是initializer_list 类型。所以auto 不建议采用初始化列表
- 窄化转化 的概念
对象和值
左值和右值
-
理解左值和右值, 从概念上很难理解,但是从以下两个属性方面理解,会容易理解些:
有身份 在程序中有对象的名字, 或指向该对象的指针,或该对象的引用。
可移动 能把对象的内容移动出来(比如我们能把它的值移动到其他出来,剩下的对象处于合法,但是未指定的状态)使用m 表示可以移动, i 表示有身份,可以有以下三种组合:
- 左值(i&!m)
- 特别值(i & m)
- 纯右值(!i & m)
泛左值 包括左值和特别值
右值 包括 特别值和纯右值
-
std::move(vs) 是一个特别值(不确定是否还有其他特别值)
-
在实际代码编写中,考虑左值和右值就足够了,一条表达式要么是左值,要么是右值。