处理数据

6.6k 词

简单变量

变量名

C++提倡使用有一定含义的变量名。

必须遵循以下几种简单的C++命名规则:

  • 在名称中只能使用字母字符、数字和下划线
  • 名称的第一个不能是数字
  • 不能将C++关键字用作名称
  • 以两个下划线打头或以下划线和大写字母大头的名称被保留给实现使用。以一个下划线开头的名称被保留给实现,用作全局标识符
  • C++对于名称的长度没有限制,名称中所有的字符都有意义,但有些平台有长度限制

如果想用两个或更多的单词组成一个名称,通常的做法是用下划线字符将单词分开,或者从第二个单词开始将每个单词的第一个字母大写。

整型

整数就是没有小数部分的数字。

不同C++整型可能使用不同的内存量来存储整数。使用的内存量越大,可以表示的整数值范围也就越大。

术语宽度用于描述存储整形时使用的内存量。

C++的基本整型char short int long和C++11新增的long long,其中每种类型都有有符号版本和无符号版本。

整型short int long和long long

计算机内存由一些叫作位的单元组成。C++的short、int、long和long long类型通过使用不同数目的位来存储值,最多能够表示四种不同的整数宽度。

C++提供了一种灵活的标准,确保了个整型的最小长度:

  • short至少为16位。
  • int至少与short一样长。
  • long至少32位,且至少与int一样长。
  • long long至少64位,且至少与long一样长。

要知道系统中整数的最大长度,可以在程序中使用C++工具来检查类型的长度。例如我们可以使用sizeof运算符返回类型或变量的长度,单位为字节。我们也可以使用头文件climits,其中包含了整型限制的信息,定义了表示各种限制的符号名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>
#include<climits>


int main()
{
using std::cout;
using std::endl;

int n_int = INT_MAX;
cout << "Maximum value int: " << n_int << endl;

cout << "int is " << sizeof(int) << "bytes." << endl;
}

climits头文件与符号常量

头文件climits定义了符号常量来表示类型的限制。包含与#define INT_MAX 32767(16位int老系统)类似的语句。

其中,#define#include一样,也是一个预处理器编译指令。该编译指令告诉预处理器,在程序中查找INT_MAX,并将所有的INT_MAX替换位32767。

我们也可也使用#define来定义自己的符号常量,然而,C++更偏向使用const创建符号常量。不过在某些头文件中,尤其是可以在C中使用的文件,还是需要使用#define.

初始化方式

除了上述代码的初始化,C++11还允许使用大括号进行单支变量的初始化。

采用这种方式时,可以使用等号,也可以不使用。

1
2
int enums{7};
int rheas = {12};

在大括号内可以不包含任何东西,在这种情况下,变量将被初始化位0.

无符号类型

前面介绍的4种整型都有一种不能存储负数值的无符号变体,其优点是可以增大变量能够存储的最大值。

要创建无符号版本的基本整型,只需使用关键字unsigned来修改声明即可。

下面的程序演示了如何使用无符号类型,并说明试图超越整型的限制时产生的后果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<iostream>
#include<climits>
#define ZERO 0

int main()
{
using std::cout;
using std::endl;

short n = SHRT_MAX;
unsigned short m = n;

cout << n << ' ' << m << endl;

n = n + 1;
m = m + 1;

cout << n << ' ' << m << endl;

n = ZERO;
m = ZERO;

cout << n << ' ' << m << endl;

n -= 1;
m -= 1;

cout << n << ' ' << m << endl;
}

可以看出,整型变量如果超越了限制,其值将为范围另一端的取值。

整型字面值

整型字面值是显式地书写的常量。

C++能够以三种不同的计数方式来书写整数:基数为10、8、16.

  • 如果第一位为1~9,则基数为10.
  • 如果第一位是0,第二位为1~7,则基数为8.
  • 如果前两位为0x或0X,则基数为16.

头文件iostream提供了控制符dec hex和oct,分别用以指示cout以十进制、十六进制和八进制格式显示整数。

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


int main()
{
int a = 16;
int b = 042;
int c = 0x43;

cout << dec;
cout << a << ' ' << b << ' ' << c << endl;
cout << oct;
cout << b << endl;
cout << hex;
cout << c << endl;
}

如何确定常量的类型

程序的声明将特定的整型变量类型告诉了C++编译器,但编译器是如何知道常量的类型呢?

除非有理由存储为其他类型(如使用特殊的后缀指定类型,或值本身超越了限制),否则C++将整型常量存储为int类型。

