PL真有意思(五):数据类型 (4)

通过前面的类型检查我们可以保证表达式的各各组成部分具有合适的类型,那么这整个表达式的类型是什么来着?其实在大多数的语言中也是比较简单的,算术表达式的类型与运算对象相同、比较表达式总是布尔类型、函数调用的结果在函数头声明、赋值结果就是其左值的类型。在一些特殊的数据类型中,这个问题并不是那么清晰明了,比如子界类型、复合类型。比如下面的子界类型问题(Pascal):

type Atype=0..20; type Btype=10..20; var a: Atype; var b: Btype;

那么a+b什么类型呢???它确实是不能是Atype或者Btype类型,因为它可能的结果是10-40。有人觉得那就新构造一个匿名的子界类型,边界时10到40。实际情况是Pascal给的答案是它的基础类型,也就是整数。

在Pascal中,字符串'abc'的类型是array[1..3] of char、而Ada则认为是一种未完全确定的类型,该类型与任何3个字符数组相容,比如在Ada中'abc' & 'defg'其结果是一个7字符的数组,那么这个7字符数组的类型是array[1..7] of cahr呢还是某一个也是7个字符组成的类型array (weekday) of character呢,更或者是其他任意一个也是包含七个字符数组的另外一个类型。这种情况就必须依赖表达式所处的上下文信息才能推到出来具体的类型来。

记录(结构)与变体(联合)

一些语言中称记录为结构(struct),比如C语言。C++把结构定义为class的一种特殊形式(成员默认全局可见),Java中没有struct的概念,而C#则对struct采用值模型,对class采用引用模型。

语法与运算

一个简单的结构体在C中可以这样定义:

struct element{ char name[2]; int number; double weight; Bool merallic; };

等价于Pascal中的:

type two_chars=packed array [1..2] of char; type element - record name:two_chars; number:integer; weight:real; metallic:Boolean end

记录里面的成员(如name,number...)称为域(field)。在需要引用记录中的域时,大部分语言使用“.”记法形式。比如Pascal中:

var copper:eement; copper.name=6.34;

大部分语言中还允许记录的嵌套定义,比如在Pascal中:

type short_string=packed array[1..30] of char; type ore=record name:short_string; element_yielded:record /*嵌套的记录定义*/ name:two_chars; number:integer; weight:real; metallic:Boolean end end 存储布局及其影响

一个记录的各个域通常被放入内存中的相邻位置。编译器在符号表中保存每个域的偏移量,装载和保存的时候通过基址寄存器和偏移量即可得到域的内存地址。类型element在32位的机器中可能的布局如下:

此处有图

(图在最后面,因为markdown的这个画表格不符合这个要求,又不想引图了,就直接用html写了,会被挤到最后去)

4 byte/32bits  
name(2个字节)   2个字节的空洞  
number(4个字节)  
weight
(8个字节)
 
metallic(1个字节)   3个字节的空洞  

在对结构体的存储布局方案上,如果使用正常排序,结构中的空洞会浪费空间。但是如果通过压缩来节省空间,但是可能很带来很严重的访问时间的代价

数组

数组是最常见也是最重要的复合数据类型。记录用于组合一些不同类型的域在一起;而数组则不同,它们总是同质的。从语义上看,可以把数组想象成从一个下标类型到成员(元素)类型的映射。

有些语言要求下标类型必须是integer,也有许多语言允许任何离散类型作为下标;有些语言要求数组的元素类型只能是标量,而大多数语言则允许任意类型的元素类型。也有一些语言允许非离散类型的下标,这样产生的关联数组只能通过散列表的方式实现,而无法使用高效的连续位置方式存储,比如C++中的map,C#中的Dictionary。在本节中的讨论中我们假定数组的下标是离散的。

语法和操作

大多数的语言都通过数组名后附加下标的方式(圆括号|方括号)来引用数组里的元素。由于圆括号()一般用于界定子程序调用的实际参数,方括号在区分这两种情况则有易读的优势。Fortran的数组用圆括号,是因为当时IBM的打卡片机器上没有方括号

维数、上下界和分配

对于数组的形状在声明中就已经描述,对于这种有静态形状的数组,可以用通常的方式来管理内存:生存期是整个程序的数组使用栈分配,具有更一般的生存期的动态生成数组使用堆分配。但是对于在加工之前不知道其形状的数组,或其形状在执行期间可能改变的数组,存储管理就会更复杂一点。

内情向量

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjysj.html