预备知识:

  • CPU与基本数据类型基础
  • 数、数的表示方式与Endianness

前面已经介绍过在Delphi中,整数、字符、布尔与枚举类型统称为序数类型,它们之间有很大的共性。这几种中,枚举类型是必须要定义的,RTL并没有提供某个默认的基类型。在本文中,为了省去每次都写的麻烦,如无特殊说明,代码开始的位置一般是type段。一般情况下,枚举类型的基本定义方式如下:

TEnumType = ( identifier_0, identifier_1, ..., identifier_n );

其中,identifier_x称为该类型的元素,符合Delphi的标识符命名方式:在ASCII字符集中,以拉丁字母或下划线开头(_),高版本Delphi支持以其它不在ASCII字符集范围内的Unicode字符开头(如汉字);除首字符外,其它字符在首字符的字符集基础上,再加上阿拉伯数字。枚举类型至少要有1个元素,总量上限并不容易测试。在这种声明方式下,元素的序数值从0开始(也就是说第1个元素的实际值是0),后面每个元素比前一个元素的值大1。在已有序数类型TBaseOrdinal的基础上,假设该序数类型的值域范围是M..N,则可以声明以下三种以该类型为基础的类型:

TAlias = TBaseOrdinal; { 别名 }
TRename = type TBaseOrdinal; { 重命名 }
TSubType = m..n; { 子类型:其中 M<=m<=n<=N }

其中第三种声明是序数类型独有的,前两种声明对所有类型都适用。别名与重命名这两种类型非常类似,区别在于:别名类型完全与基础类型相同,换句话说编译器认为并不存在一个全新的类型;而重命名则是相当于一个新的类似,尽管在重命名类型与基类型变量之间的赋值等情况下,两者都可以互相隐式转换,但在按地址传递参数的时候(也就是用var或out修饰参数时,关于参数会在后面讲到),编译器会认为两者类型不匹配而无法通过编译。在接下来介绍第三种声明之前,首先要介绍一个可以对类型名称使用的运算符:SizeOf。

SizeOf是一个编译期运算符,可以对任何类型名称使用,其运算结果是一个非负整数,用于描述该类型变量的声明在内存中所占用的字节数,一般称之为该类型的宽度。所谓编译期运算符,就是在编译器进行编译时,就已经得出该运算的结果,不需要等到程序运行时才执行。这个概念非常重要,因为很多运算符同时是编译期运算符和运行期运算符,在不同的情况下使用,表现出来的行为不同。例如整数运算,如果该表达式的全部项都是在编译期就能确定值的(如立即数和编译期常量),那么编译器可以直接算出该表达式的值,而不用等到运行期再去计算。

除了Int64/UInt64的宽度是8字节外,其它序数类型的宽度只能是1、2或4字节。对于非枚举类型的序数类型,以及在默认编译条件下的枚举类型,其宽度采用能够容纳下该类型的最小字节数。例如有一个包含300个元素的枚举类型,由于其最小值是0,最大值是299,一个字节最多只能容纳0..255显然不行,所以其结果是两个字节;再比如一个整数的子类型,定义的值域是-3..120,由于一个字节可以表示-128..127范围内的数,所以它的宽度会是1字节。对于整数类型,这个结果很容易判断,但也要注意:枚举类型同样是序数类型,它的宽度也同样由这个原则确定。再300个元素的枚举类型举例,假如声明一个子类型,上下限分别为标号200与20号的元素,也就是说它的值域在19..199之间,所以宽度应该是1字节;假如是第260到第270号元素,值域就是259..269,超过了一个字节能够容纳下的范围,因此宽度是2。这段内容中强调了“默认编译条件下”的枚举类型宽度,当然也就意味着有办法改变其宽度,但这里并不打算介绍,因为改变编译条件之后的行为会更复杂,初学者不易消化。

为了避免初学者的混淆,本文将只介绍单纯与类型有关的东西,而不会涉及变量。接下来要讲的,是其它对类型使用的运算符,这其中很大一部分运算符也可用于变量,但行为会更复杂一些,所以请不要将其对类型的运算与对变量的运算弄混。需要注意的是,针对类型的运算符全部是编译期运算符,在编译期就已经确定值了。

