程序设计语言与编译
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.6 C语言数据类型结构

C语言是当今最常用的语言,其数据类型比较丰富,在这一节将讨论它的主要数据类型。

2.6.1 非结构类型

C语言的非结构类型包括内部类型和用户定义类型两类。

1.内部类型

C语言内部类型比较丰富,给了程序员很大的底层控制权。非结构的内部类型有整型、实型和字符型。

整型分为基本型、短整型、长整型和无符号型。一般短整型最大值不大于基本型的最大值,长整型的最大值不小于基本型的最大值,它们都是整数集合的子集。表2-1展示了各类整型数据类型的特性。

表2-1 各类整型数据类型的特性

注:对不同的机器,占用字节数可能有所不同。

实型又称为浮点型,其值是实数集合的一个子集,可分为单精度和双精度两种类型。表2-2展示了浮点型数据类型的特性。

表2-2 浮点型数据类型的特性

字符型数据的值是一个有限字符集的元素。在C语言中,int类型与char类型数据在内存单元中的存储没有本质区别(实际存储的字符是ASCII码),可以给一个字符型的变量赋字符常量或赋数字值,例如

char a;

a='a';赋字符常量'a'

a=2;赋数字2,尽管2对应的ASCII码不是可见的

由此可见,在C语言中对字符的处理与数字相同,所以有char和unsigned char之分。

在C语言中,没有布尔(bool)型,0就表示false,任意非0值表示true,这样用起来非常方便和随意。

2.用户定义类型

C语言的非结构类型的用户定义类型称为枚举类型(Enum eration),例如

          enum bool{false,true};

          typedef enum{false,true}bool;

定义了一个新类型,其类型名为bool,可取值为true和false (分别表示真和假,类似于Pascal的类型boolean)。这是一个非结构的用户定义类型,其效果为

① 引入一个新的数据类型,名为bool。

② 定义bool数据类型的取值为false和true。

③ 定义一个顺序:false<true。

④ 隐含对这个新类型的变量可进行赋值和比较等操作。例如程序段

          enum bool{false,true};
          enum bool b;
          b=true;
          if(b==true){ }

其中,true和false供编译器识别,编译实现时将它们翻译成1和0,在生成的目标(二进制机器)代码中根本就不会出现true和false。甚至可以直接对b赋值1,例如

          b=1;

enum默认元素从0开始对应,但可以显式改变顺序,例如

          typedef enum{false=1,true=2}badbool;

          typedef enum{false=1,true=1}verybadbool;

2.6.2 聚合构造

1.数组

C语言中用数组构造实现有限映像,可分为一维数组和二维数组,格式分别为

<类型说明符> <数组名>[常量表达式]

<类型说明符> <数组名>[常量表达式][常量表达式]

数组是有序数据的集合,数组中的每个元素都属同一类型,与前面的类型说明符一致。使用一个统一的数组名和下标来唯一确定数组中的元素。下标从0开始,例如

int intarr[5];命名为intarr的数组包含5个元素,其类型为整型

char chararr[255];命名为chararr的数组包含255个元素,其类型为字符型

bool boolarr[3];命名为boolarr的数组包含3个元素,其数型为布尔型

可以进行操作

intarr[2]=4;

chararr[2]='a';

boolarr[0]=true;

但是操作

intarr[5]=0;

chararr[-2]='5';

都是不安全的。

C语言可定义二维数组,通常的方法是定义一个一维数组,它的元素又是一个一维数组。例如

float farr[3][4];

定义一个一维数组farr,它有3个元素farr[0],farr[1]和farr[2]。其中每个元素又是一个包含4个元素的一维数组。二维数组的排列顺序是按行存放的,即在内存中先存放第一行的元素,再存放第二行的元素。例如数组a[3][4]的存放顺序为

a[0][0] a[0][1] a[0][2] a[0][3]

a[1][0] a[1][1] a[1][2] a[1][3]

a[2][0] a[2][1] a[2][2] a[2][3]

C语言还可定义多维数组,例如char c[2][2][2]。

在C语言中,对数组名的处理相当于常指针,例如

int a[10];定义a为包含10个整型数的数组

int*pa;定义pa为指向常整型变量的指针变量

pa=a;直接将a值赋给pa

与语句

pa=&a[0];

等价。

2.结构

构造符struct支持笛卡儿积,其定义形式为

          struct<结构体名>
              {成员表列};

其中,花括号内是结构体中各成员(或称分量),由它们组成一个结构体,相当于Pascal的记录类型。对成员类型的说明形式为

<类型标识符><成员名>;

也可以将“成员表列”称为“域表”,每个成员称为结构体中的一个域。例如,学生登记表的一个记录可以表示为

          struct student
              {
              int num;
              char name[20];
              char sex;
              int age;
              float score;
              char addr[30];
              };

