变量和类型
上一章中显示的“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变量不同。这些是标识三个不同变量的三个不同标识符。
☞ 基本数据类型
变量的值以 0 和 1 的形式存储在计算机内存中某个未指定位置的某处。
我们的程序不需要知道变量存储的确切位置;它可以简单地通过它的名字来引用它。
程序需要注意的是存储在变量中的数据类型。
存储一个简单的整数和存储一个字母或一个大的浮点数是不一样的;
尽管它们都使用 0 和 1 表示,但它们的解释方式不同,
而且在许多情况下,它们占用的内存量不同。
基本数据类型是由语言直接实现的基本类型,代表大多数系统支持的基本存储单元。它们主要可以分为:
-
字符类型:它们可以表示单个字符,例如'A'或'$'。最基本的类型是char,它是一个单字节字符。还为更宽的字符提供了其他类型。
-
数字整数类型:它们可以存储整数值,例如7或1024。它们以各种大小存在,可以是有符号或无符号的,具体取决于它们是否支持负值.
-
浮点类型:它们可以表示实数值,例如3.14或0.01,具有不同的精度级别,具体取决于使用三种浮点类型中的哪一种.
-
布尔类型:布尔类型,在 C++ 中称为bool,只能表示两种状态之一,true或false。
这是 C++ 中基本类型的完整列表:
Group |
类型名称* |
关于大小/精度的说明 |
字符类型 |
char |
大小正好是一个字节 ,至少 8 位 |
char16_t |
不小于char ,至少 16 位。 |
char32_t |
不小于char16_t ,至少 32 位 |
wchar_t |
可以表示支持的最大字符 |
整数类型(有符号) |
signed char |
大小和char相同 ,至少 8 位 |
signedshortint |
不小于char ,至少 16 位 |
signed int |
不小于short ,至少 16 位 |
signed longint |
不小于int ,至少 32 位 |
signed long longint |
不小于long ,至少 64 位 |
整数类型(无符号 |
unsigned char |
与他们相应的signed型大小相同 |
unsigned short int |
unsignedint |
unsigned longint |
unsigned long long int |
浮点类型 |
float |
|
double |
精度不低于 float |
long double |
精度不低于 double |
布尔型 |
bool |
|
空型 |
void |
没有存储 |
空指针 |
decltype(nullptr) |
|
* 某些整数类型的名称可以省略其signed和int部分: 仅需要非斜体部分来标识类型,斜体部分是可选的.
即,signed short int 可以缩写为signed short, short int , 或简称为short; 它们都表示相同的类型。
在上面的每一组中,类型之间的区别仅在于它们的大小(即它们在内存中的占用量):
每组中的第一个类型最小,最后一个类型最大,每种类型大于或等于同一组中它前面的类型。
除此之外,组中的类型具有相同的属性。
请注意,在上面的表格中,除了char(它的大小正好是一个字节),
没有一个基本类型指定了标准值(但有一个最小值)。
因此,这些类型不需要(并且在许多情况下不需要)精确最小值。
但是这并不意味着这些类型的大小是不确定的,因为所有编译器和平台都不是统一的;
每个编译器都可以指定这些类型的大小,以最适合程序将要运行的架构。
这种相当通用的类型大小规范为 C++ 语言提供了很大的灵活性,可以适应各种平台,无论是现在还是未来.
上面的类型大小以位表示;一个类型的位数越大,它可以表示的值就越大,但同时也占用更多的内存空间:
Size |
可表示的值的数量 |
说明 |
8-bit |
256 |
=28 |
16-bit |
6,5536 |
=216 |
32-bit |
42,9496,7296(~40亿) |
=232 |
64-bit |
1844,6744,0737,0955,1616 |
=264 |
对于整数类型,具有更多的可表示值意味着它们可以表示的值范围更大;
例如,一个 16 位无符号整数将能够表示 0 到 65535 范围内的 65536 个不同值,
而它的有符号整数在大多数情况下能够表示介于 -32768 和 32767 之间的值。
与无符号类型相比,有符号类型的正值大约减半,因为其第一位用于符号;这是一个相对较小的范围差异,
很少有理由纯粹基于无符号类型可以表示的正值范围来使用它们。
对于浮点类型,使用多少位用于有效位和指数位会影响他们的精度.
如果不关注类型的大小或精度,那么char,int和double通常分别代表字符,整数和浮点型,
各自组中的其他类型仅在非常特殊的情况下使用。
可以通过使用numeric_limits类实现特定系统和编译器的基本类型的属性(请参阅标准头文件<limits>)。
如果出于某种原因,需要特定大小的类型,标准库在头文件<cstdint>中定义了某些固定大小的类型别名。
上述类型(字符、整数、浮点数和布尔值)统称为算术类型。还有两个特殊的基本类型:
void(表示没有类型);和 nullptr类型(一种特殊类型的指针)。
这两种类型都将在后面关于指针的章节中进一步讨论。
C++ 支持基于上述基本类型的多种类型;这些其他类型称为复合数据类型,这是 C++ 语言的主要优势之一。
我们还将在以后的章节中更详细地了解它们。
☞ 变量的声明
C++ 是一种强类型语言,每个变量在第一次使用之前都要做类型声明。
这会通知编译器在内存中为变量保留的预置其大小以及如何解释其值。
在 C++ 中声明一个新变量的语法很简单:
我们简单地写下类型,后跟变量名(即它的标识符)。例如:
这是两个有效的变量声明。第一个声明一个标识符为a,类型为int的变量。
第二个声明了一个标识符为mynumber,类型为float的变量。
一旦声明,变量a和mynumber就可以在程序的其余范围内使用。
如果声明多个相同类型的变量,则可以通过用逗号分隔它们的标识符在单个语句中声明它们。
例如:
下面声明了三个变量(a,b和c),它们都是int类型,并且具有完全相同的含义:
要了解在程序中的变量声明是什么样的,让我们看一下本章开头提出的有关您的心理记忆的示例的整个 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 %lt;%lt; result;
// terminate the program:
return 0;
}
|
4
|
如果除了变量声明本身之外的其他东西对您来说看起来有点奇怪,
请不要担心。大部分内容将在接下来的章节中更详细地解释。
☞ 变量的初始化
当上面例子中的变量被声明时,它们的值是不确定的,直到它们第一次被赋值。
但是变量从声明的那一刻起就有可能具有特定值。这称为变量的初始化。
在 C++ 中,共有三种初始化变量的方法。它们都是等价的,让人想起多年来语言的演变:
第一个,被称为类c 初始化(因为它是从 C 语言继承的),
变量后面附加一个等号,变量被等号后面的值初始化:
type identifier = initial_value;
例如,声明一个int名为x 并从声明它的同一时刻起将其初始化为零值,我们可以这样写:
第二种方法,称为构造函数初始化(由 C++ 语言引入),将初始值括在括号 ( ())之间:
type identifier (initial_value);
例如:
最后,第三种方法,称为统一初始化,与上述类似,但使用大括号 ( {}) 而不是括号
(这是在 2011 年 C++ 标准的修订版中引入的):
type identifier {initial_value};
例如:
这三种方式在 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;
}
|
6
|
☞ 类型推导:auto 和 decltype
当一个新变量被初始化时,编译器可以通过初始化自动找出变量的类型。
这样,auto用作变量的类型说明符就可以了:
int foo = 0;
auto bar = foo; // the same as: int bar = foo;
|
在这里,bar被声明为auto类型;因为,bar的类型等于初始化它的值的类型:
在这种情况下,它使用foo的类型,即int。
未初始化的变量也可以使用带有说明decltype符的类型推导:
int foo = 0;
decltype(foo) bar; // the same as: int bar;
|
在这里,bar被声明为与foo具有相同的类型。
auto和decltype是最近添加到C++语言中的功能。
但是引入类型推导特性是为了在无法通过其他方式获得类型或使用它提高代码可读性时使用。
上面的两个示例可能都不是恰当的例子。事实上,它们可能降低了可读性,
因为在阅读代码时,人们必须搜索foo的类型才能真正了解bar的类型。
☞ 字符串简介
基本类型表示由运行代码的平台处理的最基本类型。但是,
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;
}
|
This is a string
|
正如你在上面的例子中看到的,字符串可以用任何有效的字符串字符初始化,
就像数字类型变量可以被任何有效的数字字符初始化一样。
与基本类型一样,所有初始化格式都对字符串有效:
string mystring = "This is a string";
string mystring ("This is a string");
string mystring {"This is a 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;
}
|
This is the initial string content
This is a different string content
|
注意:插入endl表示结束这一行(即打印换行符并刷新流)。
string 类是一个复合类型。正如您在上面的示例中所看到的,复合类型的使用方式与基本类型相同:
声明变量和初始化变量的语法相同。
有关标准 C++ 字符串的更多详细信息,请参阅 string类参考。