数据类型设计_数据的概念
前言
笔者前面做过一个专栏叫"数据类设计",描述了用于界面的几个类型---矩阵图形类和像素图形类的写法.这里将把一些实用的类型设计,以及思路做一个较为精华的专栏.
本贴对数据进行一个简单回顾,从机器层面和高级语言层面做个对比
程序设计中数据的重要性
回顾经典公式"程序=数据结构+算法".算法表达的是一种由处理数据带来的逻辑.算法可以简单理解为"函数",他有两个层面的内容:逻辑层面和物理层面.在逻辑上他给需要表达的逻辑留出接口,使用者传入数据(或者无数据传入),达到自己的目的(可能有返回值也可能没有).在物理层面上,函数最终转化成机器指令,由芯片(底层电子电路)完成函数的执行.函数代表"功能",可以层层封装,直到芯片指令集.指令集的内容简单理解成"分支"---if else;"循环"---for,while;"计算"---加减乘除;"比较"---大于,小于,大于等于,小于等于,等于,不等于.所有的功能都是在这些基本指令上封装而成.是不是有些眼熟,是的,C语言的基础语法里有这些内容.
=============================内容分割线↓===================================
自己开发算法行不行呢?也是可以的.大概你自己写一个函数,如
//伪代码
void do_something(){..... //做一些什么事,产生某些结果
}
别人调用这个函数,会发生一些什么事.---这样说是很抽象,但不管哪一本工具书中,每个说明功能的函数:告诉你传入什么数据,得到什么结果,这些都是封装了指令的函数.
可以试想一下算法开发需要访问指令集或者硬件接口.和指令集打交道,明显要访问内存中某个位置(指令集放在内存中某个地址开始的一片空间中),而与硬件打交道,需调用硬件接口所在的文件.打个比方,C语言标准输出printf函数
printf("Hello World");
这句代码执行后,在IDE(例如Visual Studio)中黑乎乎的输出窗口会出现"Hello World"字符串.如前所述,字符是一张像素图(Pixel_map),字符串本质上是一个像素图的集合.printf函数设计,需要识别每个字符,再到图集中搜索字符对应的像素图,放到输出窗口中.像素图(a,b,c.....)又来自哪里呢?可能来自键盘驱动或者操作系统自带,并加载到内存中备用.
总的来说,算法开发考虑:调用指令集,调用硬件接口,直接访问内存.
以上是笔者个人一些思考,不保证准确和完整.
=============================内容分割线↑===================================
大部分的程序员不用自己开发算法,建立自己的逻辑(写函数)封装已有的函数.而设计数据是主要任务.
数据在底层(硬件)中的表示
笔者前面的帖子多次提到的,硬件层面的数据有两个基本性质:
1.数据包括"地址"和"值"两个部分,地址用于传递值和修改值,值用于表示数据的含义.
2.机器指令(硬件层面的指令)一次处理的数据量有限制:小于或等于一个字长单元.这意味着超过一个字长单元的数据,需要地址+偏移来访问.例如高级语言层面的一个复杂类型数据,或者数据集合,必须被分拆为多个字长单元以内的数据,逐个访问.
因此硬件在访问和处理数据时,关注的有:数据起始地址,数据的大小,指针的偏移量.笔者的一篇帖子https://blog.csdn.net/jllws1/article/details/154231086?spm=1011.2124.3001.6209里有较为详细的讲解.
底层数据和数据类型
本专题的目的是如何设计数据类型,值得一提的是,底层不懂什么是数据类型
硬件不知道什么是数据类型
数据类型是高级语言中定义,方便程序员表达而设立的---当建立两个不同数据类型的对象,写程序的人和看程序的人主观上很容易区别他们是不同的.芯片不认识数据类型,只认识数字.这一点在C语言中有所体现,有句话是:C语言没有严格的类型检查.例如
#include<stdio.h>float caculate(float value) {value += 2;return value;
};int main() {int a = 1;float b=caculate(a);printf("%f\n", b);printf("%d\n", b);
}
运行结果:

