【c++】【基础】【primer_plus】【第三章】基本数据类型

变量命名规则

变量由字母、数字、下划线组合而成,不能有空格。

数字不能开头。

下划线开头的一般为系统专用全局变量变量,两个下划线开头的一般给编译器使用。

字母区分大小写。

不能是c/c++语言的关键字。

长度不超过31个字符(c89)标准,c99为63个。

整型

所有用来表示整数的数据类型都称为整型。整型分为有符号整型和无符号整型。其中无符号整型前面需加unsigned关键字。

1
unsigned int a = 10;

长度

整型的长度在不同的操作系统中都有可能不同,所以c++采用了一种灵活的标准。

类型位数
short至少16位
int至少和short一样长
long至少32位, 且至少和int一样长
long long至少64位, 且至少和long一样长

例如其在各个位数的操作系统上的长度如下表所示,其中长度单位为字节(8位二进制位)。

类型16位操作系统32位操作系统64位操作系统
short222
int244
long448
long long888

最值

我们可以使用<climits>头文件获得整型的最值信息。可使用以下宏定义获得各种整型类型的大小。

1
2
3
4
SHRT_MAX SHRT_MIN
INT_MAX INT_MIN
LONG_MAX LONG_MIN
LLONG_MAX LLONG_MIN

我们使用sizeof(int)获得某个类型(这里以int型为例)的字节数。另外,对变量名使用sizeof()函数时,可以不用括号,如sizeof a

我们使用CHAR_BIT宏来获得当前操作系统一个字节占多少位,一般为8位。

选择

在所有的整型类型中,int是最自然的选择了,大部分操作系统的int都是4字节的,而不用专门进行4字节对齐(可参考操作系统相关知识了解字节对齐的好处),处理效率最高。

有时候,short也是不错的选择,如果你能保证你的数据集没有很大的数。如果此时恰好你需要存储特别多的这种不很大的数,使用short无疑会帮你节省大量的内存。

long类型的可移植性比较高,如果你要为数据将来的发展或移植考虑,如将原来在32位上跑的程序移植到64位上来,那么请直接使用long吧。

long long用来处理更大的数值,如果你打过acm竞赛就会发现,有些题目的中间结果(比如在取模之前)不用long long还真的是存不下。

有些时候我们把char类型(字符类型)也归为整型,因为它对应着[0,255]区间内的整数,它只占有一个字节,所以比short更节省内存,在某些特殊的情况下也是最好的选择。

后缀

我们使用后缀来表示一个字面量到底属于哪种类型,没有后缀的一般都为int型。如2L表示long型数值2

具体的整型后缀表如下所示。

类型后缀
longL
unsigned longuL
unsigned long longuLL

进制

一般我们显示的数值为十进制数,但是c++也可以显示八进制、十六进制的数值。

其中八进制的数据我们一般使用0作为前缀,且保证后面的每一位数不能大于7。而十六进制使用0x作为前缀,且后面每一位数可以使用0~9a~f来表示。

1
2
3
4
5
6
7
8
9
// 十进制(decimal)
int a = 23;
cout << dec << a << endl;

// 八进制(hexadecimal)
int b = 046; cout << hex << b << endl;

// 十六进制(octal)
int c = 0xAF; cout << oct << c << endl;

char

char类型为单字节字符,表示某一个字符,它将字符与其ascii码相对应。

char类型默认为unsigned char,取值范围为[0,255]。但是也可显示声明为signed char,取值范围变为[-128, 127]。

下面是一个关于char的示例程序。

1
2
3
4
5
6
7
8
9
10
#include <iostream>
using namespace std;

int main() {
char ch = 'M';
int i = ch;
cout << ch << " - " << i << endl;
cout.put(ch); cout.put('!'); // 不带回车
return 0;
}

其输出结果如下。

1
2
M - 77
M!

转义序列

我们使用转义序列来表示字符或者字符串(可以理解为由字符串起来的句子)中难以表达的字符,比如我们使用\n来表示回车。部分转义字符如下表所示。

字符含义转义序列
换行
回车
水平制表
垂直制表
退格
响铃
问号?
反斜线\\
单引号'
双引号"

c++使用反斜线\作为转义标记。可以想像一下,在c++内部解析一串字符时,当碰到\时,它便会从一张转义字符表中查找有没有与\后面的字符完全吻合的转义序列,如果有的话,则转换其意义之后再输出。这也是为什么看似稀松平常的\在字符串中出现时也需要转义成\\才能输出。

通用字符名

为了可以统一显示世界各地各种千奇百怪闻所未闻的字符,c++采用了一种通用字符名的广宁,它可以显示所有iso10646标准规定的所有unicode字符。其显示方法有以下两种。

4个十六进制位(16位),如\u006E

8个十六进制位(32位), 如\U0000222B

如下面语句。