整数后面的l或L后缀表示该整数为long常量,u或U后缀表示unsigned int常量,ul(可以采用大小写,任意顺序)表示unsigned long。C++11还提供了ll、ull用以表示long long和unsigned long long.

考虑长度时,在C++中,对十进制整数采用的规则,与十六进制和八进制稍微有些不同。

  • 对于不带后缀的十进制整数,将使用下面几种类型中能够存储该数的最小类型:int、long、long long.
  • 对于不带后缀的十六进制或八进制整数,将使用下面几种类型中能够存储该数的最小类型表示:int、unsigned int、long、unsigned long、long long或unsigned long long. 十六进制常用来表示内存地址,而内存地址是没有符号的。

char类型:字符和小整数

char类型专为存储字符而设计。编程语言通过使用字母的数值编码存储字母。因此,char类型是另一种整型。

1
2
3
4
5
6
7
8
9
10
11
#include<iostream>

int main()
{
using namespace std;

char ch;
cin >> ch;
cout << ch;
return 0;
}

通过查看内存可以知道,77是存储在变量ch中的值,而输入、输出的是M,这是因为cin、cout这些工具完成了转换。

cin和cout的行为都是由变量类型来引导的。

char字面值

在C++中书写字符常量的方式有多种。对于常规字符,最简单的办法是将字符用单引号括起。

然而有些字符不能直接通过键盘输入到程序中,例如回车键,或是被赋予特殊含义的字符,如双引号。

对于这些字符,C++提供了转义序列,表示这些字符。

通用字符名

C++实现支持一个基本的源字符集,即可用来编写源代码的字符集,还有一个基本的执行字符集,包括在程序执行期间可处理的字符。

那些被视作为字母的额外字符也可用于表示字符,C++有一种表示这种特殊字符的机制,独立于任何特定的键盘,使用的是通用字符名。

通用字符名的用法类似于转义序列。通用字符名可以以\u或\U打头,后面是4个十六进制位,\U后面则是8个十六进制位。

wchar_t

程序处理的字符集可能无法用一个8位的字节表示,这种情况,C++的处理有两种方式:

  • 如果大型字符集是实现的基本字符集,则编译器厂商可以将char定义为一个16位的字节或更长的字节。
  • 其次,一种实现可以同时支持一个小型基本字符集和一个较大的拓展字符集。8位char可以表示基本字符集,另一种类型wchar_t可以表示拓展字符集。

对于wchar_t,在iostream提供了wcin和wcout来处理wchar_t.

C++11 新增的类型:char16_t和char32_t

进行字符串编码时,如果有特定长度和符号特征类型,将很有帮助,而wchar_t的长度和符号特征随实现而异。

因此C++11新增char16_t和char32_t,两者都是无序号的,前者长16位,后者则为32位。

bool类型

在计算中,布尔变量可以是true或false. C++将非零值解释为true,将零解释为false.

const限定符

创建常量的通用格式如下:

1
const type name = value;

注意,应在声明中对const进行初始化。

常量被初始化后,编译器将不允许再修改常量的值。

浮点数

书写浮点数

C++有两种书写浮点数的方式:

  • 使用常用的标准小数点表示法
  • E表示法:确保数字以浮点格式村春,即使没有小数点。格式为:±\pmd.ddE±\pmn.

浮点类型

C++有三种浮点类型:float、double和long double. 这些类型是按它们可以表示的有效位数和允许的指数最小范围来描述的。

有效位是指数字中有意义的位。

C和C++对有效位数的要求是:

  • float至少32位。
  • double至少48位,且不少于float。
  • long double至少和double一样多。

在输出浮点数时,通常cout会省略结尾的0. 调用cout.setf()将覆盖这种行为。

浮点常量

在默认情况下,浮点常量通常使用double类型。如果希望常量为float类型,使用f或F后缀;对于long double类型,可使用l或L后缀。

浮点数的优缺点

浮点数的优点在于:

  • 可以表示整数之间的值
  • 由于有缩放因子,它们可以表示的范围大得多

缺点在于浮点运算速度通常比整数运算满,精度会存在降低的情况。

C++算术运算符

C++5种基本算术运算符为:+ - * % /

运算符优先级和结合性

当多个运算符可用于同一个操作数时,C++使用优先级规则来决定首先使用哪个运算符。

算术运算符遵循通常的代数优先级,先乘除,后加减。

当优先级列表不够用,C++将看操作数的结合性是从左到右,还是从右到左。

  • 从左到右的结合性意味着如果两个优先级相同的运算符被同时用于同一个操作数,则首先应用左侧的运算符。
  • 从右到左的结合性则首先应用右侧的运算符。