首先要介绍的,是一个只能对类型使用的运算符:TypeInfo。这个运算符几乎可以对任何类型(不限于序数类型)使用,用于返回一个该类型的信息地址(现在我们还暂时不会讲它,因为使用起来相对比较复杂,这里只作为了解性知识介绍一下),也就是一个指向无类型的指针;但它无法对某些情况下的枚举类型使用,否则会返回一个编译器错误。包括哪些情况下的枚举类型呢?从Delphi6开始,引入了一个新的枚举类型的声明语法,即可以在声明枚举类型时,指定元素的整数值:

TEnumType = ( ... identifier_n = x, ... ); { x必须为整数 }

这样有一些好处,例如枚举类型首元素的值可以不必从0开始,比如从1开始或者从-2开始等。接下来元素的值的规则与前面类似,如果未指定一个值的话,则比前一个元素增加1。不过要注意的是,并不是所有这种形式声明的枚举类型都无法进行TypeInfo运算,而只是以下情况:当指定某个元素的值时,与其自然序号的值不同。这句话的意思举例来说就是:假如有一个10个元素的枚举类型,如果不指定值的话,第5个元素的自然序号值就应该是4;现在如果我们给它显式的指定一个值,并且这个值不是4的情况下,就无法进行TypeInfo运算——反之如果给的值还是4的话,实际上就跟没指定值完全没有差别。为什么会产生这样的结果,我会在后面讲类型信息的时候介绍。就我个人来说,我是不使用指定值的声明方式的,如果要定义一个其它的值(如-1),我会采用编译期常量的方式声明,这样用起来既没有差别,又可以使用TypeInfo——当然这只是个人偏好,在这点上完全可以爱怎么来怎么来(如果是我认为不应该用的方式,我会强调的)。

接下来要介绍的,还是一个只能对类型使用的运算符:default。这个运算符是D2009开始新增的,所以如果使用老版本的话会不支持。对一个类型T使用default运算符(default(T)),其返回结果是一个T类型的编译期常量,该常量的所有字节都是用0初始化的。由于一个序数类型的值域的下限可能会大于0(如Tfoo=2..100),所以返回值可能会不在该类型定义的值域内(0超出了Tfoo的范围)。这个行为看起来似乎有些奇怪,但在以后介绍自动管理生存期的引用类型时,我会谈到这个结果产生的原因。

既然刚刚提到了引用类型,那么现在我就先概念性的介绍一下,题目中提到的“值类型”与刚刚提到的“引用类型”之间的差别。可以认为,在声明一个变量时,就在程序的内存中有了一块空间,用于存放这个变量。例如,声明一个局部变量,则变量在栈(Stack)中,而全局变量在全局变量段中。在Delphi中,有些类型虽然用起来很方便,但是它在声明时实际上只声明了一个指针,实际内容在堆(Heap)中——这种类型就是引用类型(如长字符串、动态数组等)。而像目前介绍过的简单类型,如序数类型和浮点类型,都是值类型,在它们声明的时候,保留的空间直指用于存放其值的数据,而不是一个指针。目前简单了解一下就可以了,以后我会详细介绍的。关于栈、堆、全局变量段,参考《Win32编程基础》一文。

接下来,是一组运算符:LowHigh。这组运算符不仅仅是类型运算符,还能对一些类型的变量使用;在应用于类型时,只能对部分类型进行运算,这里介绍对序数类型的运算结果。在作用于序数类型时,这组运算符返回一个值,分别表示该类型的最大值与最小值,返回值的类型与运算的对象类型相同。举个例子,Low(Boolean)的返回值是False,也就是它的下限的序数值是0、类型是Boolean,所以结果就是False。

目前能想到的类型运算符就只有这些了,以后想起来别的我再往本文里加。

转载于:https://www.cnblogs.com/egust/articles/1797031.html

