int、long、long long和short都是整型,但它们在c++中标准规定的最小值不同,
类型 | 含义 | 最小尺寸 |
---|---|---|
int | 整型 | 16位 |
long | 短整型 | 32位 |
long long | 长整型 | 64位 |
short | 长整型 | 16位 |
同时编译器也可以赋予这些类型更大的尺寸,某一类型所占比特数不同,表示的数据范围也不同。
整型(除了布尔型和扩展的字符型)可以划分为带符号的(signed)和无符号的(unsigned)两种。
带符号类型可以表示正数、负数和0,其最高位比特为符号位。
无符号类型仅能表示大于等于0的数,但其所有比特都用来存储数值。
float、double都是浮点数
类型 | 含义 | 最小尺寸 | 有效位数 |
---|---|---|---|
float | 单精度浮点数 | 32位 | 7个 |
double | 双精度浮点数 | 64位 | 16个 |
利率,本金和付款都用double型
因为它们既可能是整数,也可能是实数,long double提供的精度在这种情况是没有必要的,也造成了运行时的消耗,float通常精度不够,而float和double的计算代价相差无几,因此选择double。
#include
int main()
{
unsigned u = 10, u2 = 42;
std::cout << u2 - u << std::endl; //32
std::cout << u - u2 << std::endl; //4,294,967,264
//模为2^32=4294967296
//但u-u2=-32,是一个负数,我们需要将它转化为一个取模之后的值
//取模后为4394967296-32=4294967264
int i = 10, i2 = 42;
std::cout << i2 - i << std::endl; //32
std::cout << i - i2 << std::endl; //-32
std::cout << i - u << std::endl; //0
std::cout << u - i << std::endl; //0
}
把负数转换成无符号数类似于直接给无法好书赋一个负值,结果等于这个负数加上无符号数模。
当从无符号数中减去一个值,无论这个值是不是无符号数,都必须确保结果不能是一个负值。
字面值 | 数据类型 |
---|---|
'a' | 字符字面值 |
L'a' | 宽字符字面值(wchar_t) |
"a" | 字符串字面值 |
L"a" | 宽字符字符串字面值(wchar_t) |
'a'表示的就是单独的字符a,字符串"a"表示了一个字符的数组,该数组包含两个字符:一个是字母a,一个是空字符。
字面值 | 数据类型 |
---|---|
10 | 十进制整型字面值 |
10u | 无符号十进制整型字面值 |
10L | 十进制长整型字面值 |
10uL | 无符号十进制长整型字面值 |
012 | 八进制整型字面值 |
0xC | 十六进制整形字面值 |
字面值 | 数据类型 |
---|---|
3.14 | 浮点型字面值 (double) |
3.14f | 单精度浮点型字面值(float) |
3.14L | 扩展精度浮点型字面值 (long double) |
#include
int main()
{
int month = 9, day = 7;
//month,day都被定义为整型并被初始化,分别为9和7
int month = 09, day = 07;
//month,day都被定义为整型
//字面值以0开头表示是八进制整型字面值,而09却不符合八进制数,非法
}
字面值 | 含义 | 数据类型 |
---|---|---|
"Who goes with F\145rgus?\012" | \145表示e \012表示换行符 | 字符串字面值 |
3.14e1L | 扩展精度浮点型字面值 | long double |
1024f | 单精度浮点型字面值 | float |
3.14L | 扩展精度浮点型字面值 | long double |
#include
int main()
{
std::cout << "2\115\n"; //输出2M
std::cout << "2\t\115\n"; //输出2 M
}
#include
int main()
{
std::cin >> int input_value;
// >>运算符的作用是接受一个istream作为左侧运算对象,接受一个对象作为右侧运算对象
//从istream中读入数据,存入到给定对象中
//此语句右侧对象是未定义的,而且int是内置类型名无法作为标识符
//应先定义再赋值,为了使程序可靠,应初始化每一个内置类型的变量
//可改为
int input_value = 0;
std::cin >> input_value;
int i = { 3.14 };
//从double收缩转换到int型,列表初始化存在丢失信息的风险,编译器报错
//可改为
double i = { 3.14 };
double salary = wage = 9999.99;
//wage未定义,应先定义再赋值
//可改为
double wage = 0;
double salary = wage = 9999.99;
int i = 3.14;
//自动类型转换,丢失了部分值
//可改为
double i = { 3.14 };
}
#include
std::string global_str; //global_str非显式地初始化为一个空串
int global_int; //global_int定义在函数体外部,被初始化为0
int main()
{
int local_int; //local_int为int型 定义在函数体内部的内置函数不被初始化,报错
std::string local_str; //local_str非显式地初始化为一个空串
}
(a) extern int ix = 1024;
是定义,任何包含了初始化的声明即成为定义。
(b) int iy;
声明并定义
(c) extern int iz;
是声明
int main()
{
int double = 3.14; //非法
int _; //合法
int catch - 22; //非法
int 1_or_2 = 1; //非法
double Double = 3.14; //合法
}
int i = 42;
int main()
{
int i = 100;
int j = i;
}
j的值为100,因为给j赋值的i时局部变量,而非全局变量,因此赋值为100。
#include
int main()
{
int i = 100, sum = 0;
//当i=10时循环结束
for (int i = 0; i != 10; i++)
sum += i; //由于i被定义在循环里,因此是局部变量,作用域只在循环内部
//sum计算的是由0到9的整数和
//循环结束后,循环内部定义的i不能再访问,因此输出的i值为函数开头定义的100
std::cout << i << " " << sum << std::endl;
}
//输出 100 45
int main()
{
int ival = 1.01; //合法
int &rvall = 1.01; //不合法
//引用类型的初始值必须是一个对象
int &rval2 = ival; //合法
int &rval3; //不合法
//引用必须被初始化,而且和它的初始值绑定在一起
}
int main()
{
int i = 0, &r1 = i;
double d = 0, &r2 = d;
r2 = 3.14159; //合法,把3.14159赋给了d
r2 = r1; //合法,把与r1绑定的i的值赋给了d
i = r2; //合法,把与r2绑定的d的值赋给了i
r1 = d; //合法,把d的值赋给了与r1绑定的对象i
}
#include
int main()
{
int i, &ri = i;
//i赋值为5
i = 5;
//把10赋给与ri绑定的对象i
ri = 10;
std::cout << i << " " << ri << std::endl;
}
//输出 10 10
#include
int main()
{
int val = 1, val2 = 0, * p = &val;
//将val2的地址赋给指针p
p = &val2;
//解引用p,将5赋给val2
*p = 5;
std::cout << val << " " << val2 << std::endl;
}
//输出 1 5
引用本身不是一个对象,不允许初始化后令引用再绑定到另外一个对象,而指针本身就是一个对象,允许对指针赋值和拷贝,在生命周期内可以先后指向几个不同的对象。
引用必须初始化,指针无须在定义时赋初值,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值。
#include
int main()
{
int i = 42;
//p1是指向整型变量i的指针
int* p1 = &i;
//解引用p1得到i的值,将其平方后的值重新赋给i
*p1 = *p1 * *p1;
std::cout << i << std::endl;
}
//输出 1764
int main()
{
int i = 0;
double* dp = &i; //试图把int型对象的地址赋给double型指针
int* ip = i; //不能把int变量直接赋给指针
int* p = &i; //合法
}
if (p) //如果指针的值是0,条件的值为false,若是非0指针,条件的值为true
if (*p) //如果指针所指向的对象的值为0,条件的值为false,如果指针所指向的对象的值为非0,条件的值为true
不能,如果在定义指针时未初始化,p指向的地址未知。
应尽量等定义了对象之后再定义它的指针,也可以把指针初始化为nullptr或0。
int main()
{
int i = 42;
void* p = &i;
long* lp = &i;
}
void*能存放任意类型对象的地址
lp是long类型的指针,不能存放int型对象的地址
int main()
{
int* ip, i, & r = i;
//ip是一个int型指针,i是一个int型变量,r是一个引用,与i绑定在一起
int i, * ip = 0;
//i是一个int型变量,ip是一个int型指针,值为0
int* ip, ip2;
//ip是一个int型指针,ip2是一个int型变量
}
int main()
{
const int buf; //不合法,const对象必须初始化
int cnt = 0; //合法
const int sz = cnt; //合法,cnt的值被拷贝给了sz
++cnt; //合法
++sz; //不合法,sz是一个const类型的对象,不能改变自身内容
}
int main()
{
int i = -1, & r = 0; //非法,r是一个非常量引用,初始值必须是一个对象
int* const p2 = &i2; //合法,p2是一个常量指针
const int i = -1, & r = 0;//合法,i是一个int型常量,r是一个常量引用,允许绑定字面值0
const int* const p3 = &i2;//合法,p3是一个指向常量对象的常量指针
const int* p1 = &i2; //合法,p1是一个指向常量对象的指针
const int& const r2; //非法,r2是一个常量引用,引用不是对象
const int i2 = i, & r = i;//合法,i2是一个常量,r是一个常量引用,与i绑定
}
int main()
{
int i, * const cp; //非法,i是整型变量,cp是一个常量指针,必须初始化
int* p1, * const p2; //非法,p1是一个指向int型的指针,p2是一个常量指针,必须初始化
const int ic, & r = ic; //非法,ic是一个未经初始化的常量,r是一个常量引用
const int* const p3; //非法,p3是一个指向常量对象的常量指针,必须初始化
const int* p; //合法,p是一个指向常量的指针
}
i = ic; 合法,把常量ic的值赋给i
p1 = p3; 非法,指向常量的指针不能赋值给普通指针
p1 = ⁣ 非法,指向常量的指针不能赋值给普通指针
p3 = ⁣ 非法,常量指针不能被赋值
p2 = p1; 非法,常量指针不能被赋值
ic = *p3; 非法,常量不能被赋值
int main()
{
const int v2 = 0;
//v2被声明成了顶层const
int v1 = v2;
int* p1 = &v1, & r1 = v1;
const int* p2 = &v2, * const p3 = &i, & r2 = v2;
//p2被声明成了底层const,r2被声明成了底层const
//p3被声明成了既有顶层const又有底层const
}
r1 = v2; 合法
p1 = p2; 非法,p2包含底层const的定义,但p1没有
p2 = p1; 合法,int*能转换成const int*
p1 = p3; 非法,p3包含底层const的定义,但p1没有
p2 = p3; 合法,p2和p3都是底层const
int main()
{
int null = 0, * p = null;
//非法:不能把int变量直接赋给指针
//改为:
int null = 0, * p = nullptr;
}
int main()
{
int i = 0, & r = i;
const int ci = i, & cr = ci;
auto a = r; //a是一个整数
auto b = ci; //b是一个整数
auto c = cr; //c是一个整数
auto d = &i; //d是一个整型指针
auto e = &ci; //e是一个指向整型常量的指针
auto& g = ci; //g是一个整型常量引用,绑定到ci
a = 42; //a=42
b = 42; //b=42
c = 42; //c=42
d = 42; //error
e = 42; //error
g = 42; //error
}
int main()
{
const int i = 42;
auto j = i;
//j类型为int
const auto& k = i;
//k类型为const int&
auto* p = &i;
//p类型为const int*
const auto j2 = i, & k2 = i;
//j2类型为const int
//k2类型为const int&
}
#include
int main()
{
int a = 3, b = 4;
//a和b都是int
decltype(a) c = a;
//c是int
decltype((b)) d = a;
//d是int&
++c;
++d;
std::cout << a << " " << b << " " << c << " " << d << std::endl;
}
//4 4 4 4
#include
int main()
{
int a = 3, b = 4;
//a,b为int
decltype(a) c = a;
//c为int
decltype(a = b) d = a;
//d为int&
std::cout << a << " " << b << " " << c << " " << d << std::endl;
}
// 3 4 3 3
类型说明符 | 变量 | 引用 |
---|---|---|
auto | 忽略顶层const | 识别引用所指对象类型 |
decltype | 保留顶层const | 识别为引用类型 |
decltype的结果类型与表达式形式密切相关。
如果使用的是一个不加括号的变量,得到的结果是这个变量的类型。
如果使用的变量加上了一层或多层括号,编译器把它当作一个表达式,得到引用类型。
int main()
{
int i = 0;
const int ci = i, & cr = ci;
auto a = i; //a是一个int
decltype (i) x = 0; //x是一个int
auto b = ci; //b是一个int(ci的顶层const特性被忽略)
decltype(ci) y = ci; //y的类型是const int
}
struct Foo { /* 此处为空 */ } //注意:没有分号
int main()
{
return 0;
}
编程题和第一章类似。