正文:
2.1基本内置类型
算数类型:
算数类型包含整形、浮点型、字符等,其中不同的类型所占的空间大小也不同,同一个类型在不同机器上锁占的大小也不同,例如int占用4个字节的大小,一字节等于八比特(位),也就是int类型占据32位,每一位都是一个二进制数字(0或1)。下表是C++标准规定的最小尺寸
除去布尔类型和扩展的字符型之外,其他整形可以划分为带符号的和无符号的,int、short等都是带符号的,在前面加上unsigned能转换为无符号的,无符号的所有比特都用来储存数据,而带符号的有部分比特位用来保存符号,所以无符号数能表示的数字更大
类型转换:
当在程序的某处我们使用了一种类型而其实对象应该取另一种类型时,程序会自动进行类型转换,例如:`
int i = 3.14//程序会自动把3.14转换为整形,即3
注意:当我们给带符号类型一个超过它表示范围的值时,结果是未定义的,此时程序可能继续工作、可能崩溃,也可能生成垃圾数据
无符号类型在使用时一定要注意取值范围,当一个表达式中既有无符号数又有int时,那个int值就会转换成无符号数,例:
unsigned u = 10;
int i = -42;
std::cout << i + i <<std::endl;//输出-84
std::cout << u + i <<std::endl;//如果int占32位、输出4294967264
2.2变量
变量定义:
变量定义的基本形式:类型说明符+变量名,其中变量名可以为一个或多个,在对象创建时获得了一个特定的值,这个过程成为初始化,初始化有以下几种形式:
int a = 0;
int a = {0};
int a{0};
int a(0);
用花括号初始化是C++11的新标准,这种方式成为列表初始化。
注意:初始化不是赋值,初始化的含义是创建变量时赋予一个初始值,而赋值的含义是把对象的当前值擦除,而以一个新值来替代。
变量声明和定义的关系:
变量只能被定义一次,但是可以被多次声明,如果要在多个文件使用同一个变量,必须对变量进行声明,方法如下:
extern int i;//声明i而非定义i
int j;//声明并定义i
2.3复合类型
引用:
C++11标准提出了右值引用的概念,在本文只讨论左值引用,以下的“引用”全部指左值引用。引用的具体用法:
int ival = 1024;
int &refval = ival;//对ival引用
引用不创建新的对象,它只是原来对象的别名,所以,对修改引用的值就是修改原来对象的值。
注意:由于引用一经“绑定”就不得修改,所以引用必须初始化
指针:
指针用来存放某个变量的初始值,用法:
int ival = 1024;
int *p = &ival;//指针的定义
p中存放的是变量ival的地址,&是取地址符,用来取变量ival的地址。
指针的值应该属于下列四种情况之一:
1.指向一个对象
2.指向紧邻对象所占空间的下一个对象
3.空指针,意味着指针没有指向任何对象
4.无效指针,也就是上述情况之外的值
要特别注意指针的使用,试图访问无效指针属于非法行为,会引发意想不到的后果。
空指针:
空指针不指向任何对象,其可以定义为:
int *p1 = nullptr;//等价于int *p1 = 0
int *p2 = 0;//直接将p2初始化为字面常量0
int *p3 = NULL;//需要先包含头文件cstdlib,等价于int *p3 = 0;
要特别注意,下面这种定义空指针的方式是错误的:
int zero = 0;
pi = zero;
指针和引用:
由于引用并没有创建一个新对象,只是已存在对象的别名,所以不存在指向引用的指针,但是存在指针的引用
int i = 42;
int *p;//p是一个int型的指针
int *&r = p;//r是一个对指针p的引用
r = &i;//r引用了一个指针,因此给r赋值&i就是让指针p指向i
*r = 0;//解引用r得到i,也就是p指向的对象,将i的值改为0
2.4const限定符
由于const修饰的变量无法被修改,所以在定义时必须初始化
const int a = 0;//正确
const int b;//错误,定义是必须进行初始化
const与引用:
初始化常量引用时可以使用任意表达式,例:
int i = 42;
int &r1 = i;//正确,引用r1绑定变量i
const int &r2 = i;//正确,r2也绑定变量i,但是不允许通过r2来改变i的值
r1 = 0;//正确
r2 = 0;//错误,r2为常量引用
const与指针:
与引用类似,指针也可以用const来修饰,包含指向常量的指针和常量指针,例:
int i = 0;
int j = 0;
const int *p1 = &i;//指向常量的指针
*p1 = 1;//错误,不能通过p1修改i的值
p2 = &j;//正确,将p2指向j的地址
int * const p2 = &i;//常量指针
*p2 = 1;//正确
p2 = &j;//错误,不能修改指针p2指向的地址
两种指针的区别在于一个不可以改变指向的值,一个不可以改变指向的对象,也可以使用两个const修饰符进行修饰,其两个值都i无法改变
int i = 0;
const int * const p = &i;//正确
顶层const与底层const:
用名词顶层const表示指针本身是一个常量,而用名词底层const表示指针指向的对象是一个常量。当执行对象拷贝操作时,顶层const不会受什么影响,对于底层const来说,拷入和拷出的对象必须具有相同的底层const资格,或者两个类型可以相互转换,一般来说,非常量可以转换为常量,反之则不行。
2.5处理类型
类型别名:
使用关键字typede或者使用别名声明可以为一个变量定义别名,例:
typedef double wages;
using wages = double;
auto类型说明符:
编程时常常需要把表达式的值赋给变量,那么就必须知道变量的准确类型,有时我们无法得知,则可以使用auto说明符,auto是C++11的新标准,其使用方法是:
auto item = val1+val2;//由val1和val2相加的结果确定item的变量类型
显然,使用auto类型说明符时,变量必须有初始值
auto与引用:
由于当引用被用作初始值时,其参与运算的时引用所绑定的对象,所以:
int i = 0,&r = i;
auto a = r//a是一个整数
decltype类型指示符:
如果需要编译器自动推断变量类型,且不想用该表达式的值初始化变量,则可以使用decltype指示符,decltype是C++11的新标准,使用方法是:
const int ci = 0,&cj = ci;
decltype(ci) x = 0;//正确,x的类型是const int
decltype(cj) y = x;//正确,y的类型是引用,绑定到变量x上
decltype(cj) z;//错误,z的类型是引用,必须初始化
decltype与引用:
如果decltype使用的不是变量,而是表达式,则返回的是表达式结果对应的类型:
int i = 42,*p = &i,&r = i;
decltype(r+0) b;//正确,加法的结果是int,所以b的类型是int
decltype(*p) c;//错误,c的类型是引用,引用必须初始化
注意:如果decltype使用的是一个不加括号的变量,则得到的结果是该变量的类型;如果给变量加上了一层过或多层括号,编译器就会把它当成一个表达式,且变量是一种可以作为赋值语句左值的特殊表达式,所以这样decltype将会得到引用类型:
decltype((i)) d;//错误,d的类型是引用,引用必须初始化
2.6自定义数据类型
谨记使用struct创建一个结构体时,务必在{}后加上分号
练习题:
练习2.1:
区别是在地址中所占的大小不同
区别是所有比特位是否都用来表示数字
区别是精度不同
练习2.2:
利率使用float类型,本金和付款使用double类型
练习2.3:
结果如下
练习2.5:
(a)char, wchar_t, char, wchar_t
(b) int, unsigned, long, unsigned long, int, int
(c)double, float,long double
(d)int, unsigned, double, double
练习2.6:
有区别,第一组是十进制数,第二组是八进制数
练习2.7:
(a)字符串(b)long long(c)float(d)long double
练习2.8:
#include <iostream>
int main()
{
std::cout << "2M\n";
return 0;
}
#include <iostream>
int main()
{
std::cout << "2\tM\n";
return 0;
}
练习2.9 :
(a)不能在输入时定义变量,应该先定义再赋值
(b)(d)无非法操作,但可能会丢失数据
(c)wage未定义
下面是改正后的代码:
int input_value;
std::cin>>input_value;
double salary = 9999.99,wage = 9999.99;
练习2.10:
global_str, local_str为空字符串
global_int为0
local_int未定义
练习2.11:
(a)(b)是定义,(c)是声明
练习2.12:
(b)(c)(d)是非法的
练习2.13:
j = 100
练习2.14:
100 45
练习2.15:
(b)不合法,引用的右值应为变量
(d)不合法,引用必须初始化
练习2.16:
(a)合法,将3.14159赋给r2绑定的对象d
(b)合法,将r1绑定对象的值赋给r2绑定对象的值
(c)合法,将i的值赋给r2绑定的对象
(d)合法,将d的值赋给r1绑定的对象
练习2.17:
10 10
练习2.18:
#include <iostream>
int main()
{
int i = 0;
int* p = &i;
i = 5;
std::cout << i<<" ";
*p = 10;
std::cout << i;
return 0;
//运行结果为5 10
}
练习2.19:
指针是指向地址的变量,而引用只是一个变量的别名
练习2.20:
指针pi指向变量i的地址,然后将i的值变为i*i
练习2.21:
(a)非法,类型不相符
(b)非法,没有取地址符
练习2.22:
if(p)//如果指针p不为空指针,则表达式为真
if(*p)//如果指针指向的对象不为0,则表达式为真
练习2.23:
可以使用if(p)来判断,若指针有所指对象,则执行if下面的语句
练习2.24:
类型不相符
练习2.25:
(a)指针、int、int
练习2.26:
(a)不合法,const修饰的变量必须初始化
(d)不合法,常量不可修改
练习2.27:
(c)(d)(e)(g)合法
练习2.28:
(a)(b)(c)(d)不合法
练习2.29:
(a)(c)合法
练习2.30:
第一个为顶层,最后i一个为底层
练习2.31:
不合法
int *p = nullptr;
练习2.35:
#include <iostream>
int main()
{
const int i = 42;
auto j = i;
const auto& k = i;
auto* p = &i;
const auto j2 = i, & k2 = i;
std::cout<<typeid(j).name()<<std::endl;
std::cout << typeid(k).name() << std::endl;
std::cout << typeid(p).name() << std::endl;
std::cout << typeid(j2).name() << std::endl;
std::cout << typeid(k2). name() << std::endl;
return 0;
}
运行结果:
练习2.36:
a,b,c为int,d为引用
a = b = c = d =4
练习2.37:
a,b,c为int,d为int*
练习2.38:
decltype不会将表达式的值赋给变量,auto会将表达式的值赋给变量
int i = 0;
//下面是相同的示例
auto b1 = i;
decltype(i) c1;
//下面是不同的示例
int &ri = i;
auto b2 = ri;//b2为整型
decltype(ri) c2;//错误,c2为引用,必须赋初值
练习2.39:
struct Foo
{
}
int main()
{
return 0;
}
小结:
本章对于没学过C语言的同学具有一定难度,我在这里总结出来的仅仅是皮毛,读者还应以书本为主,多多阅读才能快速进步。如果本文有任何错误,欢迎大家指出,我看到后会及时修正。