第7章:数据类型——《实践之路》笔记


大多数语言有表达式,对象的类型两个概念

类型为操作提供了隐式上下文环境

如传递给函数类型根据类型重载函数

类型支持实现了一部分接口限制了程序中可执行操作的集合
好的类型捕捉无意义操作导致的错误

7.1类型的意义与作用
7.2类型等价与相容
7.3-9复合类型的语法,语义,实现

7.10对象相等,对象赋值

7.1类型系统
硬件 用不同方式 解释 存储器上的 一组二进制位
解释为:指令地址字面量(字符,数字)
二进制位本身无类型
标识符——地址——值(二进制位)

高级语言将值与类型相联系,提供上下文信息错误检查

类型系统包括:
定义类型,与特定语言结构相关联
一组类型等价,类型相容,类型推理规则

具有类型的结构:有的,或引用值的 结构
结构包括:命名常量,变量,记录域,参数,(有时)函数
及包含这些结构的表达式

类型等价:两个类型相等的条件
类型相容:一个类型支持另一个类型的接口
类型推理:确定表达式的值,部分类型运算后得到的值的类型指定

多态性变量参数的语言中,区分引用的类型  被引用的对象的类型

引用——类型——对象

两两打包

同一名字不同时刻可能引用不同类型对象

不能动态创建子程序 且用静态作用域的语言中,编译器总能确定名字引用的子程序,保证正确调用

7.1.1类型检查
保证程序遵守语言的 类型相容性规则
违背:类型冲突

强类型禁止操作应用不支持操作的对象。不发生强制??
静态类型化:强类型,且所有(退一步:大多数)检查在编译时间执行

检查操作——检查类型

类型的来源:声明,或推理

携带类型信息:引用携带,值携带

C语言漏洞:联合,可变个数参数,指针和数组的互操作性
不做运行时检查


动态类型检查迟约束的一种形式


python强类型化

动态作用域规则:动态类型化
因为编译器不知道名字引用了什么对象,无法知道其类型

7.1.2多态性

参数多态性,子类型多态性


同一代码体可用于多个类型的对象
需要运行时动态检查,对象是否实现了操作

动态类型化 又称支持隐式的参数多态性
运行时开销,推迟错误报告
类型推理系统,通过静态类型化,支持隐式的参数多态性
推断每个对象与表达式的类型
编译器任务:对程序中对象静态确定一种 一致性的类型指派


面向对象中,子类型多态性允许父类型引用子类型
因为派生类要求支持基类的所有操作

子——>转化——>父

简单继承模型编译时实现类型检查
使用这种实现的语言提供了显式的参数多样性泛型
允许程序员定义带类型参数的类
泛型用于集合

python检查推迟到运行时

编译时推断
运行时跟踪


7.1.3 类型的含义

编译时推断
运行时跟踪
除此之外,

声明对象的类型,

声明非内部类型的特征

指称
构造
基于抽象
观点

指称:一个类型是一组值(域)
属于值集合——对象属于类型

构造:类型:由内部类型 迭代 构造出的复合类型

抽象:类型是一个接口,由一组定义良好的相互协调语义操作组成

指称语义学:一组值:一个论域:一个类型
表达式:论域中的一个值
函数:一个存储映射到另一个存储
存储:名字的映射
赋值:赋值前内容映射到赋值后内容

可以基于集合上的数学运算,描述用户定义类型


枚举类型:一组值


7.1.4类型分类

内部类型:处理器硬件支持的类型
整数,字符,布尔,实数

离散类型:可数有序——枚举,子界


标量类型:简单类型


枚举类型:一组命名元素
排序比较前驱后继枚举控制
整数相容性不定

子界:离散基类型的连续子集
编译器分析子界的声明,动态检查越界

复合类型:结构类型:非标量类型

记录:一组域,各域类型笛卡尔积

变体:各个域类型并集

数组:下标元素映射成员类型

集合基类型的数学幂集

指针:左值,直接用地址实现
用于实现递归的数据类型

(链表):没有映射与下标。长度可变

文件:位于大规模存储设备内存之外的数据,
当前位置的概念,在顺序操作隐式使用下标
反映了物理设备固有特性,如顺序访问


7.1.5正交性
高度正交的语言,容易理解,使用,
容易做形式化的推理

