c++ primer 第五版----学习笔记(二)
1.算术类型:字符、整型数、布尔值和浮点数;c++标准规定的尺寸的最小值:
- c++语言规定一个int至少和一个short一样大,一个long long至少和一个long一样大。其中数据类型long long实在c++11中新定义的。
- 带符号类型(signed),无符号类型(unsigned);在带符号类型名前添加unsigned就能得到无符号类型。
- 如何选择类型:(1)当明确知晓数值不可能为负时,选用无符号类型。
- (2)使用int执行整数运算。
- (3)在算术表达式中不要使用char或bool,只有在存放字符或布尔值时才使用它们。
- (4)执行浮点数运算选用double。
2.类型转换(部分重点):
- 赋给无符号类型一个超出其表示范围的值时,结果是初始值对无符号类型表示数值总数取模后的余数。例如:8比特大小的unsigned char 可以表示0至255区间内的值,如果我们赋了一个区间以外的值,则实际的结果是该值对256取模后所得的余数。因此,把-1赋給8比特大小的unsigned char 所得的结果是255.
- 当我们赋給带符号类型一个超出它表示范围的值时,结果时未定义的(undefined)。此时,程序可能继续工作、可能崩溃,也可能生成垃圾数据。
3.含有无符号类型的表达式:
//1.当表达式中既出现无符号数又有int值时,int值会转换成无符号数
unsigned u = 10;
int i = -42;
cout << i + i << endl;//输出-84
cout << i + u << endl;//如果int占32位,输出4294967264
//2.从无符号数减去一个值,无论该值是不是无符号数,都要确保不能为负值
unsigned u1 = 42, u2 = 10;
cout << u1 - u2 << endl;//输出32
cout << u2 - u1 << endl;//结果是取模后的值
//3.当使用无符号数作为循环变量时,注意循环控制条件
4.字面值常量(对应一种数据类型,字面值常量的形式和值决定了它的数据类型):
- 整型和浮点型字面值:形如42、20(整型字面值),3.141519(字符型字面值)
- 字符和字符串字面值:形如'a'(字符字面值),"hello world"(字符串字面值)
- 转义序列(均以反斜线作为开始):
//转义序列
// 换行符\n 横向制表符\t 报警(响铃)符\a
// 纵向制表符\v 退格符\b 双引号\"
// 反斜线\\ 问号\? 单引号\'
// 回车符\r 进纸符\f
cout << '\n';//转到新一行
cout << "\tHi!\n";输出一个制表符,输出"Hi!",转到新一行
//泛化转义序列示例
// \7(响铃) \12(换行符) \40(空格符)
// \0(空字符) \115(字符M) \x4d(字符M)
cout << '\115' << '\n';//输出M,转到新一行
- 指定字面值类型(通过添加下表的前缀和后缀,可以改变字面值的类型):
- 布尔字面值和指针字面值:true和false(布尔字面值),nullptr(指针字面值)
5.变量初始化(对象获得一个特定的值):
- 初始化不是赋值,初始化的含义是创建变量时赋予其一个初始值,而赋值的含义是把对象的当前值查擦除,以一个新值替代
- 列表初始化:用花括号来初始化变量,形如 int units_sold{0};
- 默认初始化:如果定义变量时没有指定初值,则变量被默认初始化
6.extern关键字:
//如果想声明一个变量而非定义它,就在变量前添加关键字extern
extern int i; //声明i而非定义i
int j; //声明并定义j
//extern语句如果包含初始值就不再是声明而是定义
extern double pi = 3.1416; //定义
7.如果函数有可能用到某全局变量,则不宜再定义一个同名的局部变量
int i = 42;
int main()
{
int i = 100;
int j = i;
}
//程序中j的值为100,定义在外部的全局变量被同名局部变量覆盖
8.复合类型:引用和指针
(1)引用:
- 定义引用时,程序将引用和它的初始值绑定在一起
- 引用并非对象(所以不能定义引用的引用),它只是为一个已经存在的对象所起的另外一个名字
- 引用只能绑定对象,而不能与字面值或某个表达式的计算结果绑定在一起
int ival = 1024;
int &refval = ival; //refval指向ival(是ival的另一个名字)
int &refval2; //报错:引用必须被初始化
int refval4 = 10; //错误:引用类型的初始值必须是一个对象
double dval = 3.14;
int &refval5 = dval; //错误:引用的类型要与绑定的对象一致
(2)指针:
- 指针本身是一个对象,允许对指针拷贝和赋值
- 指针无须在定义时赋初值
- 指针的类型与所指对象的类型要匹配
- 使用nullptr初始化指针,得到空指针(0或null也可以初始化指针,但常用nullptr)
- void *指针:一种特殊的指针类型,可以存放任意对象的地址
- 指向指针的指针、指向指针的引用
int ival = 42;
int *p = &ival; //p存放变量ival的地址,或者说p是指向变量ival的指针
int *p1 = nullptr; //使用nullptr初始化指针
double obj = 3.14, *pd = &obj;
void *pv = &obj; //obj可以是任意类型的对象
pv = pd; //pv可以存放任意类型的指针
int ival = 1024;
int *pi = &ival; //pi指向一个int型的数
int **ppi = π //ppi指向一个int型的指针
int i = 42;
int *p;
int *&r = p; //r是一个对指针p的引用
r = &i; //r引用了一个指针,因此给r赋值&i就是令p指向i
9.const限定符
- const对象创建后其值不能改变,所以const对象必须初始化,初始值可以是任意复杂的表达式
- 默认状态下,const对象只在文件内有效,不能多个文件共享;如果想在多个文件之间共享const对象,须在变量定义之前添加extern关键字
- const的引用,若引用绑定在const对象上,则称之为对常量的引用,对常量的引用不能被用作修改它所绑定的值
- 允许为一个常量引用绑定非常量的对象、字面值或一般表达式
- 指向常量的指针不能用于改变其所指对象的值;只能使用指向常量的指针存放常量的地址;常量指针必须初始化
- 顶层const表示指针本身是一个常量(任意的对象是常量),底层const表示指针所指的对象是一个常量
const int i = 42;
const int j; //错误:const常量必须初始化
//file_1.cc定义并初始化了一个常量,该常量能被其它文件访问
extern const int bufsize = fun();
//file_1.h头文件
extern const int bufsize; //与file_1.cc中定义的bufsize是同一个
const int ci = 1024;
const int &r1 = ci;
r1 = 42; //错误:r1是对常量的引用
int &r2 = ci; //错误:试图让一个非常量引用指向一个常量对象
const double pi = 3.14;
double *ptr = π //错误:ptr是一个普通指针
const double *cptr = π //正确:cptr可以指向一个双精度常量
*cptr = 42; //错误:不能给*cptr赋值
const double pi = 3.14159;
const double *const pip = π//pip是一个指向常量对象的常量指针
10.constexpr和常量表达式
- 常量表达式:指值不会改变并且在编译过程就能得到计算结果的表达式
- c++11新标准规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式
- 一个constexpr指针的初始值必须是nullptr或者0,或者是存储于某个固定地址中的对象
const int max_files = 20; //max_files是一个常量表达式
cosnt int limit = max_files + 1; //limit是一个常量表达式
constexpr int mf = 20; //20是常量表达式
constexpr int limit = mf + 1; //mf + 1是常量表达式
constexpr int *q = nullptr; //q是一个指向整数的常量指针
11.处理类型:类型别名、auto类型说明符,decltype类型指示符
- 类型别名:使用关键字typedef、using来定义类型的别名
- auto类型说明符:让编译器通过初始值来推算变量的类型
- decltype类型指示符:选择并返回操作数的数据类型
typedef double wages; //wages是double的同义词
using SI = Sales_item; //SI是Sales_item的同义词
//auto声明语句只能有一种基本数据类型
auto i = 0, *p = &i; //i是整数、p是整型指针
auto sz = 0, pi = 3.14; //错误:sz和pi的类型不一致
auto &h = 42; //错误:不能为非常量引用绑定字面值
const auto &j = 42; //正确:可以为常量引用绑定字面值
decltype(f()) sum = x; //sum的类型就是函数f的返回类型
int i = 42;
decltype((i)) d; //错误:d是int&,必须初始化
decltype(i) e; //正确:e是一个未初始化的int
//切记:decltype((variable))的结果永远是引用,而decltype(variable)的结果只有当variable本身是一个引用时才是引用。
12.部分习题解答:
2.1:
short和int至少16位,long至少32位,long long至少64位;无符号类型只能表示大于0的数,有符号则能表示正数、负数和0;float精度低,double精度高
2.5、2.7、2.8:
熟悉转义序列以及指定字面值类型
2.10:
string global_str; //初值为空字符串
int global_int; //初值为0
int main()
{
int local_int; //初值不确定
string local_str; //初值为空字符串
}
2.17:
int i, &ri = i;
i = 5; ri = 10; //首先将i赋值为5,通过指针将i的值改为10
cout << i << " " << ri << endl; //输出结果10 10
2.18:
#include <iostream>
using namespace std;
int main()
{
int a = 0, b = 1;
int *pi = &a, *pv = pi;
pi = &b; //改变了指针的值
*pv =b; //改变了指针所指对象的值
return 0;
}
2.23:
不能,指针是否有效还未知!!
2.27:
(a)引用r的赋值对象必须是一个对象,不合法
(b)合法,将p2设置为常量指针,初始化为i2对象的地址
(c)合法,将i设置为常量,r设置为常量的引用(const int &r = 0正确,而int &r = 0不正确)
(d)合法,p3为指向常量的常量指针,初始值为i2的地址
(e)合法,p1为指向常量的指针,初始值为i2的地址
(f)使用不合法
(g)合法,正常赋值
2.30:
v2是顶层const;p2是底层const;p3最左是底层,右边是顶层const
其他都不是const
2.31:
顶层const拷贝不受限制,但是底层const的拷贝对象必须具有相同的底层const资格。
2.36:
a是int类型 4
b是int类型 4
c是int类型 4
d是int &类型 4
2.39:
[error] expected ';' after struct definition
2.41、2.42:
//Sales_data.h
struct Sales_data {
std::string bookNo;
unsigned units_sold = 0;
double revenue = 0.0;
};
//Sales_data.cpp
#include<iostream>
#include<string>
#include "Sales_data.h"
using namespace std;
int main()
{
Sales_data data1, data2;
double price ;
cin >> data1.bookNo >> data1.units_sold >> price;
data1.revenue = data1.units_sold * price;
cin >> data2.bookNo >> data2.units_sold >> price;
data2.revenue = data2.units_sold * price;
if (data1.bookNo == data2.bookNo)
{
unsigned totalcnt = data1.units_sold + data2.units_sold;
double totalrevenue = data1.revenue + data2.revenue;
cout << data1.bookNo << " " << totalcnt << " " << totalrevenue << " ";
if (totalcnt != 0)
{
cout << totalrevenue / totalcnt << endl;
}
else
{
cout << " (no sales) " << endl;
}
system("pause");
return 0;
}
else
{
cout << "Data must refer to the same ISBN" << endl;
}
system("pause");
return 0;
}