目录
基本内置类型
1. 算术类型
2. 类型转换
3.字面值常量
变量
1. 变量定义
2. 变量声明与定义的关系
3. 标识符
4. 作用域
复合类型
1. 引用
2. 指针
3. 理解复合类型的声明
const限定词
1. const的引用
2. const和指针
3. 顶层const
4. constexpr和表达式
处理类型
1. 类型别名
2.auto类型说明符
3.decltype类型指示符
内置类型分为算术类型和空类型。空类型不具有值的含义,仅用于特殊场合,常见的有函数无返回值时,用空类型作为返回值。
算术类型可以分为整型和浮点型。
浮点型分为单精度float、双精度double和扩展精度long double浮点型。一般情况下,float为6-7个有效数字,double为16-17有效数字,long double根据需求不同,长度也不同。
测试:使用vs2017尝试打印float和double长度。
float fa = 1.0 / 3; double_t da = 100000.0 / 3; std::cout << fa << " " << da;可以看到只有6位有效数字,与知识不符
后查阅资料得知,因为cout默认输出有效数字为6个(qDebug也是),要使用的话需要用到setprecision()函数,使用这个函数各需要包含头文件 iomanip,代码如下:
#include <iostream> #include <iomanip> int main() { std::cout << "Hello World!\n"; float fa = 1.0 / 3; double_t da = 100000.0 / 3; std::cout << std::setprecision(20) <<fa <<" "<< da; }可以看到结果,float类型到6位有效数字后不准确,double到16位有效数字后不准确。
Qt中虽然能包含这个头文件,但是qDebug输出无法通过这种方式修改。
最后,附上算术类型最小尺寸表。
算术类型最小尺寸表 类型含义最小尺寸bool布尔类型未定义char字符型8位wchar_t宽字符16位char16_tUnicode1616位char32_tUnicode3231位short短整型16位int整形16位long长整形32位long long长整形64位float单精度浮点型6个有效数字double双精度浮点型10个有效数字long double扩展精度浮点型10个有效数字无符号和带符号类型
除了布尔型和扩展字符(wchar、char16_t、char32_t)外,其他整型都分为带符号(signed)和无符号(unsigned)两种。其中char类型具体表示signed还是unsigned根据编译器决定,其他整形不带符号(signed/unsigned)都表示带符号整形。
常见的类型转换(隐式转换):
非布尔算术值赋值给布尔型,0为false,非0为true(注意负数)。布尔型赋值给非布尔型,false为0,true为1.浮点型赋值给整型,截取整数部分。整型赋值给浮点型,自动补0无符号类型赋值超过范围的值。为初始值对无符号类型能表示数值总数求模的余。如-1赋值给unsigned short,则为-1 mod 256,商-1余255,所以赋值结果为255带符号类型赋值超过范围值,会出现未定义情况。软件可能会崩溃。求模运算
求模与除法最大的区别在于:除法的商是向0取,而求模的商是向负无穷取。所以正数两者相同,负数时会有差异。
除法: 10 / 3 商取3,余1。 -10 / 3 商取-3余-1。
求模:10 mod 3 商取3,余1。-10 mod 3 商取-4 余2。
注意:无符号类型的转换两点易错情况
作为循环条件时,可能会出现死循环情况。作为表达式运算结果时,会对运算结果转换 for (unsigned int i = 10; i >= 0; --i) { ; }以上代码,当i=0时,满足条件,进入函数体后运算--i,将-1赋值给i,变成4294967295.一直循环。
unsigned short uia = 1; unsigned short uib = 2; unsigned short uic = uia - uib; short ib = -2; unsigned short ic = uia + ib; std::cout << uic << std::endl; std::cout<<ic << " " << uia - 2;以上结果可以看到uic和ic输出是按我们说的求模的余,但是uia-2则不对,这是因为进行了整数提升,后面内容会提到。
字面值常量的形式和值决定了他的数据类型
整型和浮点型字面值常量
整型字面值可以写为十进制、八进制和十六进制形式,0开头为八进制,0x或0X开头为十六进制。
默认情况下,十进制字面值是带符号数。
浮点型字面值以小数或科学记数法表示的指数。
默认情况下,浮点型字面值是doule。
字符和字符串字面值
以单引号括起来的一个字符是字符型字面值,以双引号括起来的单个或多个字符是字符串字面值。
两者的区别在于字符串字面值存储时比字面上多一个空字符'\0',作为字符串的结束字符。注意"A"和'A'的区别。
转义序列
一种是不可打印字符,如回车,另一种是特殊字符如'\',这时候需要在字符前加一个'\'字符表示转义。
指定字面值的类型
指定字面值类型 前缀含义类型uUnicode16char16_tUUnicode32char32_tL宽字符wchar_tu8UTF-8char后缀含义类型 整型字面值u或U无符号unsignedl或Llonglongll或LLlong longlong long 浮点型字面值f或Ffloatfloatl或Llong doublelong double布尔型和指针字面值
布尔型字面值是false和true。指针字面值是nullptr。
变量是一个具名的,可供程序操作的存储空间。
变量定义的形式:一个类型说明符,加上若干个变量名(逗号隔开),以分号结束。当然定义的时候可以为其中若干个变量赋初值。
int a,b=2,c=3,d,e;初始值
像上面在定义的时候为变量赋初值叫对变量的初始化。初始化和赋值的区别在于:初始化含义是在创建变量时赋予初始值,而赋值是将当前值擦出,写入新的值。
列表初始化
初始化时用大括号的形式,叫做列表初始化,这种方式的好处在于,内置类型初始化时如果存在数据丢失的风险,则会报错。
int i = {4.14}; int b{5.12};默认初始化
如果变量定义时未被赋予初值,则变量会被默认初始化。默认初始化根据位置和类型有所区别。
内置类型定义于函数体之外,则被初始化为0内置类型定义域函数体之内,则未被初始化,及未定义其他类根据类自身决定初始化方式。如string类,会初始化为一个空字符串建议函数体内变量都初始化。
一个变量要想在别的文件中使用,必须在另一个文件中声明,声明的形式与定义类似,只要在其前加上extern关键字。
注意:任何显示初始化的声明即为定义。
两者的区别:声明可以多次,定义只能一次。
由字母数字和下划线组成,且首字母不能为数字。
全局作用域:定义于函数体之外,在整个程序中都可以使用。
块作用域:定义于函数体内,仅作用于当前块作用域。(始于当前声明,结束于当前语句块)
引用即 将引用名绑定到对象,并且不可以重新绑定另外对象,所以引用必须初始化。
引用仅仅是给对象起一个别名并绑定到对象上,所以其并非对象,无法对其执行对象的操作,如定义引用的引用。
引用的定义
和变量定义一样,引用可以一次定义多个引用,但是每个引用的标识符都要以&开头。
int i=0,i1=2,i3=4; int &r = i,&r1 = i2, r2 = i3;//r,r1为引用,r2为变量除了两种情况(const引用和)外, 引用的定义的类型必须与绑定的对象类型保持一致。
指针也实现了对其他对象的间接访问。指针获取对象的地址可以用取地址符&。
指针值
指针的状态一般有4种:
空指针指向一个对象的地址指向一个对象地址的相邻地址野指针,也就是上述三种之外的情况访问无效指针可能会导致软件崩溃。
指针访问对象
可以通过解引用符(*)访问指针指向的对象。当然解引用符只适合有效指针。
空指针
生成空指针的几种方式
int *p1 = 0; int *p2 = nullptr; int *p3 = NULL;在使用任何指针之前一定要判断是否为空。
void *指针
一种特殊的指针,可以存放任意对象的地址。
定义多个变量
定义时,&和*只作用于一个变量,所以每个复合类型变量定义前都是以&和*开头。
指向指针的指针
int i=0; int *p = &i; int **p = &p;指向指针的引用
int i=0; int *pi = &i; int *&rpi = pi;有时希望变量不可改变,可以在定义时加上const限定词。
因为const限定词定义的对象不可改变,所以必须初始化。
const对象只是不能对其进行改变其值的操作,其他操作均可以;
也称为常量引用。前面提到引用必须保持引用类型与其所引用对象类型必须保持一致,但有两个例外,一个就是此处的常量引用。
常量引用针对的只是定义的对象,对于初始化时并不要求引用的对象也是const。
此外只要初始化值能隐式转换到常量引用的基础数据类型,就不会报错。
double dval = 3.14; const int &ival = dval;//正确。其实此处是经过了一层转换。
double dval = 3.14; const int temp = dval; const int &ival = temp;//正确。所以当改变dval值时,ival值并不会改变。
指向常量的指针。与常量引用类似,要求不能通过指针改变右侧的值。
常量指针。const限定词限制的是指针,此时指针初始化后指向的地址不可改变,但是该地址指向的变量值可以改变。
所谓指向常量的指针和引用,只是自以为指向了常量,不自觉的不去改变对象的值
顶层const:const限定的对象,不可执行改变操作。
底层const:可执行改变操作。
在执行拷贝操作时,底层const要求两者有相同的底层const资格,或能相互转换。
相当于给类型起一个别名。
两种方式:typedef和using
typedef ldouble long double; ldouble dtemp = 4.32; using temp = int; temp ta = 2;auto类型说明符可以根据初始值来推算变量类型。
因为一条声明语句只能定义一个类型,所以auto定义多个变量时,每个变量都要是相同的类型。
auto i=0,*p = &i; auto i=4.2,j = 3;//错误,因为i,j会是不同类型复合类型、常量和auto
当auto作用于引用时,实际参与的是引用对象的类型。
其次当一般顶层const会被忽略,底层const被保留,要想保留顶层const,则需要在auto前加const。
作用于复合类型时顶层const被保留,此时,引用仍是顶层const,指针却变成底层const。
与auto不同的是,以表达式的返回值获取类型,并不关心各变量初始值。
decltype会保留所有类型,包括顶层const和引用。
对于一个引用类型变量,直接使用decltype会得到一个引用类型,要想获取基本数据类型,可以使用表达式法,即decltype(r+0).另外一种注意情况是,如果变量名加上括号,会得到引用类型(仅针对变量而言,对表达式无效)。
int a=0, &r = a; decltype(r) c = a;//c类型为int & decltype(r+0) d = 2;//d类型为int decltype((r+0)) h = 3;//h为int decltype(d) e = 10;//e类型为int decltype((d)) f = e;//f为int &