1
cout << "k\u00E2rper" << endl;  -- k芒rper

宽字符类型

宽字符也是为了显示更多或者更完整的字符,所以一般会使用更多的空间,如2字节、4字节等。

wchar_t

wchar_t类型的字符串以L作为前缀,一般占用2个字节,且有专门的输入输出处理api,如下代码所示。

1
2
wchar_t wch = L 'P' ;           // 使用前缀L表示
wcout << L "hello" << endl; // 使用wcout和wcin代替cout和cin

char16_t和char32_t

chara16_tchar32_t都是宽类型的字符,一个占16位,一个占32位,如下代码所示。

1
2
char16_t ch1 = u'\u00F6';       // 无符号 16位
char32_t ch2 = U'\U0000222B'; // 无符号 32位

bool 型

进行逻辑运算的数据类型。

1
2
bool flag = true;               // 任何非零值
bool ok = false; // 零值

浮点型

浮点型在计算机中用来表示小数,它的存储方式(可参考计算机组成原理相关知识),存储大小,以及书写格式等与整型都存在较大的区别。

书写格式

标准小数点表示法如下所示。

1
2
0.023 
8.0

E表示法如下所示。

1
2
3
4
5
3.45E8
2.52e+5
8.33E-4
7E5
-18.34e24

有效位与取值范围

IEEE754浮点数表示法里,浮点数的存储分为三个部分,如下所示。

逐点类型总位数符号位(决定正负)指数位(决定范围)尾数位(小数部分)
float32(至少32)1823
double64(至少48,不少于float)11152
long double128(不少于double)115112

其中指数位可决定取值范围,而尾数位决定有效数字(精度)。下面以float为例详细说明。

float的指数位为8位,所以指数的范围为[-127,128],则float的取值范围为 \(\[-2^{-128},2^{128}\]\),此取值范围与正整数有关,可认为负整数无限趋近于0.0了。转换为10进制即为\(\[-3.4\times10^{38},3.4\times10^{38}\]\)

float的尾数位为23,则\(2^{23}=8388608\),总共7位,但不能保证所有的7位尾数都是精确的(大于8366608的不精确),所以float的有效数字位数为6~7位

下面为一使用浮点数的示例代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;

int main() {
float a = 10.0 / 3.0;
double b = 10.0 / 3.0;
const float c = 1.0e6;

// 使用定点表示法输出
cout.setf(ios_base::fixed, ios_base::floatfield);
cout << "a = " << a << endl;
cout << "b = " << b << endl;

cout << "a*c = " << a*c << endl; // 精度不一样
cout << "b*c = " << b*c << endl;

cout << "a*c*10 = " << a*c*10 << endl; // 结果不正确
return 0;
}

输出结果如下。

1
2
3
4
5
a = 3.333333
b = 3.333333
a*c = 3333333.250000
b*c = 3333333.333333
a*c*10 = 33333332.000000

浮点后缀

浮点类型后缀示例
floatF/f1.234f 1.234F
double默认类型,无后缀1.234
long doubleL/l1.234L 1.234l

算术运算符

基本运算符

+ 加法。

- 减法。

* 乘法。

/ 除法。两个操作数都是整数时结果才为整数。

% 求模。两个操作数必须是整数。

优先级

先乘除(取模),后加减。

可用括号控制优先级。

读者可自行搜索c++运算符优先级表,后续本站也会更新优先级相关资料。

结合性

结合性意为两个优先级相同的运算符被同时用于一个操作数时,需要考虑是左结合还是右结合。

如下代码所示,会从左往右进行计算,因为对于4而言,除法/和乘法*处于同一优先级,但是左结合性的,所以从左往右计算。

1
120/4*5 = 150;

类型转换

自动类型转换

大范围转小范围(long->int或float->int等)会丢失精度。

初始化时或将一种类型赋值给另一种类型时自动转换。

表达式中包含不同类型时,会出现整型提升,即小范围的类型变为大范围的类型。

参数传递给函数时会发生自动类型转换。

以{}初始化(列表初始化)时,不允许缩窄(float->int),这是c++11的新规定。

强制类型转换

1
2
3
(typename)value;                // C语言格式
typename(value); // C++格式 -- 像函数调用一样
static_cast<typename>(value); // 使用模板函数 -- c++11提供的更严格的转换

auto初探

auto数据类型是c++11新标准定义的数据类型,它可以根据上下文语境自动推断数据类型(如果可以的话)。这在某些时候会给我们写代码时提供极大的便得,甚至实现更强大的功能。

最常见的例子是使用STL(标准模板库,以后会讲到)时迭代元素更加方便,更具有泛型的思想。如下代码所示。此处只作了解,以后会仔细分析auto更详细的用法及原理。

1
2
vector<int> x;
vector<int>::iterator it = x.begin();

可简化为如下代码。

1
2
vector<int> x;
auto it = x.begin();