基本值类型(一):序数类型与运算符相关推荐

  1. C2678 二进制“<“:没有找到接受“ const_Ty“类型的左操作数的运算符

    C2678 二进制"<":没有找到接受" const_Ty"类型的左操作数的运算符 运行c++程序时遇到上述错误,点击VS中的错误后发现无法定位到错误所在 ...

  2. 错误 C2679 二进制“=”: 没有找到接受“const _Ty”类型的右操作数的运算符(或没有可接受的转换)的一种情况发生的错误

    错误 C2679 二进制"=": 没有找到接受"const _Ty"类型的右操作数的运算符(或没有可接受的转换 记录一下自己写代码出现的错误. 问题 写关于复数 ...

  3. C++ 没有找到接受const _Ty类型的左操作数的运算符

    在使用STL中的count(const )算法时报错没有找到接受const _Ty类型的左操作数的运算符, 原因是 使用自己创建的双向链表类没有重载左运算符== ,添加上重载运算即可. bool op ...

  4. C# 编程入门第三课 类型转换,++,关系运算符,bool类型(布尔类型),逻辑运算符,判断闰年,结构

    C# 编程入门第三课 文章目录 C# 编程入门第三课 1. 类型转换 2. ++,-- 3. 关系运算符 4. bool 类型(布尔类型) 5. 逻辑运算符 6.判断闰年 7. 结构 1. 类型转换 ...

  5. 二进制“==”: 没有找到接受“XX”类型的左操作数的运算符(或没有可接受的转换)

    项目场景: 自定义数据结构,进行内存数据的一些遍历等(QList等存储) 问题描述 因为出现==无法找到可接受的转换,那么你一定会像我一样,找==符号,一个项目里==符号那么多,要找到对应的对象==, ...

  6. 【C++】c++编译错误-- C2678 二进制“=”: 没有找到接受“_Ty”类型的左操作数的运算符(或没有可接受的转换)

    [C++]c++编译错误-- C2678 二进制"=": 没有找到接受"_Ty"类型的左操作数的运算符(或没有可接受的转换) 代码: //by 鸟哥 rever ...

  7. c++编译错误-- C2678 二进制“=”: 没有找到接受“_Ty”类型的左操作数的运算符(或没有可接受的转换)

    代码: //by 鸟哥 reverse引起的编译错误 //有疑问请留言或加群 1032082534 #include<iostream> #include<algorithm> ...

  8. Go 学习笔记(27)— type 关键字(类型定义、类型别名、类型查询、定义接口、定义结构体)

    1. 类型别名定义 定义类型别名的写法为: type TypeAlias = Type 类型别名规定: TypeAlias 只是 Type 的别名,本质上 TypeAlias 与 Type 是同一个类 ...

  9. C++中类型转换函数:将当前类的类型转换为其它类型

    1.C++中类型转换函数:将当前类的类型转换为其它类型 转换构造函数能够将其它类型转换为当前类类型(例如将double类型转换为complex类型),但是不能反过来将当前类类型转换为其它类型(例如将c ...

最新文章

  1. ecside 列表排序问题
  2. Go 语言编程 — Overview
  3. KVM虚拟化环境搭建
  4. 连接MYSQL的时候报错(找不到请求的.net framework data provider。可能没有安装
  5. 衡量模块独立性的两个定性标准
  6. python中的scipy基础知识_Python机器学习(五十二)SciPy 基础功能
  7. sersync实时备份安装及设置
  8. baidumap api MySQL_百度地图API获取数据
  9. 计算机高级职称答辩ppt模板,毕业论文答辩PPT模板优秀 范例 11.ppt
  10. 中职 计算机 教案,中职计算机基础教案.doc
  11. 全网最简单Win10桌面美化教程,只需4步!!
  12. hbuilder中uniapp运行微信小程序模拟器时出错
  13. 齐博cms基础教程之认识齐博cms
  14. 标签助手(TagHelper)
  15. Ubuntu Samba高危安全漏洞修复
  16. Access数据库的.ldb文件
  17. 狗是人类最忠诚的伙伴,当狗遇到不同的人会有不同的反应,下面要求编写一个案例模拟狗遇到不同的人时的反应。
  18. SA387Gr22Cl2美标容器板介绍,SA387Gr22Cl2钢板规格8-90mm厚
  19. SVG实现圆形进度条
  20. 无线网络监控分析工具

热门文章

  1. leetcode - 53. 最大子序和
  2. 网络爬虫中Jsoup请求url
  3. MySQL之DML(操作)语句
  4. 设计模式学习笔记——装饰(Decorator)模式
  5. 【实践驱动开发3-001】TI WL1835MODCOM8 在android的移植 - 准备
  6. 北京科技大学大小年计算机,【猛戳】本科一批没录满高校名单(参考2015)!报志愿三步走211分8档,985分4档,全在这!...
  7. pdo mysql下载,yum安装mysql5.7 和 pdo_mysql扩展
  8. python 数学公式识别_Python实现基于KNN算法的笔迹识别功能详解
  9. android第三方推送实现,Android--利用第三方推送实现APP伪保活(小米篇)
  10. 倒序查10条数据_餐饮业总营收增量七成由外卖拉动,天眼查数据显示今年我国新增相关企业超10万家...