变量和数据类型

变量和数据类型

内置类型的机器表示

8个 bit 为一个 byte,32位系统下4个 byte 叫一个 word

基本内置类型

unsigned int 也可以简称为 unsigned

unsigned 类型在赋值超出范围的时候会取该值对该类型取值数目求模后的值。例如 unsigned char 类型在给超出255的值(如336)进行赋值时会将其对256进行求模操作,这样下来它的值就是80(336 % 255 = 80)

字符(串)字面值前面加上 L 就可以变成宽字符(串)字面值,如

1
2
L'我';
L"我有一个";

详细的字面值类型见下表

前缀 含义 类型
u Unicode16字符 char16_t
U Unicode32字符 char_32t
L 宽字符 wchar_t
u8 utf-8(仅用于字符串字面常量) char

定义对象

初始化:创建变量并为它赋初值。

赋值:擦除对象的当前值并用新值代替

声明和定义

如果声明有初始化式,即使前面有 extern 也可以当作是定义,如

1
extern double pi = 3.1416; //虽然有extern,但是可以当成是定义而非声明

注:只有当 extern 声明位于函数外部时,才可以含有初始化式。也就是说函数体内部不能使用上述办法。

名字的作用域

全局作用域中的 const单个文件的局部变量,只有在定义时加上 extern 才能被别的文件访问。

const 引用

const 引用只能绑定到与该引用同类型的对象,而 const 引用可以绑定到不同但相关的类型的对象或绑定到右值

1
2
3
4
5
6
7
8
int i = 7;

int &invalid_r1 = 47; //不行,编译器会报错,因为右边是一个字面常量,和int并不是相同类型
int &invalid_r2 = invalid_r1 + i; //也不行,因为临时变量不能做非const引用的右值
const int &r1 = 47; //行
const int &r2 = r1 + i; //行

int &r3 = i; //行,因为是同种数据类型

类的数据成员

一般不能把类的初始化作为其定义的一部分,定义数据成员时,只指定该数据成员的名字和类型类不是在类定义里面定义数据成员的时候初始化成员,而是在构造函数里面。

C++11新标准规定:可以为数据成员提供一个类内初值。创建对象时类内初值将用于初始化数据成员。

使用 struct 关键字

structclass 不同之处在于默认访问标号,struct 默认是 public 访问,而 class 则是默认 private 访问。

列表初始化

列表初始化允许用一对大括号来初始化对象了,其特点是:当使用列表初始化初始化对象时存在丢失信息的风险,编译器会报错(窄化转换)。现在初始化有四种形式,如下。列表初始化不会损失精度!

1
2
3
long double ld = 3.1415926536;
int a{ld}, b = {ld}; //这两种是列标初始化,编译器要报错,因为丢失精度
int c(ld), d = ld; //这两种编译器不会报错,但是丢失精度

如果不用等号,那么就是直接初始化,如果用等号,就是拷贝初始化。拷贝初始化就是新建一个临时变量,再把其值复制过去。

常量表达式

值不会改变且在编译过程中就能得到计算结果的表达式。

C++11中允许将变量声明为 constexpr 类型以便由编译器来验证变量的值是否是一个常量表达式。声明为 constexpr 的变量一定是一个常量。如下

1
2
3
constexpr int mf = 20; //是一个常量表达式
constexpr int limit = mf + 1; //是一个常量表达式
constexpr int sz = size(); //如果size()是一个constexpr函数,那就是。否则不是

如果 constexpr 定义的是一个指针,那么限定符仅对指针有效,与指针所指的对象无关。

1
2
const int *p = nullptr; //这个是底层const
constexpr int *p = nullptr; //这个是顶层const

类型别名

就是类型的小名,有两种方法定义,如下是老办法typedef

1
2
typedef double wages; //wages 是 double 的小名
typedef wages base, *p //base 是 double 的小名,p是 double *的小名

C++11定义了新的办法 别名声明,如下

1
using SI = Seals_item; //SI是Seals_item的小名

auto类型说明符

auto 一般会忽略掉 顶层const ,保留 底层const。如

1
2
3
const int ci = i;
auto b = ci; //b是int,因为顶层const被忽略了
auto e = &ci; //e是const int *类型,因为对常量对象取地址是一种底层const

如果想要auto推断出一个顶层const的话需要显式说明,如

1
const auto f = ci; //因为auto推断的时候忽略了顶层const,手动加上了一个顶层const后,f的数据类型又变成了const int

也可以将引用类型设置为auto,此时引用的初始化规则仍然有效。如

1
2
3
auto &g = ci; //行,auto推断出来g是一个整型常量引用,可以绑定
auto &h = 42; //不行,非const引用没办法绑定字面值常量
const auto &j = 42; //可以,显式将auto变成了顶层const之后就可以绑定字面值常量了

注:在一个语句中定义多个变量时切记,符号 & 和 * 只从属于某个声明符,而非基本数据类型的一部分!!如下

1
2
3
4
//其中ci是const int类型,i是int类型
auto k = ci, &l = i; //k是整数,l是整型引用。引用不是数据类型,整型才是!这句正确
auto &m = ci, *p = &ci; //m是对整型常量的引用,p是指向整型常量的指针。ci数据类型是const int,所以这句正确
auto &n = i, *p2 = &ci; //这句错误,因为i的数据类型是int而ci的数据类型是const int

decltype类型指示符

这个类型指示符只用来返回表达式的数据类型,不返回表达式的值。用法如下

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

使用decltype时,编译器并不实际调用 f()

decltype 处理顶层const和引用的方式与auto不同。如果decltype使用的表达式是一个变量,则decltype返回该变量的类型(包括const和引用),如下

1
2
3
4
5
const int ci = 0, &cj = ci;
decltype(ci) x = 0; //x是const int型
decltype(cj) y = x;//y是const int引用,绑定到x

decltype(cj) z; //错误,因为const int引用必须得初始化

又如

1
2
3
int i = 42, *p = &i, &r = i;
decltype(r + 0) b; //加法的结果是int,这句是对的,b没被初始化
decltype(*p) c; //如果表达式是解引用的话,那么decltype得到的是引用类型。所以这句错误,因为引用没有初始化

切记:decltype((variable)) 的结果永远是引用,而 decltype(variable) 的结果只有当 variable 本身是引用时才是引用,如

1
2
3
int i = 0;
decltype((i)) d; //错误,d是int&,必须初始化
decltype(i) e; //正确,e是一个未初始化的int

类内初始值(C++11)

创建对象时,类内初始值将用于初始化数据成员,没有初始值的成员将会被默认初始化。

string::size_type 类型

string::size_type是一个无符号类型的值而且足够存下任何string类型值。所有用于存放string类的size函数返回值的变量都应该是string::size_type类型

C++11中允许使用 autodecltype 来推断变量类型,如

1
auto len = line.size()

注:如果一条表达式中已经有了 size() 函数就不要再使用 int 了!原因如下

1
2
int n = -1;
if(s.size() < n); //该语句的结果很大可能是true,因为n是负数,它会自动转换成一个很大的 unsigned 值