• 复合结构的赋值语句理解

    还是从内存映射出发
    服务器君一共花费 16.690 ms 进行了 4 次数据库查询,努力地为您提供了这个页面。
    广告很萌的

    我们前面讲到的赋值语句是最简单的赋值语句。出现在赋值符号左边的只是一个简单的变量名。实际上,能够出现在赋值符号左边的内容远远不止如此。我们下面就来讲解更加复杂的赋值语句——复合结构的赋值。 

    复合结构的赋值

    什么叫做复合结构呢?比如,我们大家一般都用过手机,也应该都知道,手机里面有个联系人名录,里面记录了联系人的姓名、手机、住宅电话、公司电话、备注等信息。在这个例子里面,每个联系人条目就是一个包含了姓名、手机、住宅电话、公司电话、备注等信息的复合结构。

    这个概念不难理解,但是,如果认真详细解释起来,还需要费一大堆口舌。本书不打算在这种简单易懂的概念上浪费口水。读者如果有什么不明白的,可以参考具体语言中的复合结构的概念和定义。比如,在C语言中,复合结构的对应数据类型叫做“structure”(结构)。在C++、Java、Python、Ruby等更加高级的面向对象的语言当中,复合结构的对应数据类型叫做“Class”(类)。

    面向对象是一个非常重要的概念,是命令式编程语言的主流编程模型,后面会加以详细讲述。 

    现在,假设我们有一个叫做contact(联系方式)的复合结构数据,其中包含name、mobile、home_phone、office_phone、memo等属性。我们就可以这样一条条设置contact这个数据的每一个属性。 

    contact.name = “Tom” 
    contact.mobile = “1338978776” 
    contact.home_phone = “8978776” 
    contact.office_phone = contact.home_phone 
    contact.memo = “Tom is a SOHO. He works at home.” 
    

    上述语句中的“.”表示访问复合结构的内部数据。比如,contact.name就表示contact这个复合结构数据中的name属性。这也是高级命令式语言的一种语法惯例。

    从上面的例子中可以看到,复合结构变量的属性的用法和简单变量完全相同。复合结构变量的属性既可以出现在赋值符号的左边,也可以出现在赋值符号的右边。比如,contact.home_phone先是出现在“=”的左边,接着又出现在“=”的右边。

    那么,我们如何在内存结构中理解复合结构呢?首先,我们还是要给内存单元贴上标签。我们想象一下,在一个布满了小格子的大柜子里面,选出一个小格子,然后在上面贴上“contact”这个标签。然后,我们从贴上“contact”的那个小格子开始,根据每个属性的数据宽度(即占用最小内存单元的个数),依次贴上“name”、“mobile”、“home_phone”、“office_home”、“memo”等几个标签。

    在这个例子中,“contact”就相当于内存中的一个基本地址,而那些属性则相当于以基本地址为基础的几个偏移地址。

    当我们访问contact的属性的时候,实际上就相当于访问“contact”基本地址再加上属性偏移地址的那个单元格的内容。比如,contact.name实际上就是contact基本地址加上name属性偏移地址之后的那个单元格的内容。对于复合结构的属性的访问,实际上就是一次内存中的间接寻址。

    当我们定义了一个包含了多个属性的复合结构的时候,实际上就相当于我们自己定义了一套内存结构映射方案。这还不是最简单的情况,复合结构里面还有可能包含复合结构。事实上,复合结构的嵌套层次是没有限制的,可以嵌套到任意深度。因此,我们有可能写出这样的访问深层次属性的代码:contact.address.city.zipcode。

    • 我们需要把复合结构的概念理解道内存结构映射的层次吗?需要。即使你现在不需要,以后早晚也会需要。随着你对编程语言掌握的深入,你早晚需要理解到这个层次。与其到时候费二遍功,还不如现在就一次搞定。

    下面我们看看数组类型的复合结构在内存中的映射。

    数组类型内存映射

    复合结构并非唯一的内存结构映射定义。在命令式语言中,还有一个极为常见的类型——数组类型,同样对内存结构进行了映射。

    数组类型对内存结构的映射是一种十分整齐的映射。我们可以想象一列整整齐齐的单元格,每个单元格的数据宽度完全相等。因此,我们可以通过简单的等距位移来访问其中某一个单元格的内容。事实上,数组正是通过数字下标来访问其中某一个位置的数据的。

    比如,假设我们有一个数组变量array。我们可以想象一列长长的宽度相同的单元格,第一个单元格上贴着一个标签“array”。

    我们想访问array数组中第30个数据。我们就可以这么写,array[30],就可以定位到array数组的第30个数据单元。

    “[]”这样的方括号,表示访问一个数组中的某一个位置。这也是高级命令式语言的一种语法惯例。

    同样,array[30]可以出现在赋值表达式的左边,也可以出现在赋值表达式的右边。比如:

    array[30] = 1 
    array[31] = array[30] 
    

    需要注意的是,在很多命令式语言中,数组下标是从0开始的。因此,如果我们想访问第30个数据单元,很多情况下,我们必须写成array[29]。

    • 综上所述,出现在赋值符号左边的变量,主要就是三种——简单变量、复合结构变量、数组变量。而且,这三种变量都有一个特点,他们都可以唯一定位到内存中的某一个具体位置。这体现了赋值语句的最根本的含义——将一个表达式执行的结果存入到某一个指定的内存单元中去。我们对赋值语句的理解,必须达到这个层次,才能够正确理解随之而来的一系列相关概念。

    另外,在一些更高级别的命令式编程语言(如Python、Ruby等),实现了部分函数式编程语言(Functional Programming Language)的部分特性,如模式匹配(Pattern Match)这样的特性。在这些语言中,有可能出现一次对多个变量同时赋值的赋值语句。比如:

    (a, b) = (1, 2) 
    a, b = 1, 2 
    

    某些情况下,这种语法用起来相当方便。比如,某个复合结构中有多个属性,我们想就把其中一些属性一次性复制到多个变量中的时候,就可以这么写。

    想深入了解这种语法的读者,可以去研究一下函数式编程语言中的模式匹配特性。

    当然,在我个人看来,即使在函数式编程语言中,模式匹配也不是什么核心的概念,只不过是一种简化书写的语法糖。掌握不掌握,都对编程核心概念的理解没有什么本质影响。

    以上讲述的赋值语句都是“显式”赋值语句,即存在明确的赋值符号(=)的赋值语句。除了显式赋值语句之外,还有一种特殊的隐式赋值语句——参数传递。这个在后面小节再做介绍。

更多 推荐条目

Welcome to NowaMagic Academy!

现代魔法 推荐于 2013-02-27 10:23   

本章最新发布
随机专题
  1. [移动开发] Android根基概念Context 8 个条目
  2. [Python程序设计] 从PHP到Python 3 个条目
  3. [软件工程与项目管理] 浏览器的HTML解析器 8 个条目
  4. [计算机算法] 两数交换的各种算法细节 2 个条目
  5. [计算机算法] TAOCP与算法 12 个条目
  6. [移动开发] Android开发基础知识 4 个条目
  7. [PHP程序设计] htaccess 设置技巧 6 个条目
  8. [搜索引擎优化] 百度搜索引擎优化指南 3 个条目
  9. [数据结构] 图的定义 1 个条目
  10. [移动开发] Android加载器Loaders 5 个条目
  11. [计算机算法] 从双端队列引出的卡特兰数 3 个条目
  12. [Python程序设计] Tornado表单处理 3 个条目
窗口 -- [资讯]