C++官方参考链接:Variables and types - C++ Tutorials (cplusplus.com)
变量和类型
在前一章中展示的"Hello World"程序的有用性是相当值得怀疑的。我们必须编写几行代码,编译它们,然后执行生成的程序,只是为了获得写在屏幕上的一个简单句子的结果。如果我们自己输入输出句子,肯定会快得多。
然而,编程并不仅仅局限于在屏幕上打印简单的文本。为了更进一步,为了能够编写执行有用任务的程序来节省我们的工作,我们需要引入变量的概念。
让我们想象一下,我让你记住数字5,然后我让你同时记住数字2。你刚刚在你的记忆中存储了两个不同的值(5和2)。现在,如果我让你在我说的第一个数字上加1,你应该在你的记忆中保留数字6(即5+1)和2。然后我们可以,例如,减去这些值,得到4。
上面描述的整个过程是计算机可以用两个变量完成的一个明喻。同样的过程可以用以下语句在C++中表示:
a = 5;
b = 2;
a = a + 1;
result = a - b;
显然,这是一个非常简单的示例,因为我们只使用了两个小整数值,但是考虑到您的计算机可以同时存储数百万个这样的数字,并使用它们进行复杂的数学运算。
现在可以将变量定义为存储值的内存的一部分。
每个变量都需要一个名称来标识它,并将其与其他变量区别开来。例如,在前面的代码中,变量名是a、b和result,但是只要变量是有效的C++标识符,我们就可以用我们能想到的任何名称来调用它们。
标识符
一个有效的标识符是一个或多个字母,数字或下划线字符(_)的序列。空格,标点符号和符号不能成为标识符的一部分。此外,标识符应该总是以字母开头。它们也可以以下划线字符(_)开始,但是在大多数情况下,这样的标识符被认为是保留给编译器特定的关键字或外部标识符,以及在任何地方包含两个连续下划线字符的标识符。在任何情况下都不能以数字开头。
C++使用许多关键字来标识操作和数据描述;因此,程序员创建的标识符不能与这些关键字匹配。不能用于程序员创建的标识符的标准保留关键字是:
alignas, alignof, and, and_eq, asm, auto, bitand, bitor, bool, break, case, catch, char, char16_t, char32_t, class, compl,const, constexpr, const_cast, continue, decltype, default, delete, do, double, dynamic_cast, else, enum, explicit, export, extern, false, float, for, friend, goto, if, inline, int, long, mutable, namespace, new, noexcept, not, not_eq, nullptr, operator, or, or_eq, private, protected, public, register, reinterpret_cast, return, short, signed, sizeof, static, static_assert, static_cast, struct, switch, template, this, thread_local, throw, true, try, typedef, typeid, typename,
union, unsigned, using, virtual, void, volatile, wchar_t, while, xor, xor_eq
特定的编译器也可能有其他特定的保留关键字。
非常重要:C++语言是一种“区分大小写”的语言。这意味着用大写字母书写的标识符不等于用小写字母书写的同名标识符。因此,例如,RESULT变量与result变量或Result变量不同。这是三个不同的标识符,分别标识三个不同的变量。
基本数据类型
变量的值以0s和1s的形式存储在计算机内存中未指定的位置。我们的程序不需要知道变量存储的确切位置;它可以简单地通过它的名称来引用它。程序需要知道的是变量中存储的数据类型。存储一个简单的整数与存储一个字母或一个大的浮点数是不一样的;尽管它们都是用0s和1s表示的,但它们的解释方式并不相同,而且在许多情况下,它们并不占用相同的内存数量。
基本数据类型是由代表大多数系统本地支持的基本存储单元的语言直接实现的基本类型。主要可分为:
*字符类型:它们可以表示单个字符,例如'a'或'$'。最基本的类型是char,这是一个单字节字 符。还为更宽的字符提供了其他类型。
*数值整数类型:它们可以存储一个整数值,例如7或1024。它们以不同的大小存在,可以是有 符号的,也可以是无符号的,这取决于它们是否支持负值。
*浮点类型:它们可以表示实数值,如3.14或0.01,具有不同的精度级别,这取决于使用的是三 种浮点类型中的哪一种。
*布尔类型:布尔类型,在C++中称为bool,只能表示两种状态中的一种,true或false。
以下是C++基本类型的完整列表:
Group | Type names* | Notes on size / precision |
---|---|---|
Character types | char | Exactly one byte in size. At least 8 bits. |
char16_t | Not smaller than char . At least 16 bits. | |
char32_t | Not smaller than char16_t . At least 32 bits. | |
wchar_t | Can represent the largest supported character set. | |
Integer types (signed) | signed char | Same size as char . At least 8 bits. |
signed short int | Not smaller than char . At least 16 bits. | |
signed int | Not smaller than short . At least 16 bits. | |
signed long int | Not smaller than int . At least 32 bits. | |
signed long long int | Not smaller than long . At least 64 bits. | |
Integer types (unsigned) | unsigned char | (same size as their signed counterparts) |
unsigned short int | ||
unsigned int | ||
unsigned long int | ||
unsigned long long int | ||
Floating-point types | float | |
double | Precision not less than float | |
long double | Precision not less than double | |
Boolean type | bool | |
Void type | void | no storage |
Null pointer | decltype(nullptr) |
*某些整数类型的名称可以缩写,没有signed和int组件——只有非斜体的部分需要标识该类型,斜体的部分是可选的。也就是说,signed short int可以缩写为signed short、short int或者简写short;它们都具有相同的基本类型。
在上面的每一组中,类型之间的区别只是它们的大小(即它们占用内存的多少):每一组中的第一个类型是最小的,最后一个是最大的,每一种类型至少与同一组中的前一个类型一样大。除此之外,组中的类型具有相同的属性。
注意,在上面的面板中,除了char(其大小正好是一个字节),没有任何基本类型指定了标准大小(但最多是最小大小)。因此,该类型不需要(在许多情况下不是)这个最小大小。这并不是说这些类型的大小不确定,而是说在所有的编译器和机器上没有标准的大小;每个编译器实现都可以指定这些类型的大小,以最适合程序将要运行的架构。这种相当通用的类型大小规范为C++语言提供了很大的灵活性,以适应各种平台的优化工作,包括现在和未来的平台。
上面的类型大小用位表示;一个类型的位越多,它可以表示的不同值就越多,但同时也会消耗更多的内存空间:
Size | Unique representable values | Notes |
---|---|---|
8-bit | 256 | = 2^8 |
16-bit | 65 536 | = 2^16 |
32-bit | 4 294 967 296 | = 2^32 (~4 billion) |
64-bit | 18 446 744 073 709 551 616 | = 2^64 (~18 billion billion) |
对于整型来说,拥有更多可表示的值意味着它们可以表示的值范围更大;例如,一个16位无符号整数可以表示0到65535范围内的65536个不同的值,而它的有符号整数在大多数情况下可以表示-32768到32767之间的值。注意,与无符号类型相比,有符号类型中正数的范围大约减半,这是由于符号使用了16位中的一个;这是范围上相对较小的差异,很少有理由纯粹基于它们所能表示的正值范围来使用无符号类型。
对于浮点类型,大小通过使用更多或更少的位来表示它们的有效位和指数,从而影响它们的精度。
如果不关心类型的大小或精度,则通常选择char、int和double分别表示字符、整数和浮点值。它们各自组中的其他类型只在非常特殊的情况下使用。
特定系统和编译器实现中基本类型的属性可以通过使用numeric_limits类获得(请参阅标准头文件<limits>)。如果由于某种原因需要特定大小的类型,标准库在头文件<cstdint>中定义了特定大小固定的类型别名。
上面描述的类型(字符、整数、浮点和布尔)统称为算术类型。但是还存在另外两种基本类型:void,用于标识类型缺失;nullptr的类型,这是一种指针的特殊类型。这两种类型将在关于指针的下一章中进一步讨论。
C++支持基于上面讨论的基本类型的各种类型;这些其他类型被称为复合数据类型,是C++语言的主要优势之一。我们还将在以后的章节中更详细地看到它们。
变量的声明
C++是一种强类型语言,要求每个变量在第一次使用之前都用它的类型声明。这将告知编译器要在内存中为变量保留的大小以及如何解释其值。在C++中声明新变量的语法很简单:我们只需要在变量名(也就是它的标识符)前面写上类型。例如:
int a;
float mynumber;
这是两个有效的变量声明。第一个声明了一个int类型的变量,标识符是a。第二个声明了一个float类型的变量,标识符是mynumber。一旦声明,变量a和mynumber就可以在程序中它们的其他作用域内使用。
如果声明多个相同类型的变量,可以在一条语句中声明它们,只需用逗号分隔它们的标识符。例如:
int a, b, c;
这声明了三个变量(a、b和c),它们都是int类型,与下面的声明有完全相同的含义:
int a;
int b;
int c;
为了了解程序中变量声明的实际效果,让我们来看看本章开头提出的关于心理记忆的例子的整个C++代码:
// operating with variables
#include <iostream>
using namespace std;
int main ()
{
// declaring variables:
int a, b;
int result;
// process:
a = 5;
b = 2;
a = a + 1;
result = a - b;
// print out the result:
cout << result;
// terminate the program:
return 0;
}
如果除变量声明本身之外的其他东西对您来说有点奇怪,请不要担心。大部分内容将在接下来的章节中进行更详细的解释。
变量的初始化
当声明上面例子中的变量时,它们有一个未确定的值,直到第一次给它们赋值。但是对于一个变量来说,从它被声明的那一刻起就有一个特定的值是可能的。这被称为变量的初始化。
在C++中,有三种初始化变量的方法。它们都是等价的,让人想起语言多年来的演变:
第一个,被称为类C初始化(因为它是从C语言继承来的),包括添加一个等号,后面跟着变量初始化的值:
type identifier = initial_value;
例如,要声明一个名为x的int类型变量,并在声明的同时将其初始化为0,可以这样写:
int x = 0;
第二种方法称为构造函数初始化(由C++语言引入),它将初始值括在圆括号(())之间:
type identifier(initial_value);
例如:
int x(0);
最后,第三种方法,称为统一初始化,与上面类似,但使用花括号({})而不是圆括号(这是2011年C++标准修订版引入的):
type identifier{initial_value};
例如:
int x {0};
在C++中,所有三种初始化变量的方法都是有效且等价的。
// initialization of variables
#include <iostream>
using namespace std;
int main ()
{
int a=5; // initial value: 5
int b(3); // initial value: 3
int c{2}; // initial value: 2
int result; // initial value undetermined
a = a + b;
result = a - c;
cout << result;
return 0;
}
类型推断:auto和decltype
当一个新变量被初始化时,编译器可以通过初始化器自动找出该变量的类型。为此,使用auto作为变量的类型说明符就足够了:
int foo = 0;
auto bar = foo; // the same as: int bar = foo;
在这里,bar被声明为具有一个自动类型;因此,bar的类型是用于初始化它的值的类型:在这种情况下,它使用foo的类型,这是int。
未初始化的变量也可以使用decltype说明符进行类型推导:
int foo = 0;
decltype(foo) bar; // the same as: int bar;
在这里,bar被声明为具有与foo相同的类型。
auto和decltype是最近添加到该语言中的强大特性。但是,它们引入的类型推断特性是用于无法通过其他方式获得类型或使用它可以提高代码可读性的情况。上面的两个例子可能不是这些用例中的任何一个。事实上,它们可能会降低可读性,因为在阅读代码时,必须搜索foo的类型才能知道bar的类型。
介绍string
基本类型表示由运行代码的机器处理的最基本类型。但是C++语言的主要优势之一是它丰富的复合类型集,其中的基本类型仅仅是构建块。
复合类型的一个例子是string类。这种类型的变量能够存储字符序列,如单词或句子。一个非常有用的特性!
与基本数据类型的第一个区别是,为了声明和使用这种类型的对象(变量),程序需要包含在标准库中定义该类型的头文件(头文件<string>):
// my first string
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string mystring;
mystring = "This is a string";
cout << mystring;
return 0;
}
正如您在前面的例子中看到的,string可以用任何有效的字符串字面量初始化,就像数值类型变量可以初始化为任何有效的数字字面量一样。与基本类型一样,所有初始化格式对string都有效:
string mystring = "This is a string";
string mystring ("This is a string");
string mystring {"This is a string"};
string还可以执行基本数据类型可以执行的所有其他基本操作,比如声明时不带初始值,并在执行过程中更改其值:
// my first string
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string mystring;
mystring = "This is the initial string content";
cout << mystring << endl;
mystring = "This is a different string content";
cout << mystring << endl;
return 0;
}
注意:插入endl操纵符结束行(打印换行符并刷新流)。
string类是一个复合类型。正如您在上面的例子中看到的,复合类型的使用方式与基本类型相同:声明变量和初始化变量使用相同的语法。
有关标准C++的string的更多细节,请参见string类参考。