C消除语句表达式的差别?提高正交性
语句只为提供值:返回空类型(如函数返回值void

数组元素类型,数组下标多样性:正交性体现

非正交性:数组的长度要求编译时确定

复合文字量聚集值:JS对象字面量
用于静态数据结构初始化/赋值


7.2类型检查
大多数静态类型的语言中,定义对象时必须指定类型
对象出现的上下文也是有类型的(上下文究竟是什么?)

上下文:对象周围环境对对象的要求、期待?作用于对象的规则要求?
语言包含了规则,限制了上下文中可以合法出现的对象类型:上下文文法?

类型相容:正确,弱于类型等价的要求
类型变换,类型强制非变换转换

类型推理:子表达式类型——整体类型

7.2.1类型等价

结构等价:成分相同
名字等价:每个定义的类型只与自身类型相等。新语言的选择


结构等价:低级的,基于实现
潜在区别
域的声明顺序

编译器递归展开定义——类型名序列
递归与基于指针的展开


名字等价:既然定义了两次,则,定义的是不同的类型语义

类型别名
严格名字等价:别名类型互不相同
宽松

严格,宽松的区别——声明定义的区别???
type A=B
严格:这是定义
宽松:这是声明


类型变换转换

期望实参形参类型匹配:相同,相容(转化后相同)

显式类型转换:
1.结构等价,名字不等价
2.不同值集合有交集判断是否为交集情况,否则报错
3.不同的底层表示,处理器用于变换的机器指令。存在信息损失算数溢出

非变换类型转换:临时值
颠覆了语言的类型系统,绕过检查机制
强制上下文相容

7.2.2类型相容
类型等价——上下文相容

运算符:运算对象支持 公共类型,对公共类型操作


实参 相容于 形参
返回值

C 类型双关,指定方式解读二进制位

自动隐式变换:类型强制
强制也可能需要动态类型检查(复合?继承?),或底层表示间变化

强制削弱了语言安全性

C混用数组与指针

趋势:静态类型化,远离类型强制

重载:一个名字,不同对象
上下文信息解析

强制:一个对象,不同类型
匹配规则

文字字面常量:重载的

通用引用类型:可保存任意类型对象的引用
为写出通用的容器

c void*
java c# object

当赋值给特定引用类型对象时,保持类型安全性

通用类型到专门类型的 赋值合法性
更普遍问题——保证任意赋值的合法性

解决方式:

对象具有自描述——面向对象语言

值模型:指针类型与引用对象类型

引用模型:值自带类型

名字——地址——值

类型解释地址中的值


需要动态 方法约束?(为子类选择合适的父类方法继承)

类型强制,产生合适异常

容器类型,保存通用引用类型对象
通用引用类型对象:与对象本身属性无关

没有类型标志的语言
不可能检查赋值时的类型转变
凭借不检查的类型转换

表达式的类型
运算符结果运算符类型(函数签名)
函数调用结果:函数声明返回类型
赋值结果:左值类型

内部运算符——内部数据类型
内部运算符——复合类型

声明对象类型
字面量推断类型
类型不确定——多态

7.3记录(结构),变体(联合)
记录:不同类型,相关数据 打包成整体

c++ struct class 成员可见性区别

域:记录中成员
访问域:.运算符

C++允许结构标记作为类型名
不需要struct前缀
实现为typeof语法糖

嵌套记录域

存储布局
编译器 符号表 保存每个记录类型中 各个域的偏移
访问域:位移寻址,加载/存储

局部对象:基址寄存器:帧指针,位移值:偏移量之

内存空洞
压缩:未对齐:优化空间,降低速度

记录赋值
相等比较空洞一致填充(0) 或按域比较
折中方案:对齐条件排序域
重排的优点:需要频繁访问的域集中在一起:同一缓存行空间局部性
重排只是个实现问题,可以指定对齐方式

with语句
pascal:省略引用

python:添加作用域?

变体(联合):共享内存相同字节
系统程序:同一字节不同时刻不同解释
意图:彼此用于不相关的目的:数据不同时存在


非转换类型强制显式绕过语言的类型系统

继承:代码重用,内存重用类型安全

7.4数组
下标类型成员类型的映射

正交性:
下标可选类型
成员可选类型
成员是否必须同类型

非离散下标:关联数组散列表
字典类型相似

离散的下标:允许连续的内存分配高效
定长:更高效

声明
构造符
字面量赋值

7.4.1语法与操作

[]运算符,返回引用显式左值
否则,get,set

多维数组
数组的数组
切片支持

c:指针与数组之间的集成导致不支持切片?

数组运算

7.4.2维度个数上下界,分配

声明描述形状
静态形状:栈中分配
动态形状:堆

加工不知道形状,或动态改变

加工???
分配空间,生成形状信息支持索引

编译的语言:可以允许上下界动态,通常要求维度个数静态

内情向量运行时 保存形状信息


内情向量
编译期间  符号表  维护每个数组的维度与边界信息
每个记录:域偏移量
如果数组维度数目边界在编译
已知:符号表中找出,计算数组元素的地址
未知:生成代码,运行时从内情向量中查找

内情向量包含:每个维度的下界
除最后一维外的各维度大小最后一维大小=元素类型大小
包含每个维度的上界,如需动态语义检查下标越界
下界可推导出上界,保存上界:避免反复运算

内容在加工时,或维度数目,上下界变化初始化
赋值语句可能需要复制内情向量

栈分配
子程序参数动态形状数组
静态数组——传参给动态数组,生成内情向量


局部数组形状加工时固定
帧栈分为固定大小区可变大小区(保存指针与内情向量)
加工时???

任意时刻改变大小(形状)完全动态
改变的顺序栈的进出顺序不一致
必须在堆中分配

完全动态但维度静态可知
帧栈上放置指针+内情向量

维度也可变
内情向量放在堆上堆的开始处。避免重复复制???

废料回收
退出栈回收堆上分配的内存空间

7.4.3内存布局
行优先列优先
内存布局方式:影响迭代顺序
缓存效率缓存缺失

行指针布局:java

允许不规则数组(字符串数组)
加快访问元素
间接访问/地址计算

连续布局地址计算
寻找计算中的编译时常数
公共表达式
地址计算中的动态部分——部分信息编译已知——转化为静态部分

静态消除检查:特别是枚举控制的

C数组维度上的下标下界为0
数组与指针的互操作性

7.5字符串

换意序列:增加一位,扩充字符集

字符串是一维的,不包含引用


7.6集合
公共类型任意数目
基类型:元类型
位向量:元素个数少
散列表
数组,散列表,树


7.7指针与递归类型

指针:值是引用(对象地址)的变量

指针与递归类型

指向指针的指针

递归类型:包含 本类型对象的引用
用于构造链式结构:表,树,图

引用模型:每个对象都是引用
容易实现递归

值模型:需要指针概念,值为其他对象的引用

指针:高级概念,特定对象的引用
地址:低级概念,内存单元的位置

显式回收:语言实现简单
自动回收:区分废料与活动对象

指针操作:
堆中对象的分配、释放间接操作:访问引用的对象,赋值重载运算符

java:内部对象值模型,自定义对象引用模型

需要相互引用:编译器中符号表的记录 语法树的节点~~

C没有可用于连接数据结构的聚集值写法

分配内存:

c: malloc 库函数,返回void*
c++:new 类型安全

访问指针引用的对象:显式的间接运算符

c指针,数组
无下标数组名自动转换——指针:数组第一个元素
传递数组,传递指针

下标运算基于指针运算定义:自举,语法糖

指针+-整数,指针-指针,比较

加工时需要分配空间!!!!
数组:整个数组空间
指针:一个指针空间

指针:使得两个左值的别名判断变难

声明必须使编译器确定元素大小,等价的,指针引用对象的大小

sizeof字节数
编译时计算,除了可变长数组


悬空引用:活动指针 不再引用合法对象,可能破坏堆栈结构
指向栈内局部变量的指针
多个指针同时指向,一个指针显式回收,剩余指针

指针和被引用对象都可能作为实参传递???后果

废料收集:
消除了检查悬空引用的必要性

引用计数
对象无效:不存在指向它的指针
递归地工作


java:显式创建 作用域存储区域,线程不再访问,整体回收

正确识别指针的位置

只有在语言强类型情况,引用计数工作

无法回收循环引用结构???

追溯式收集:

废料:堆外不可访问的对象

理想:不会再使用的对象

堆外指针出发:递归遍历探查堆,确定有用的东西(标记),清扫

必须找到所有指针块开头放置类型描述符

遍历:栈——无可用空间

  隐式栈:指针反转用于回溯

标记好翻转的指针,每个时刻只有一个指针翻转?

图图图!

停顿并复制

堆分为两个区间:一个区间分配,满,压缩到另一个区间,语义交换

实际使用虚拟空间,不会造成浪费

分代式收集

最动态的是最短命

存活久的先保留着

???废料不再访问,为何保留???

保守式收集

在全局与栈中扫描按字对其的量,分析是否包含堆中地址

看着像指针的,都保留着指向的地址

需要程序员不隐藏指针指针转换为其他类型

不能做压缩???

7.8表

表:
递归定义的结构:
空,
有序对:对象(原子),表

成分 构造
提取 成分

判断
长度
索引
翻转
匹配
应用函数构造新表

7.9文件 IO

交互式IO,依赖顺序

文件:程序地址空间外的存储器OS实现
临时文件:因为内存放不下
持久文件:读写存数据

file数据类型

7.10相等性检测,赋值
赋值需要相等性检测,赋给自身

别名
引用相同(地址相同)
值相同

相等定义:归结于左值右值的区分
比较左值:比较地址
引用同一对象浅比较

比较右值:比较
深比较

赋值
赋值:复制引用
赋值:复制副本,值

复合对象的比较:递归比较

复合类型
类型大小形状
数组大小形状

自定义类型

自定义比较

原文地址:https://www.cnblogs.com/qmcj/p/9108188.html