C++ | 一个简单的变量声明和定义背后复杂的语法概念与存储实现

为了有效地完成数据存储任务,需要对存储器做如下处理:

1 划分:以字节(8个位bit,对应8个开关晶体管,0或1)为单位将存储器划分为一系列的存储单元。

2 编址:以线性方式给每个存储单元赋予唯一的存储地址,可以随机访问。

3 数据存储约定:数据以二进制形式放入存储空间,需要约定数据的存储空间大小、存储空间地址、数据编码和解码方式以及字节排序方式。如一个基本类型需要多字节时,用大头地址还是小头地址做为数据的地址?

3.1 数据的编码和解码方式

I 以补码方式存储整数;

II 通过ASCII编码方式存储字符;

III 以real-4或real-8方式存储浮点数(实数或小数);

3.2 大头还是小头,按理说只要统一约定就好,但是Intel公司的机器多采用小头方式,IBM、Sun等公司采用大头方式。一般情况下,可以不考虑字节排序方式的问题,但是在针对网络应用编写程序时,就必须关注使用哪种字节排序方式,这是因为网络程序通常需要在不同类型的机器之间交换数据。

为什么是8个bit为一个byte做为最小(基本)的存储单元呢?有两个原因使8显得很特殊。首先,8是2的3次方。由于计算机在最底层使用的是二进制位,而每一位只有两个可能的值,所以2的乘方用起来比10的乘方更方便。其次,需要8位(1个字节)才能对一个字符(比如一个英语字母或其他键盘符号)进行编码。

下面的变量声明和定义就暗含了上面全部的内容。

double PriceOfRice;

数据=数据规格(类型)+数据地址(变量名)

数据规格=存储空间大小+编码和解码方式+可接受的运算处理

(一个变量对应两个值:地址值与数据值)

如果数据值也存储的是一个地址值(或解析为地址),则这样的变量称为指针变量

double* p;

1 在内存中开辟一个8个字节的内存单元,可以通过sizeof(double)得到字节数;

2 该段内存单元的地址用PriceOfRice来表示;

3 此时的值是一个随机值,因为每个字节的每个位都是随机的0或1。如此时用代码cout<

另外,PriceOfRice的取名有诸多要求和考究,例如,不能使用关键字,不能用数字开头,如建议能见名知义,通过名字而不是通过注释去了解其含义。

给变量赋值:

PriceOfRice =3.18;

3.18是字面量,属于代码部分,不可寻址,3.18按real-8的编码方案编码(一串01)后放到PriceOfRice对应的内存空间(每个比特或0或1)。

字面量写法:

浮点型字面量也可以写成.46、1.e20、2.3f、123e12、1.e4L、-.34e-2f;

整形字面量:157、-0655、0xFE、-0x1UL;

字符型字面量:'C'、'ab'、'$'、' '、'\x61'、L'中'、L'a';

布尔型字面量:true、false;

字符串型字面量:"C++ programming"、L"abc"、"\x41\x42\x43"(ABC);

除了字符串型字面量,其它字面量都不可寻址,字符串型字面量存储在被分配的内存空间的数据段。

另外,两行代码写到一起:double PriceOfRice =3.18;

表示变量声明、定义、初始化同时完成。

关于初始化的内容,请见:《C++|变量、对象、对象成员初始化的一些细节》。

对于代码:PriceOfRice = PriceOfRice *1.1

表示右边的PriceOfRice表示取数据值,乘以1.1,按double的编码方案编码后放到左边的PriceOfRice对应的内存单元。

C++左值和右值的概念:左值表示一种可以定位的存储空间,右值表示从存储空间中临时读取的数据值。左值可以做右值,右值不能用做左值。

而乘法运算符*则表示了double这种类型可接受的运算处理,如double就能不使用取余运算符%,而指针类型一般不会使用*来做运算(指针一般用其做解运算)。

下面是一个关于整数编码的小实例:

#include

using namespace std;

int main()

{

unsigned long unl = -0x1UL;

unsigned long ul = 0x1UL;

long l = -0x1L;

cout<

cout<

cout<

cout<

cout<

cout<

cout<

//正数的原码、反码、补码都相同

//负数补码:原码的符号位(最高位)不变,其余位取反,然后再加1

//原码:10000000 00000000 00000000 00000001(最高位是符号位)

//反码:11111111 11111111 11111111 11111110(符号位不变,其余取反)

//补码:11111111 11111111 11111111 11111111(反码加1)

system("pause");

return 0;

}

/*

4294967295

1

-1

ffffffff

1

ffffffff

4

*/

另外,double PriceOfRice;写在不同的位置,或者前面有诸如static、external等不同的限定词,决定了其有不同的作用域和存续期,同时也确定了其存储到被分配给程序的一块内存空间中不同的区域(栈区、全局区静态区等)。

变量规定了名字和数据类型的存储空间,执行程序就是促使变量的存储空间状态发生变化,并达到预期状态的。因此,编程时应该能够随时在大脑中描绘出变量相对应的存储空间状态,称为存储空间映像。

下图是关于数据类型、修饰符、长度、编码方案、用于赋值的字面量的一个综述:

如果是数组变量、结构体变量或对象,其存储空间是如何映像的呢?本质也是通过变量名或对象名来确定其对应的内存空间的首地址,通过成员各自的长度或统一分配的长度来映像。

对于数组,就是首元素的地址,通过整数偏移来确定分量的地址。

结构体、对象的数据成员都是集中存储在一起的,确定了首地址,也就能按其成员类型给予的空间大小顺序找到各成员的地址。不像数组通过整数(下标)来偏移,结构体、对象是从结构体、对象名称开始,通过引用数据成员变量名来进行偏移的。

一个结构体变量或对象的成员数据也是顺序存储在一起的,或者是按各自长度存储,或者编译器基于效率因素增加一些额外字节以使存储边界对齐。

(转自头条 作者小智雅汇)

你可能感兴趣的:(C++ | 一个简单的变量声明和定义背后复杂的语法概念与存储实现)