除法分支

除法运算符的行为取决于操作数的类型。

  • 如果两个操作数都是整数,则C++将执行整数除法。
  • 如果其中有一个(或两个)操作数是浮点值,则小数部分将保留,结果为浮点数。

运算符重载

除法运算符表示了三种不同的运算:int除法、float除法和double除法。使用相同的符号进行多种操作叫作运算符重载。

C++不仅内置一些重载示例,还允许拓展运算符重载。

求模运算符

求模运算符返回整数除法的余数。

类型转换

为解决不同类型的数据进行运算可能出现的混乱状态,C++自动执行很多类型转换:

  • 将一种算术类型的值赋给另一种算术类型的变量时,C++将对值进行转换。
  • 表达式中包含不同的类型时,C++将对值进行转换。
  • 将参数传递给函数时,C++将对值进行转换。

初始化与赋值进行的转换

C++允许将一种类型的值赋给另一种类型的变量。这样做时,值将被转换为接收变量的类型。

将一个值赋给取值范围更大的类型通常不会导致什么问题。然而将一个很大的long值赋给float变量将降低精度。

因此有些转换时安全的,有些则会带来麻烦。

  • 将较大的浮点数类型转为较小的浮点数类型:精度降低,值可能超出目标类型的取值范围,在这种情况下,结果将是不确定的。
  • 将浮点类型转换为整型:小数部分丢失,原来的值可能超出目标类型的取值范围,在这种情况下,结果是不确定的。
  • 将较大的整型转换为较小的整型:原来的值可能超过目标类型的取值范围,通常只复制右边的字节。

以{}方式初始化时进行的转换(C++11)

C++11将使用大括号的初始化称为列表初始化。

这种初始化方式对类型转换的要求更严格,它不允许缩窄。

表达式中的转换

当同一个表达式中包含两种不同的算术类型,C++将执行两种自动转换:

  • 一些类型在出现时便会自动转换,
  • 有些类型在与其他类型同时出现在表达式中将被自动转换。

在计算表达式中,C++将bool、char、unsigned char、signed char和short值转为int.

将不同类型进行算术运算时,也会进行转换:

  1. 如果有一个操作数的类型是long double,则将另一个操作数改为long double.
  2. 否则,如果有一个操作数的类型是double,则将另一个操作数转换为double.
  3. 否则,如果有一个操作数的类型是float,则将另一个操作数转换为float.
  4. 否则,说明操作数都是整数,因此执行整型提升。
  5. 在这种情况下,如果两个操作数都是有符号或无符号的,且其中一个操作数的级别比另一个低,则转换为级别更高的类型
  6. 如果一个操作数是有符号的,另一个操作数是无符号的,且无符号操作数级别比有符号操作数级别高,则将无符号操作数转换为有符号操作数所属的类型。
  7. 否则,将两个操作数都转换为有符号类型的无符号版本。

传递参数时的转换

传递参数的类型转换通常由C++函数原型控制。(虽然可以取消原型对参数传递的控制,但这并不明智)

强制类型转换

C++还允许强制类型转换机制显式地进行类型转换。

强制转换的通用格式如下:

1
2
(typeName) value
typeName (value)

第一种格式来自C,第二种格式是纯粹的C++。新格式的想法是,要让强制类型转换就像是函数调用。

C++还引入了4个强制类型转换运算,对它们的使用要求更加严格。

其中static_cast<>可用于将值从一种数值类型转换为另一种数值类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<iostream>

int main()
{
using std::cout;
using std::endl;

int a, b, c;
a = 11.99 + 19.99;
b = int(11.99) + int(19.99);
c = (int)11.99 + (int)19.99;

cout << a << ' ' << b << ' ' << c << endl;

char ch = 'M';
cout << ch << ' ' << int(ch) << ' ' << static_cast<int>(ch) << endl;
}

C++11中的auto声明

C++11新增了一个工具,让编译器能够根据初始值的类型判断变量的类型。

auto是一个C语言关键字,在之前很少使用,也不适合简单的声明情况。例如下面的程序将z声明为int,而往往编写者希望声明为double.

1
2
3
auto x = 0.0;
double y = 0;
auto z = 0;

但是在处理复杂类型,如STL中的类别时,自动类型的推断优势才能显现出来。

1
2
3
4
   std::vector<double> scores;
std::vector<double>::iterator pv = scores.begin(); //C\+\+98

auto pv = scores.begin(); // C\+\+11
留言