结构体变量不能整体输入和输出,

只能对其中的各个成员分别进行操作。例如

          struct student me,you;定义两个student类型的变量me和you
          strcpy(me.name,"j ohn");
          me.sex='M';
          me.age=21;
          me.score=60;
          strcpy(me.addr,"UESTC");

可以把一个结构体变量整体赋给该结构体另一个变量,例如

you=me;

在内存中,结构体各个成员变量依次存储,而且结构体变量的赋值只是纯粹地按位复写。

C的结构体可以嵌套使用,例如定义

          struct date
              {int month;
              int day;
              int year;
              };

在student结构体中加入生日,形成

          struct student
              {int num;
              char name[20];
              char sex;
              struct date birthday;
              int age;
              float score;
              char addr[30];
              };

引用时可以一级一级地找到最低一级的成员,例如

me.birthday.day=7;

即可引用到day,上述语句是对me的结构体赋值,其生日是7。

3.联合

C语言的构造符union(联合),支持判定或,即变体记录,但它与Pascal变体记录有很大的不同,它没有标识符域,只是把不同类型的变量存放在同一(段)内存单元中,形成变体。一个单元究竟存放的是哪种类型变量的值,编译器不提供任何信息,所以在程序设计时把应该使用哪个数据(类型)的判定完全交给了程序员,显然这是非常不安全的。另外,程序员在编程时还必须知道编译器是如何表示(实现)不同类型的,使得编制程序完全依赖实现。例如,程序员要具体了解编译器把几个字节作为一个字长。

联合结构举例说明如下:

          union data
              {int i;
              char c;
              float f;
              };
          union data a,b,c;

其访问形式与结构一样,例如

          a.i=2;
          a.c='s';
          a.f=3.14;

上述程序段中,同一(段)内存单元可用来存放几种不同类型的数据,但在某一瞬间只能存放一类数据,即只有一个成员起作用,不是同时把它们存放起来。存放新成员时,原有的成员就被覆盖了。上述程序段中,实际存放的是a的值3.14。

4.文件

C语言把文件看成一个字符序列,它支持用户定义类型构造的序列。文件是由一个个字符(字节)数据顺序组成的。根据数据的组织形式,可分为ASCII码文件和二进制文件,即把文件看成一个字节流或二进制数据流,不考虑记录的边界。这表明C语言的文件与Pascal语言文件不同,它并不是由记录组成的。文件FILE是由语言预定义的,其格式如下:

      typedef struct
        {
            int    -fd;     文件名
            int    -cleft;  缓冲区中剩下的字符
            int    -mode;   文件操作模式
            char   *-next;  下一字符位置
            char   *-buf;   文件缓冲区位置
        }FILE;

C语言中文件的操作比较丰富,有建立、打开、关闭、定位、读和写文件等操作。除了顺序读/写文件外,也可随机读/写文件。

2.6.3 指针

指针是C语言的第3类数据类型,是非结构的,可用来构造结构数据,支持递归。

C语言规定,所有变量在使用前必须定义,规定其类型。指针变量的值是地址,必须将它定义成“指针变量”。例如

          int    i,j;
          int    *pi,*pj;

定义两个整型变量i,j和两个指向整型数据的指针变量pi和pj。通过赋值语句

pi=&i;

pj=&j;

使pi指向i,pj指向j。

在C语言中没有空指针的概念,可以对指针赋0值表示空。指针用来定义递归类型的数据,例如

          struct tree
              {
              char day;
              struct tree *lchid;
              struct tree * rchild;
              };
          struct tree *my-tree;

其中,第1个成员为数据项,第2和第3个成员分别为左分支和右分支。语句

          struct tree *my-tree;

定义了一个指向二叉树根结点的指针。语句

          my-tree=0;

构成空二叉树,可以把0指针看成空指针。如何构造一棵二叉树是大家熟知的,这里不再讨论。

2.6.4 空类型

C语言有一种特殊的数据类型void,称为空类型。它也是非结构的,主要有下述两种用途。

C语言没有严格区分过程和函数,它把一般语言的过程和函数统一在函数中。函数一般要返回函数值,过程则无须返回过程值。为了统一起见,表示一个不返回函数值的函数,就定义它的返回值为空类型void。

空类型的另一个用途是void指针。它是一个特殊类型,用它定义的指针变量,内容是一个地址,但这个地址的存储单元的数据类型不确定,可把任意类型的指针赋给它,也可以把它赋给任意类型的指针。

C语言不支持用户定义类型构造机制的幂集。

C语言的类型结构如图2-6所示。

图2-6 C语言数据类型结构