计算机中数据存储在硬盘上或运行时的内存上,对于实际的物理设备上,某个位置点最多只有两种状态,有或者无,用数字表示也就是1或0,这也就是计算机中所说的位,每一位的值只能是1或者是0,磁盘上所有的位数就代表了磁盘的实际容量。
如果把硬盘看做一张带有格子的纸,上面所有格子的数量就代表了该硬盘的实际容量,格子里的1或0就是存储在硬盘上的数据了。因为1个格子只有两种状态,能够表示的信息实在是太少了,为了能够表示更加复杂的信息,采用了一系列格子的状态的组合作为一个单元来表示信息。比如采用2个格子来表示信息,就能够表示4种状态(00、01、10、11),采用8个格子就能够表示256种状态。定义了计算机中的单位字节,用8个位表示一个字节,把字节作为计算机中的基本单位。
常用单位换算:1K = 1024字节,1M = 1024K,1G = 1024M等等。
字节作为计算机中的基本单位,它只能够表示256种状态,对于现实复杂的世界来讲显然是不够用的,因此定义了一系列的类型来表示数据。比如:
类型 |
字节数 |
char (unsigned char) |
1 |
short (unsigned short) |
2 |
int (unsigned int) |
4 |
long (unsigned long) |
4 |
float |
4 |
double |
8 |
|
|
|
|
|
|
|
|
|
|
说明:上面计算的字节数量通常指目前的32位系统。
虽然现实世界中的事物最终都要用上述基本的数据类型进行表示,但不是单单这些基本的数据类型就能够描述现实世界。通常编程语言都会提供两种基本的机制将数据组织起来,这两种机制分别是数组和结构。
数组是相同的基本数据类型数据元素的集合。数组的声明是隐含的,例如int list[5]定义了一个有5个元素的整型数组,其合法的下标范围是0、1、2、3、4。
结构是一系列数据元素的集合,这些数据元素彼此没有任何数据类型的要求。其定义是显示的,例如一个学生的信息可以按如下抽象表示:
struct student
{
char lastName;
int ID;
char grade;
};
说到指针类型必须先说明什么是地址。内存也是由每个位构成的,但是在划分的时候以基本单位字节来进行编号划分,比如一条1K的内存,就是由1024个字节构成,把每一个字节进行编号,这个编号就是该字节的地址。在32位系统下,用32位来表示地址的值。
指针类型实际上就是指地址类型,指针的值对应的是实际的地址的值,指针的类型是用来解析该地址的。比如int *p = (int *)1000;表示p是整形指针类型,p指向地址1000的位置。取地址对应的值采用符合*,*p表示起始地址为1000的存储的整形的值,也就是由地址为1000、1001、1002、1003的四个字节组成的值,因为整形占有4个字节。简单的来说,指针变量的值表示的是起始地址,指针的类型表示要从该起始地址取多少个字节。
取地址符号&是用来取得一个变量的存储地址的,例如int a=5; &a表示a的实际存储地址,既然是地址就可以赋值给指针,如int *p = &a; 此时*p == a; 表示p所指向的地址中存储的值为a。
也可以直接对指针指向的地址进行赋值,如*p = 7; 表示把7存储在p指向的地址的连续4个字节空间里。
数据类型指的是一个对象集合和一组在这些对象上的操作的总称。抽象数据类型是一个数据类型,其数据对象和对象上的操作接口不依赖与对象的存储表示和对象上操作的实现。例如C++中的类的概念,简单的说就是把数据及其操作分离开,严格点的只能通过操作接口访问数据,这样不论内部怎么对这些数据进行管理,只要接口不变,外部调用的程序就不用改变。
下面举例说明:自然数是一个数据类型,它的对象集合为{0,1,2,…,INT_MAX},作用在对象上的操作为通常所说的加、减等整数操作。定义自然数类型为NAT_NO,则有下列操作接口等:
NAT_NO Zero();
BOOL Is_Zero(NAT_NO x);
NAT_NO Add(NAT_NO x, NAT_NO y);
NAT_NO Subtract(NAT_NO x, NAT_NO y);
BOOL Equal(NAT_NO x, NAT_NO y);
NAT_NO Successor(NAT_NO x);
算法直观的理解就是解决问题的方法,算法的复杂性包括空间复杂性和时间复杂性。程序的空间复杂性指的是程序从开始执行到完成所需要的存储空间的数量;程序的时间复杂性指的是程序从开始执行到完成所需要的计算时间。
程序所需要的存储空间分为下面两部分:
(1) 固定的空间需求:这部分主要指那些不依赖与程序输入、输出数量和大小的空间需求。固定空间需求包括指令存储空间(存储代码所需要的空间),存储简单变量、固定大小的结构变量和常量的存储空间
(2) 可变的空间需求:这部分包括结构变量的存储空间,这些结构变量的大小依赖于所求问题的特定实例,同时还包括函数递归调用所需要的额外的存储空间。
在分析程序的复杂性时,通常只关心可变的空间需求。注意函数的参数传递方式,以值传递的会增加可变空间,以地址传递则不会;另外递归函数所需要的存储空间远远大于其相应的迭代函数所需要的存储空间。
程序所需时间分为编译时间和运行时间。由于编译时间不依赖于问题实例特征,所以编译时间类似于空间复杂性中的固定空间需求部分,真正值得关注的是程序的执行时间。
通常采用计算程序执行的操作的数量,这样给出的是与机器无关的估计,但是为了计算所执行操作的数量,就必须明确如何将程序划分为独立的程序步。一个程序步是一个在语法和语义上有意义的程序片段,该程序段的执行时间与程序的实例特征无关。
程序的时间复杂性由为程序编写的函数所需要的执行步数确定。有时程序的执行步数随着输入参数的改变而改变,当选定的参数不能够唯一确定程序步数时,可以通过区分如下三种情况来解决:最好情况下的程序步数、最坏情况下的程序步数以及平均情况下的程序步数。
本章主要介绍了一些基础知识,作为后面学习数据结构相关知识的铺垫。重点是要对数据在计算机中的存储与表示有一个直观的了解。