第二章 基础决定上层建筑——2.2 左眼看量—2.2.2 变量向右走

时间:2022-11-22 00:18:08

2.2.2 变量向右走

我们从两个角度来看待我们的变量:变量类型&作用域。这两个角度合起来的话,就像一个全景照相机一样,可以把一个变量看的比较透彻了。下面我们先从变量类型的角度出发

角度一看变量——变量类型

通过变量类型呢,我们基本可以把变量分成以下几种来看待(因为我们不是搞理论研究,所以分类不必苛责,只要便于我们理解就行)

第一类——基本数据类型。C++有四种基本数据类型:分别是intcharfloatdoubleOk,看似简单,无数教科书已经前仆后继的讲了若干遍了,为了向全世界显示一下咱们的水平,在这里咱们就不展开说了,说点教科书上没怎么提到的。提到数据类型,我们不能不提长度这件事,一直以来,我们都会不假思索的向世界宣布:int型占两个字节,char一个字节。哦,下回可千万别这么说了,我们需要强调一下:标准c的内部类型(c++继承了它)规范并不说明每个内部类型必须有多少位,规范只说明了内部类型必须能存储的最大值和最小值,so,上面的说法应该纠正为int类型最少占据两个字节。

第二类——bool类型,这是《c++编程思想》上的分类。暂时好像没什么可以说的。蹦过。

第三类——说明符(specifier)。这是配合基本类型用的,它的加入将c++内部数据类型的数量几乎double了一次。怎么说呢,这个东西相当于形容词,一堆干巴巴的名词遇上一堆形容词是不是把语言变得更丰富了?就是这个道理。它们分别是:longshortsignedunsigned。比如说int,当引入说明复制后,我们就可以得到如下的等级(注意,不是大小,因为标准那玩意规定的是范围,范围怎么比较大小?)short intintlong int。也就是说一个int类型必须达到short int这么大,最大大到long int这么大。同样浮点型的大小等级是floatdoublelong double(注意,没有long flaot,也没有short浮点数)。

至于signedunsigned,我们暂时还没有必要死板教条的去理解它们,等日后用到了再说。

第四类——就是复合类型。实际复合类型也没什么神奇的,就是八一队简单类型打个包封装在一起而已。我们接下来讨论一下structenumunion、数组。接下来的内容可能稍微长一点,注意,看的时候不要迷失了,我们是在讲“从变量的类型看变量”,是站在变量的高度总结,而不是喋喋不休的将复合类型怎么用。

1)        Struct,结构体。实际没什么可以大书特书的,我们需要记住两件事情就行了,firststruct里面的变量都是public的,因此可以用‘.’操作符直接访问。Second,注意书写的形式,在c++的世界里面,我们提倡这样来定义一个结构体:

Struct mystructType{

……

}

mystructType mystruct;

c语言的世界里,定义的形式实在是丰富多彩,多彩的都说不清了。

2)      Enumunion。实在没什么好说的了。

 

角度二看变量——量的作用域

一个量,它是有它的作用域的,一般来看呢,我们分成下面这几种作用域。

第一类——全局变量。全局变量是在所有函数体外定义的。程序的所有部分(甚至其它文件)都可以访问它。他没有什么很神奇的声明方式,just写到所有函数外就ok了。好,什么叫“一个文件可以访问另一个文件的global变量”?我们关心的是怎么访问?用extern。看下面:

//modify.cpp

extern int glo;

void modify()

{

    glo = 100;

}

//main.cpp

#include<iostream>

using namespace std;

 

int glo;

 

void modify();

 

int main()

{

    glo = 0;

    cout<<"Before modify:"<<glo<<endl;

    modify();

    cout<<"After modify:"<<glo<<endl;

    return 0;

}

我们在main里面定义了一个全局的变量,叫做glo,接着我们在另外一个文件(modify)里面定义了一个函数(modify)来修改这个量,于是在modify.cpp里面要想引用到glo,就必须采用这样的语法:extern int glo;

第二类——局部变量。局部变量只是在一个作用域内有效,他们局限于一个函数。通常呢,局部变量我们又把他叫做自动变量(automatic variable),很好理解嘛,进入作用域时它自动生成,离开作用域又自动消失,自动化程度很高的说。比如我们声明一个局部变量可以用auto标识,但是没人这么干,因为是默认的。

第三类——静态变量。我们在这里并不想干巴巴的谈论各种特性,只想说明静态变量的两种看似对立,但又非常amazing的特性:全局性V.S局部性。很玄乎嘛,怎么个意思?所谓全局性,就是我们通常理解的,一个static变量它的生命周期是整个程序的生命周期,在其间它不会消失掉,于是给我们一种可以把它当做全局变量用的幻觉。那什么又是所谓的局部性呢?就是说给函数或者全局变量扣上一个static的帽子,它就只能在本文件内被访问,这就叫做“文件作用域”,不信的话,就把上面那个程序清单的glo前面加上个static试试看。怎么样,编译过不去了吧?就是这个意思。他可以帮助我们实现访问控制。

第四类——外部变量。我们在全局变量中简单的用到了一下外部变量这个概念。这里我们详细的来说一下何谓外部变量。为了理解这个所谓的外部变量呢,我们必须对linkage这个概念有所理解。在这里,我们做一个约定:我们写的代码都是基于多文件的(也就是说你的代码不是写在一个文件里面的),并且都是面向过程的写法(没有类、结构等复杂结构)。这样的话呢,势必就涉及到一个问题:他们之间是如何进行通信(交互)的,互相之间怎么访问对方的接口呢?ok,外部变量的概念正是随着这个问题的出现而出现的。如何描述一个对象在不同文件里面的访问方式呢?于是linkage这个概念横空出世,它约定每个个对象都有一个标签,上面写了三种东西:要么是external linkage,要么是internal linkage,要么是no linkage。说到这里我们似乎有点晕了,等等,什么是“对象”,它指针么?ok,所谓的“对象”就是指全局变量和函数名。由于函数的参数、局部变量(自动变量)只是临时存在一个叫做运行时栈(Run-time stack)的地方,因此链接器并看不到他们,所以他们被贴上一个标签,叫做“no linkage”,表示只能在函数内部被访问。当链接器看到一个全局变量或者函数(非静态),它就给这个全局的变量贴上一个标签,叫做“external linkage”,表示它能被任何地方访问,甚至是其他文件的函数。好了,到这里,我们就明朗了:如果我想定义一个全局变量或者函数,那它就被赋予了external linkage的属性,我想要在其他文件中访问它,只需加上extern就可以了(当然函数可以省略extern,因为默认的原因)。如果我定义了一个全局变量或者函数,我不想让其他文件访问到,怎么办?加上static就可以啦,看“第三类——静态变量”。

所以说到这里,要想理解上面的一堆说法,最好的方法就是把前面给出的那个程序各种修改就可以了,比如定义两个同名的全局变量会怎样,不加extern会怎样,定义重名的函数会怎样,加上static再从另一个文件访问会怎样等等等等。

好啦,说到这里,我们就把c++中“量”这个概念理解的差不多一半了,为什么是一半呢?因为我们仅仅是知道了“有哪些量”,我们还不知道“量放在那里”呢。下面我们会有专门的篇幅来详细讨论“我的量到哪里去了”。

【延伸阅读】http://publications.gbdirect.co.uk/c_book/chapter4/linkage.html 讲的是Linkage