Learning C++ 之 2.1 基本变量定义,初始化和赋值

时间:2022-09-09 21:00:02

内存地址:

本课程实在1.3节材料的基础之上的。

在之前课程的学习过程中,我们知道变量只是一段存储着基本信息的内存的命名而已。简单回顾一下,电脑中有随机存储器(RAM)供程序使用。当变量定义的时候,一段内存就会分配给该变量。

内存中最小的单位是二进制的0或者1,你可以把二进制考虑成为一个灯的开关,开为1,关为0.没有中间状态。如果你观察一段随意的内存空间,你会发现类似于001010101010这样的数字,或者其他的组合。内存被组织成为顺序的单元之后就被叫做内存地址,也可以简称为地址。类似于街道地址可以让我们很容易找到一个房子,内存地址的作用就是通过该地址找到内存中特殊的一部分。也许令人意外的是,现在的计算机中并不是每一个bit都有一个自己的地址。最小的地址单元叫做byte。现在的标准是一个byte包含8个连续的bit。也许其他古老的计算机可能不是这样,但是在本教材中,我们会统一使用1byte=8bit。

下面的图代表了一段存储地址:

Learning C++ 之 2.1 基本变量定义,初始化和赋值

因为所有在计算机中的数据都是一段连续的bit,我们就需要一个数据类型来描述怎样把相应的内存中的信息转换成为有效信息。你已经看到了一个这样的例子,interger。当我们把变量定义一个整数类型的时候,我们就需要告诉电脑下面连续的存储空间存储的都是interger类型的数据。

当你用一个数据类型定义一个变量的时候,编译器和CPU负责将你的值复制到相应的连续内存空间中去。当你需要返回这部分值的时候,你的数值会从该内存地址处重新组合成你想要的值。

在C++中除了int类型的值之外还有很多其他类型,在后面我们都会学到。

基础数据类型:

C++自带了一些内置的数据类型的支持,这叫做基础数据类型,经常被称作基础类型或者内置数据类型。下面是游戏额基本数据类型的列表,之前你已经学过一些:

Category Types Meaning Example Notes
boolean bool true or false true  
character char, wchar_t, char16_t, char32_t a single ASCII character ‘c’ char16_t, char32_t introduced in C++11
floating point float, double, long double a number with a decimal 3.14159  
integer short, int, long, long long a whole number 64 long long introduced in C99/C++11
void no type void n/a

这一章会详细讨论这些数据类型。

定义一个变量:

在C++基础部分你已经明白了怎么去定义一个变量,

int nVarName; // int is the type, nVarName is the name of the variable

由此可见定义一个变量的步骤如下:

type varName; // type is the type (eg. int), varName is the name of the variable

下面的例子中我们定义5个不同类型的变量:

bool bValue;
char chValue;
int nValue;
float fValue;
double dValue;

void是由自己的特殊用法的,所以void不能定义变量:

void vValue; // won't work, void can't be used as a type for variable definitions

当定义了一个变量之后,你可以给变脸一个初始值,这个叫做变量的初始化。C++支持三种变量初始化的方法:

1.我们可以通过=号来copy值给变量,如下:

int nValue = 5; // copy initialization

注意这里的=号只是语法的一部分,和创建了一个变量之后,进行赋值的=号不同。

2.用一个()直接赋一个值

int nValue(5); // direct initialization

尽管这种方法像是一种函数调用,编译器会跟踪哪一些是函数,哪一些是变量初始化,这样就能解决这个问题了。直接赋值要比通过=号赋值好一些,以来这可以明确区分开这是初始化赋值,再就是当我们后续讨论类的时候,直接赋值的优势更加明显。所以我们建议使用直接赋值的方式。

C++11中统一的初始化

因为C++是不断发展变化的,使用=号赋值或者使用直接赋值的方式都是不满足一些特定情况的赋值的,你不能使用这两种复制方法来赋值一个列表。

为了提供一种统一给所有数据类型赋值的方法,C++提供了一些新的赋值方法,叫做统一赋值(大括号初始化)。

int value{5};
用空括号初始化一个变量,表示给这个变量赋值为0或者NULL,
int value{}; // default initialization to 0

同意初始化有一个附加的好处,即不允许缩小变量的范围。也就是如果你用统一初始化的方法来初始化一个变量不匹配的类型,编译器会报错的。

int value{4.5}; // error: an integer variable can not hold a non-integer value

变量赋值

当给一个变量定义后给一个初始值的话,叫做变量的赋值。

int nValue;
nValue = 5; // copy assignment

未初始化的变量:

一个基础变量定义了之后但是没有初始化,那么就叫做未初始化的变量。在C++中一个未初始化的变量可能是任意值,因为要非常小心该变量的使用。

C++也有很多非基础变量,如指针,结构体,和类。这些里面有的默认被初始化了,有的没有。这个在之后的学习中将会进一步接触。现在我们可以假定所有的变量都是未定义的。

定义多个变量:

可以通过”,“在一个定义语句里面定义多个变量:如下面的两种定义方法是相同的

int a, b;
int a;
int b;

你还可以同时初始化多个变量:

int a = 5, b = 6;
int c(7), d(8);
int e{9}, f{10};

新手往往会犯三个错误,当他们定义变量的时候:

1.在一行里给每一个变量一个变量类型

int a, int b; // wrong (compiler error)
 
int a, b; // correct
2.在一行里面定义不同类型的数据
int a, double b; // wrong (compiler error)
 
int a; double b; // correct (but not recommended)
 
// correct and recommended (easier to read)
int a;
double b;

3.想要给a和b同时赋值:

int a, b = 5; // wrong (a is uninitialized!)
 
int a= 5, b= 5; // correct
在第一种赋值的情况下,a是没有赋初始值的,如果编译器报了错误还好,如果编译器没有上报错误,那么在使用A的过程中很有可能会程序崩溃,或者输出一些奇怪的值。

最好的识别该种赋值的方法是通过直接赋值:

int a, b(5);
int c, d{5};

可以明确的看出b是有初始值的,而a没有。

在哪里定义变量:

老的c语言强制把所有变量的赋值在函数的最前面:

int main()
{
    // all variable up top
    int x;
    int y;
 
    // then code
    std::cout << "Enter a number: ";
    std::cin >> x;
 
    std::cout << "Enter another number: ";
    std::cin >> y;
 
    std::cout << "The sum is: " << x + y << std::endl;
    return 0;
}

这种方式现在已经被废弃了,C++建议变量定义的地方要尽可能的离使用的地方近。

int main()
{
    std::cout << "Enter a number: ";
    int x; // we need x on the next line, so we'll declare it here, as close to its first use as we reasonably can.
    std::cin >> x; // first use of x
 
    std::cout << "Enter another number: ";
    int y; // we don't need y until now, so it gets declared here
    std::cin >> y; // first use of y
 
    std::cout << "The sum is: " << x + y << std::endl;
    return 0;
}

这样会有一些优势:

首先,这样定义会使变量离使用该变量的上下文比较近。如果x已经定在了函数的最前面,我们需要找到使用他的地方才能知道该变量的作用。在使用x的附近定义,可以一目了然地知道该变量的使用过程。

其次,这样定义变量会让我们明确的直到定义之前的代码与该变量无关,是代码结构更加清晰。

最后,这样减少了变量不赋初始值的情况,因为一旦定义,我们就会使用,就会赋值。

多数情况下你可以在使用一个变量前来顶一个变量,但是有时也是不行的。要么不可取(性能原因),要么不可行(因为变量将会销毁,之后又会用到)。我们会在之后的章节谈论该情况。

最后建议定义变量的位置尽可能的离使用它的位置近一些。