将int类型数据a传入float类型形参value,编译过程不会报错.将float类型数据b在printf函数交给%d去解释,也不会报错(虽然结果可能不是预想的那样).C语言不会检查数据类型,而是完全把数据当二进制数看待.这同时意味着C语言程序员需要自己做类型检查,以保证代码正确.
硬件怎样检查数据类型
C语言没有类型检查,但C++有类型检查,那么如何在机器层面实现呢?
从"芯片只认识数字"这个前提去推导,在生成数据对象和传数据给形参的时候,加上一个数字做识别.例如
//C++代码
struct Person{string name;int age;
}Person xl("xiaoli",30); //生成一个叫小李的对象//显示对象的内容
void show(Person ps){cout<<ps.name<<endl;cout<<ps.age<<endl;
}
在机器层面生成对象"小李"的时候,给个编号如17.在函数show编译的时候,给形参ps加个编号17,当调用show函数时,将传入的数据对象和形参类型做对比,如果对不上则编译报错.
由此可以推导出C++的代码编译后比C语言会多一些,数据检查时要占用一些空间.
=============================内容分割线↓===================================
笔者简单分析数据类型如何用数字表示,每一种类型用一个数字来表示,假设基本数据类型如下
| 数据类型 | 类型编号 |
| char | 1 |
| short | 2 |
| int | 3 |
| long | 4 |
| float | 5 |
| double | 6 |
| 基本类型表 | |
对于上述的类型Person,单独用一张表
| 数据类型 | 数量 |
| char | n |
| int | 1 |
| Person类型表(类型编号17) | |
其中n表示指针,即char *类型,若干个字符,当生成对象xl时,n等于7("xiaoli"6个字符加末尾一个空白符),而int数量等于1,表示只有一个int型的数据对象.
尽管笔者不确定底层是不是这样表示,但这种操作是可行的.有<数据结构>作基础,所有的数据都可以用指针表示,反映在表中数量那一栏就是n.生成数据后,n的值将被确定.
若把类型表放在一个地方存储起来,用一个动态数组存储表的元素个数,例如这里的Person是2.那么用一个类型表指针(假设Ptable)指向这个表,当从Person类型想要获得类型编号为18的类型的数据时,指针偏移2.
vector<int> des={.,.,..(前16个类型的元素表达),2(第17个类型的元素个数),..(第18个类型的元素个数)......以此类推}
| Ptable→ | 数据类型 | 数量 |
| 自定义类型前16 | / | / |
| / | / | |
| / | / | |
| / | / | |
| Person类型(17) | char | n |
| int | 1 | |
| 第18种类型 | / | / |
| / | / |
链表表示和二叉树表示:
| 数据类型 | 数量 | 数据类型 | 数量 | |
| char | n | sometype | 1 | |
| int | 1 | Btree | n | |
| Person | n | Btree | n | |
| Person类型表17 | Btree(二叉树类型) | |||
笔者不知道这个n在机器层面该怎么表达:).试想一下用字长单元最大数,比如16位,使用65535,32位使用42亿多那个数.原因是几乎用不到,一个数组如果太长就会使用其他数据结构例如散列结构分层表示,将在后续贴中有所涉及.
类型表扩展作用
类型表还有个作用,计算数据占用空间.例如数据被放入某个文件中,建立一张表填入数据个数(递归到基本数据类型),计算空间作为指针偏移量.假设有17个自定义类型数据被放入一个文件1.data中,每个类型的数据写一张表,以Person为例:
| 序号 | 文件中序号 | 类型1 | 数量 | 类型2 | 数量 | 占用空间 | 序号 | 占据空间 | |
| 1 | 20 | char | 7 | int | 1 | 11 | 1 | 5 | |
| 2 | 30 | char | 2 | int | 5 | 22 | 2 | 8 | |
| Person类型空间占用表 | 文件地址偏移表 | ||||||||
上面右边"文件地址偏移表"由多张左边表组成.当访问某个数据,例如文件中序号20的Person类型数据时,查找左边第1到19序号的占据空间值相加,得到的值是偏移到数据20的字节偏移,交给文件指针相加,即指向准确位置,进而访问到数据20内的数据(7个char和1个int)
以上是发散思考
=============================内容分割线↑===================================
数据在高级语言中的表示
数据在高级语言中必然有数据类型,当生成数据时必然有标识符,也是变量.
高级语言中数据分类
1.基本类型,如int,float,char,short等
2.基本类型的集合,如vector<int>等.
3.复合类型,包括1和2在内的类型,也包括其他复合类型,例如
struct Student{string name; //名称int age; //年龄vector<long> phone_number; //手机号(多个)
}
4.复合类型的集合,如vector<Student>.
注意:第1种基本类型也称为简单数据类型,2,3,4类型都属于复杂数据类型.3和4可以交叉没有界线.
这里面有面向对象,也有数据结构和算法的内容.本专题后续内容重点是在这一部分.
和硬件中数据的联系
变量相当于给"值"取了个名字,方便理解.高级语言中的"取地址"操作也是针对变量.
硬件中的数据是一种物理表达,偏向于空间大小;高级语言中的数据是一种逻辑表达,偏向于理解.
复杂数据类型对象的访问和修改,也必须拆分成多个字长单元对象,逐个访问.程序员的工作大部分是在处理复杂数据类型对象使之满足要求,被戏称为搬砖.其实那一块块砖就是数据.
小结
对计算机"数据"概念的一些理解
