4章 数据的表示

大多数应用程序的处理流程都是:接收用户输入数据、处理数据、最后输出数据,整个过程都是围绕数据进行的。本章首先介绍数据在计算机中的表示,接着详细介绍了C语言各数据类型的特点。

4.1 数据的存储

所有运行的程序和程序处理的数据都必须保存在计算机内存中。计算机内存是由无数个电子开关组成的。这些电子开关具有两个状态,打开时状态为“1”,闭合时状态为“0”。每个电子开关用计算机术语“位(bit)”来称呼,它可以代表二进制数的一个基本单元。计算机内存就是一个庞大的二进制基本单元——电子开关的集合体。

4.1.1 内存单元

在个人计算机中,一般由8个二进制“位”组成一个“字节”(Byte),如图4-1所示。

图4-1 字节

计算机可以处理海量信息,需要具有非常大容量的内存系统。为了适应海量内存的表示,通常内存可以有多个表示单位,各度量单位之间的转换关系如下:

        1Byte= 8 bit
        1KB=210Byte=1024Byte
        1MB=210KB=1024KB
        1GB=210MB=1024MB
        1TB=210GB=1024GB

提示

C程序可以处理内存的最小单位为位(bit)。

现在计算机都配有512MB、1GB甚至2GB以上的内存,对于这些海量内存单元,计算机怎么到指定存储单元存取数据?在计算机中,每个存储单元都有一个地址,通过这个地址即可对内存中的数据进行保存和读取操作。如图4-2所示,显示了4个字节的内容,左侧为每个字节的地址,如地址8100存储单元中保存的数据为 97,实际保存在计算机中的为二进制数“01100001”。为了直观表示,在图中显示的是十进制数据。

图4-2 内存地址

计算机中的内存是按字节顺序排列的,内存地址也是依次编码。在C程序中,程序员一般不用关心具体的地址值,这些都由C编译器自动处理。

4.1.2 字符的存储

要将数据保存到计算机中,必须使用二进制形式表示数据。对于字符和数值将按不同的方式进行保存。

对于字符,可使用ASCII(美国标准信息交换码)来表示。ASCII码将字母、数字和特殊符号进行编号,如编号97表示小写字母“a”。在计算机中保存字母“a”时,实际上是保存的数字编号97(97转换成8位二进制为“01100001”),如图4-3所示。

图4-3 字符的存储

4.1.3 数值的存储

一个字节为8位二进制位,正好可保存一个字符的ASCII码。如果保存数值,8位二进制位最大只能表示256(28=256)。如果要表示更大数值(如年人均收入、世界人口总数等),一个字节就不够了。

在PC中,一般整数用2个字节来存放,而实数用4个字节或更多的字节来存放。

在计算机中,保存到每个字节的都是8位二进制位,计算机怎么区别保存的是字符或是数值?这就需要在程序中指明将要保存或读取的数据类型,如果设置保存字符,则只将数据写入到1个字节中;如果设置保存整数,则向内存中相邻的两个字节中写入数据。同样,在读出数据时,若指定读取的数据为一个字符,则只从内存中读取一个字节的数据;若指定读取一个整数值,则将读取内存中相邻的两个字节,并组合为一个整数值。

有关各种数据类型在内存中的存放形式,本章后面各节将分别介绍。

4.2 数据类型简介

数据类型是按规定的形式表示数据的一种方式,不同的数据类型将占用不同的存储空间。C语言提供了多种数据类型,可分为基本数据类型,构造数据类型,指针类型,空类型四大类。如图4-4所示。

图4-4 数据类型

1.基本数据类型

基本数据类型的值不可以再分解为其他类型。如整数类型、浮点数类型等。

2.构造数据类型

构造数据类型是指使用基本数据类型、或其他已定义的一个或多个数据类型来构造一个新的数据类型。即一个构造类型的值可以分解成若干个“成员”,每个“成员”都是一个基本数据类型或一个已定义的构造类型。如数组类型、结构类型、联合类型等。

3.指针类型

在程序中使用指针是C语言的精华。指针是一种特殊的,具有重要作用的数据类型。指针的值表示某个内存地址。

4.空类型

空类型主要用来设置函数的返回值。一般函数都需要向调用者返回一个函数值,这个返回的函数值是具有一定的数据类型的,应在定义函数时设置好。如果函数不返回值,则设置函数为空类型。

在本章后面的内容中,将详细介绍基本数据类型中的定义、存储、使用。其余数据类型在本书后面相应章节中进行介绍。

4.3 常量

在C程序中,程序执行时其值不发生改变的量称为常量,其值可变的量称为变量。在C程序中,常量是可直接使用,而变量则必须先定义后使用。

4.3.1 直接常量

直接常量又称为字面常量,通过数据直接表现的形式来确定其数据类型,例如:

        s=3.14*r*r

在以上代码中,3.14表示为小数形式,该常量的数据类型为浮点数类型。

而以下代码:

        l=2*3.14*r

常量2表示为整数,该常量的数据类型为整数类型。

4.3.2 符号常量

在C程序中更常用的是,用一个与常量相关的标识符代替常量出现在程序中,这种相关的标识符称为符号常量。

例如,在本书第2章的程序中曾使用了PI表示圆周率3.1415926,在程序中用到圆周率时就用PI代替。

使用符号常量的优点是:

使程序中的常量含义清楚。

若需修改常量,只需要修改定义符号常量的语句即可。

提示

符号常量不是变量,它所代表的值在程序运行期间不能再改变。也就是说,在程序中,不能再用赋值语句对它重新赋值。

可用两种方法定义符号常量:

1.#define宏定义

在本书第2章的程序中就使用这种方式定义符号常量,其语法格式如下:

        #define  符号常量名  宏表达式

有关宏定义的知识将在本书第 16 章中进行详细介绍。在这里读者需要了解的是,在编译预处理时,将对程序中所有出现的“符号常量名”,都用宏定义中的“宏表达式”去替换。例如,有以下程序:

【程序4-1】符号常量示例

        1:  #include <stdio.h>
        2:  #define PI 3.14
        3:
        4:  int main()
        5:  {
        6:      float r;
        7:
        8       printf("请输入半径:");
        9:      scanf("%f",&r );
        10:
        11:     printf("面积=%.2f\n",PI*r*r);
        12:     printf("周长=%.2f\n",2*PI*r);
        13:     getchar();
        14:     return 0;
        15  }

在编译程序时,首先进行预处理,将程序中所有符号 PI 都替换为 3.14,预处理后的代码如下:

        1:  #include <stdio.h>
        2:  #define PI 3.14
        3:
        4:  int main()
        5:  {
        6:      float r;
        7:
        8:      printf("请输入半径:");
        9:      scanf("%f",&r );
        10:
        11:     printf("面积=%.2f\n",3.14*r*r);
        12:     printf("周长=%.2f\n",3.14*PI*r);
        13:     getchar();
        14:     return 0;
        15: }

第11行和第12行程序中的PI被替换为宏定义的内容。

注意

使用#define 进行宏定义时,最后不能添加分号。若有分号,在进行宏替换时将连分号一起替换,将得到错误的结果。

例如,若将第2行程序改为以下形式:

        2:  #define PI 3.14 ;

则编译预处理时,将第11行和第12行的程序替换为以下形式:

        11:     printf("面积=%.2f\n",3.14 ;*r*r);
        12:     printf("周长=%.2f\n",3.14 ;*PI*r);

将导致编译出错,并且提示错误在第11行,这时检查第11行程序及其上一行的程序都发现不了错误。

在C语言的大部分包含文件中,都使用#define定义了许多常量。例如,打开“limits.h”头文件,可看到在当前编译环境下,各种变量所能表示的最大数和最小数。在笔者的计算机中,对长整型的最大值和最小值符号常量的定义如下:

        #define LONG_MAX  2147483647L
        #define LONG_MIN   (-LONG_MAX-1)

即长整型数据的范围为-2147483648~2147483647。

2.const语句定义符号常量

另一种定义符号常量的方法是使用const语句,其语法格式如下:

        const  类型  符号常量=表达式;

注意

const为一个C语句,需要在语句末尾添加分号作为结束。

例如:将【程序4-1】中的第2行改为以下形式:

        const float PI=3.14;

程序的运行效果不变。

在【程序 4-1】中,使用#define 和 const 得到的效果是相同的。但是#define 和 const在C的编译处理过程中是不同的。

#define定义的其实是一个字面常量的名字,在程序中可以把这个名字等价于字面常量使用,在编译时会被编译器替换为该字面常量。使用这种方式,只能达到提高程序可读性的效果,但是安全性不能得到保证。

从const语句的语法格式可以看出,const语句支持为常量指定类型,编译器在编译代码时将对其类型进行安全检查。另外,使用 const 定义的符号常量有自己的内存地址,编译器为其分配了存储空间。

在可能的情况下,尽量使用const方式定义符号常量。

提示

习惯上符号常量的标识符用大写字母,变量标识符用小写字母,以示区别。

其实,在C语言中还可以使用“枚举类型”定义符号常量,有关枚举的相关内容将在本书第13章中进行介绍。

4.4 变量

在程序执行过程中,其值可以改变的量称为变量。一个变量应该有一个名字,在内存中占据一定的存储单元。在C语言中,变量必须先定义,才能使用。

4.4.1 标识符

在C语言中,为了引用常量、变量、数组、函数、宏名等,需要给这些元素设置一个名称,即标识符。每一种程序设计语言对标识符都有一定的限制,C语言标识符一般应遵循如下的命名规则:

标识符必须以字母a~z、A~Z或下画线“_”开头,后面可跟任意个(可为0个)字符,这些字符可以是英文字母、下画线、数字,其他字符不允许出现在标识符中。

标识符要区分大小写字母。如标识符name,Name和NAME是三个不同的标识符。使用过Basic编程的读者特别要注意这点。

对于标识符的长度,C89标准规定,编译器至少应该能够处理31个字符(包括31)以内的标识符。C99标准规定,编译器至少应该能够处理63个字符(包括63)以内的标识符。在程序中,如果使用超出最大数目限制的字符,来命名标识符,编译器会忽略超出的那部分字符。

C语言中的关键字(或称保留字),具有特殊的意义,不能作为用户自定义的标识符。C语言的关键字可参见附录B中的内容。

自定义的标识符最好取具有一定意义的字符串(即看名知义),以方便阅读、记忆和使用。

了解标识符的相关规定后,即可在程序中,为相关元素设置符合要求的名称了。例如,以下标识符是正确的,可以在程序中,作为符号变量、变量、函数等的名称使用:

        name
        Age
        address_home
        telephone2
        _frequency
        __x

而以下标识符是错误的(后面括号是注明了错误的原因):

        2telephone   (以数字开头)
        Firt name    (含有空格)
        Return       (为C的关键字)
        a+b               (包含+号)

4.4.2 声明变量

在C语言中,变量要求先声明后使用,这样做的好处在于:

确保程序中引用的变量名正确,凡引用未声明的变量,在编译时,将提示出错,这样可减少因输入错误而导致的变量引用错误。如声明了变量test,在后面的程序代码中输入tset,编译程序将认为变量tset未声明而提示出错。

编译器按声明的变量类型分配相应的存储单元。

在 C 语言中,不同数据类型可进行的运算不同。在声明变量时,指定了变量的类型,在编译时,可检查变量所进行的运算是否合法。

声明变量最简单的格式如下:

        类型说明符  变量名表;

其中的类型说明符为表示数据类型的关键字,如int表示整数类型、float表示浮点类型,在本章后面,将逐个介绍基本数据类型的关键字,在本书第12章和第14章中,将介绍其他类型的关键字。

例如:在程序中,使用以下两行语句,声明两个不同数据类型的变量。

        int age;
        float amout;

在一个类型说明符后面,可以声明多个变量,各变量之间用逗号分隔,例如:

        int num, cout;

以上语句声明了两个整数类型的变量。

编译程序时,编译器将在内存中按声明的数据类型为变量分配存储空间。例如,使用以上语句声明变量后,编译器为变量分配的内存效果如图4-5所示。

图4-5 为变量分配内存

在图4-5中,为整数类型变量分配2个字节,为浮点数类型变量分配4个字节。分配给变量的字节中的值为不确定的(该图中各字节表示为0,实际情况并非如此)。

例如:编写程序将摄氏温度转换为华氏温度,其转换公式为:

华氏度=32+摄氏度×1.8

【程序4-2】摄氏温度转换为华氏温度

        1:  #include <stdio.h>
        2:
        3:  int main()
        4:  {
        5:      float c,f;                           /*声明变量*/
        6:
        7:      printf("请输入摄氏温度值:");
        8:      scanf("%f",&c);                      /*输入摄氏温度值*/
        9:
        10:     f=32+c*1.8;                          /*转换为华氏温度值*/
        11:     printf("摄氏温度=%.2f,转换为华氏温度=%.2f\n",c,f);  /*输出转换结果*/
        12:     getch();
        13:     return 0;
        14: }

编译以上程序的第5行时,编译器将在内存中分配存储空间,分别用来保存变量c和变量f的值。

执行到第8行,将用户输入的数值保存到变量c指向的内存单元。

第10行从变量c指向的内存单元中读出数据,与1.8相乘后,加上32,将计算结果保存到变量f所指向的内存单元。

第11行,程序再从变量c和变量f指向的内存单元中读出数据,分别显示到屏幕上。

提示

在进行声明变量的操作时,还可限定变量的作用域、生命周期等,这些内容将在本书第11章中进行介绍。

4.4.3 变量初始化

编译器给声明的变量分配内存空间时,并不会对分配的内存单元清零,也就是说,这时的内存单元中为一个不确定的值。为了使程序不会因为使用一个不确定的值,而出现数据混乱,应在使用变量前,对其赋初值,使其具有确定的值,这种方法称为初始化。

在声明变量后,可使用赋值语句为变量赋初值,例如:

        int age, num, count;
        age=18;
        count=50;

后两条语句为赋值语句,分别将常量18保存到age变量中,将50保存到count变量中。如果多个变量具有相同的值,还可使用以下连续赋值的方式,来快速初始化变量:

        a=b=c=d=8;

执行以上语句后,变量a、b、c、d的值都为8。

最方便的初始化方法是在声明变量的同时为其赋初值,其语法格式如下:

        类型说明符 变量1= 值1,变量2= 值2,……;

例如:

        int age=18;
        int num,count=50;

在声明变量时,不允许连续赋值,如a=b=c=d=8是错误的。

普通变量的初始化不是在编译阶段完成的,而是在程序运行时执行赋值语句时,才进行操作。本书第11章中介绍的静态变量和外部变量的初始化是在编译阶段完成的。

例如:

        int age=18;

以上语句相当于下面的两条语句:

        int age;
        age=18;

而以下语句:

        int num,count=50;

相当于下面两条语句:

        int num,count;
        count=50;

只给变量count赋初值,变量num未处理。

4.5 整数类型

整数就是没有小数部分的数据,如10、256、1 048 576等。在C语言中,根据要表示数的大小,分别提供了不同的整数类型。

4.5.1 整数类型及存储

C语言提供了多种整数类型,分别用来表示不同范围的整数。其中,int类型是最基本的整数类型,程序员可根据需要选择其他类型。

1.整数类型

C 语言中,根据计算机的字长的不同,为 int 类型分配的存储空间也不一样。例如,在Pentium及以后的计算机中,因字长为32位,int类型也将用32位二进制数表示(即占用4个字节),而在之前的IBM PC机上,因字长为16位,int类型将用16位二进制数表示(2 个字节)。读者若要查看自己计算机中,int 类型占用几个字节的内存,可使用以下语句:

        printf("int:%d Bytes.\n",sizeof(int));

其中sizeof为一个运算符,用来计算指定数据类型或变量占用的存储空间大小,以字节为单位。

在声明int类型的变量时,还可给int添加各种修饰符。通过添加修饰符,可改变int类型的含义,使其更加精确地适合程序的需要。共有以下4个修饰符:

signed (有符号,默认值)

unsigned (无符号)

long (长型)

short (短型)

默认情况下,直接使用 int 关键字声明变量,该变量用来表示有符号数。以下两种方式是相同的:

        signed int i;
        int i;

在计算机中,若要表示负数,则必须使用有符号数。在有符号数中,使用一位二进制位表示符号(正号或负号),如果int类型为16位,这时将只能使用15位来表示数值,所以其表示数据范围将缩小。有符号int类型的取值范围为-32 768~32 767。若使用unsigned修饰int类型(可直接简写为unsigned),用来保存无符号数据,则可以使用16位二进制位的全部,表示数值,其取值范围为0~65 535。

使用long修饰int,可直接简写为long,可能占用比int类型更多的存储空间。例如,在字长为16位的计算机中,int类型为16位,long类型则为32位。在字长为32的计算机中,int类型和long类型都是32位。

使用short修饰int,可直接简写为short。可能占用比int类型更少的存储空间。例如,在字长为16位的计算机中,short类型和int类型都为16位。而在字长为32位的计算机中, short类型为16位,int类型为32位。

要表示特别大的整数时,还可使用两个long关键字来修饰int类型,写为如下形式:

        long long int l;

其中关键字int可省略,直接写为long long。这种类型可能占用比long类型更多的存储空间。例如,在字长为32位的计算机中,long long类型为64位(8个字节),而long类型为32位(4个字节)。

下面编写程序,分别检查在当前计算机系统中,各种整型数据所占内存空间的大小。

【程序4-3】整型数据所占内存空间的大小

        1:  #include <stdio.h>
        2:
        3:  int main()
        4:  {
        5:      printf("short int:%d Bytes\n",sizeof(short int));
        6:      printf("int:%d Bytes\n",sizeof(int));
        7:      printf("long int:%d Bytes\n",sizeof(long int));
        8:      printf("long long int:%d Bytes\n",sizeof(long long));
        9:      getch();
        10:     return 0;
        11: }

编译运行以上代码,将显示如图4-6所示的结果。从执行结果可看出,当前计算机系统的字长为32位,其short类型为2字节(16位),int类型和long类型为4字节(32位), long long类型为8字节(64位)。

图4-6 【程序4-3】运行结果

注意

如果读者使用的计算机字长为16位(现在这种计算机已经较少了)或64位,程序4-4运行的结果将与图4-6不同。

使用修饰符可改变 int 类型占用的存储空间,随着存储空间的不同,各种整数类型的取值范围也就不同。另外,同一种类型,使用unsigned和signed修饰,其取值范围也不一样。表4-1列出了在字长为32位的计算机中,4种整数类型的取值范围。

表4-1 32位计算机中整数类型取值范围

从表中可以看出,对于无符号数,其最小值为 0,最大值则是其所占字节二进制各位全部为1时的值,如unsigned short为16位二进制,若16位二进制全部为1,则表示其最大值(为65 535),使用Windows XP中的科学计算器,即可算出。

而对于有符号数,其最大数为数据类型所占位数少1位二进制位时的最大值,如对于short,其最大数为15位二进制数全为1的情况(其值为32 767),而其最小值则为最大值前添一个负号并减1,如short的最小值为 -32 767-1=-32 768。

提示

从表4-1可看出,在32位计算机中,int类型和long类型占用相同的存储空间,取值范围也相同。C 提供这两种在一定条件下相同的数据类型,是为了提高程序的可移植性。如果将在32位计算机中编写好的程序拿到16位计算机中进行编译,这时,int类型将只占用2个字节(16位),而long类型占用4个字节(32位)。

2.整数的存储

上面介绍了C语言提供的整数类型,及每种类型占用的存储空间。整数是以二进制数存储的,例如,将数字15保存到short类型中,short类型为16位,15的二进制为“1111”,其保存到内存中的效果如图4-7所示。

图4-7 正整数的存储

图4-7中,最左侧(高位)为符号位,当其为0时,表示正数,当其为1时,表示负数。在计算机中,负数的保存比较特殊,并不是简单地将符号位置1即可。为了提高计算机加减运算的速度,现代计算机都是采用补码来保存数据。

补码的转换很简单,可使用以下两个规则,来求一个数的补码:

正数的补码为其自身。

负数的补码,除符号位外,逐位取反(即0变1,1变0),最后再加1。

因此,要将-15保存到short类型中,其结果如图4-8下图所示。在图4-8上图中,直接将正数15的符号位置1,表示的是负数-32753(若是int或long类型,此数又不同)。

图4-8 负整数的存储

4.5.2 整型常量的表示

在C语言中,编译器将自动根据字面常量的表示形式确定其类型。如果程序中的字面常量看起来像整型,编译器就会将其作为整型,例如:

          f=32+c;

以上代码中,32将被作为int类型的常量。

而如果写为如下形式:

          f=32.0+c;

将32写为32.0,因其有小数点,编译器将其作为float类型(本章后面将介绍)。

1.整数常量的类型

通常,C编译器将智能识别整数常量的类型。例如,在程序中使用数字1200,编译器将该数字以int类型存储到内存中。如果程序中有数字26 828 820 818,这时,使用int类型(4个字节)将不能完整地保存该数值,编译器将自动选择使用unsigned long类型,如果仍然不能完整地表示该数,编译器将会选择使用 long long 类型保存该数,甚至使用unsigned long long类型。

以上介绍的是编译器自动选择占用存储空间少的数据类型保存整型常量。在有的情况下,可能需要强制使用更大的内存空间保存一个较小的整数。例如,在16位计算机中,int类型为16位字长,而一些函数需要使用long类型的参数,如果参数为一个很小的整数,就需要强制将其转换为long类型。

在一个小的整数后面,添加一个字母 l(小写的 L)或 L,则该数将被看作一个 long类型的常数。在16位的计算机中,编译器会将128作为2字节的int类型存储,而128L则会告诉编译器,将该数保存为4字节的long类型。

若需要将小的整数按long long类型存储,可在整数常量的后面添加后缀ll(或LL)。

若要将整数常量作为无符号数据存储,可在整数常量的后面添加后缀u(或U)。后缀U可以和L或LL一起使用。例如,10UL表示按long类型存储的无符号数10(占用4个字节存储空间),而10ULL表示按long long类型存储的无符号数10(占用8个字节存储空间)。

2.整型常量表示为十进制

在C语言中,可以将整型常量用十进制、十六进制和八进制进行书写。

十进制整型常量:十进制整型常量不使用前缀,直接由数个数字为0~9组成。例如,以下各数是合法的十进制整型常数:

235、-512、65 535、1314520;

而以下各数不是合法的十进制整常数:

0735(使用了前导0,编译器将其认为是八进制数,相当于十进制数的477);

0818(使用了前导0,编译器将其认为是八进制数,在八进制数中使用超过7的数码,编译器将会提示出错);

5F(含有非十进制数码)。

提示

C程序中,编译器将根据数字前缀,来区分各种进制。因此,在书写常数时,不要把前缀弄错造成结果不正确。

3.整型常量表示为十六进制

十六进制整型常量的前缀为0X或0x。其数码取值为0~9,A~F或a~f,共16个。

以下各数是合法的十六进制整型常量:

        0XFF(为十进制的255);
        0x100000(为十进制的65536);
        0x3a4f(为十进制的14927)。

而以下各数不是合法的十六进制整型常量:

        FFFF(无前缀0X或0x);
        0x5HB(H为非十六进制数码)。

4.整型常量表示为八进制

八进制整型常量必须以0为前缀。其数码取值为0~7。

以下各数是合法的八进制整型常量:

        0735(为十进制的477);
        0101(为十进制的65);
        010000(为十进制的4096);
        07777(十进制为4095)。

而以下各数不是合法的八进制整型常量:

        255(无前缀0,编译器将其保存为十进制数的值);
        0818(包含了非八进制数码)。

5.整型常量表示方法小结

整型常量是C程序中使用非常多的数据类型,在输入这些数据时,通过其前缀向编译器说明常量的数据进制,分三种情况:

无前缀,表示十进制数,可使用数码0~9;

前缀0,表示八进制数,可使用数码0~7;

前缀0x或0X,表示十六进制数,可使用数码0~9,a~f(或其大写)。

提示

无论在程序代码中使用哪种进制书写,数据最终将按二进制编码格式保存到内存中。在程序中使用不同的进制表示,只是方便代码的编写而已。

使用整型常量的后缀表示数据存储的方法,分四种情况:

无后缀,由编译器根据最小内存占用原则,为其分配存储空间;

后缀l或L,强制将常量按long类型存储,若long类型不能完整地存储该数据,则使用unsigned long、long long或unsigned long long等类型;

后缀ll或LL,强制将常量按long long类型存储;

后缀u或U,强制将常量按unsigned(无符号)类型存储,可与L或LL组合使用。

提示

使用八进制或十六进制表示的数据也可通过后缀指定存储空间。

4.5.3 整型数据输出

使用 printf 库函数可以将整型数据显示到屏幕上,在一般情况下,可在 printf 中,使用%d将整型数据输出。但对于unsigned类型、long long类型的数据,直接使用%d输出数据时,可能会出现问题。另外,为了方便用户,有时还可能需要将整型数据输出为八进制或十六进制的数。

1.输出不同类型数据

在printf函数中,使用%d控制符,可输出带符号的十进制整数,还可以使用以下控制符输出整数。

%ld,输出signed long数值(使用%d也可输出long数值,但若移植到其他系统,可能会出问题);

%hd,输出signed short数值;

%lld,输出signed long long数值;在有的系统(如本书使用的Dev-C++环境)中,需使用%I64d输出该类型(是i的大写字符I,不是小写字母l)。

%u,输出unsigned int数值;

%lu,输出unsigned long数值;

%llu,输出unsigned long long数值。

在一般情况下,是否使用%u,看不到明显的区别。例如,输出一个较小的正数128,使用有符号和无符号控制符得到的结果都相同——都为 128;而对于一个数,超过有符号数的表示范围,同时又在无符号数的表示范围之内时,是否使用%u,将得到两个不同的结果。具体见下面程序的输出结果。

【程序4-4】整型数据输出示例

        1:  #include <stdio.h>
        2:
        3:  int main()
        4:  {
        5:      unsigned int ui=2147483649U;
        6:      long l=65537;
        7:      short s=128;
        8:      long long vl=13198317000;
        9:
        10:     printf("无符号整型数:%u,显示为有符号整型数:%d\n\n",ui,ui);
        11:     printf("短整型数:%hd,显示为无符号短整型:%hu,显示为整型数:%d\n\n",s,s,s);
        12:     printf("长整型数:%ld,显示为整型数:%d,显示为短整型数:%hd\n\n",l,l,l);
        13:     printf("特长整型数:%lld,显示为整型数:%d\n",vl,vl);
        14:     getch();
        15:     return 0;
        16: }

编译执行以上代码,得到如图4-9所示的结果。

图4-9 【程序4-4】运行结果

提示

这是在字长为32位的计算机中运行的结果,若读者的计算机与此环境不同,可能得到的结果也不一样。本书后面的程序都是在该环境中运行,以后不再另外说明。

在以上程序中,第10行将变量ui的值按无符号数显示时,其为正常值。将其按有符号整数显示时,因该变量的值已超过有符号int类型数的表示范围(大于2147483647),将得到错误的结果。

第11行程序将short类型变量s的值,按unsigned short类型和int类型显示,都可以得到正确的结果。

第12行程序将long类型变量l的值,按int类型输出,可得到正确的结果(在当前计算机中int和long相同),而将变量l的值作为short类型输出时,因其值超过了short类型的表示范围,将得到错误的结果。

第13行中,将long long类型变量vl的值,以%I64d格式可正常输出,但作为int类型输出时,将得到错误的结果。

2.输出十六进制和八进制

编写C源代码时,可按三种数制(十进制、十六进制和八进制)书写数字,也允许按这三种数制输出整数。在默认的情况下是输出十进制数,可在printf函数中使用控制符,来控制输出数据的数制。

%x,将整数输出为十六进制。

%o,将整数输出为八进制(是小写英文字母O,不是数字0)。

例如,下面的程序可将用户输入的整数转换为十六进制数和八进制数。

【程序4-5】整数转换为十六进制和八进制示例

        1:  #include <stdio.h>
        2:
        3:  int main()
        4:  {
        5:      int i;
        6:
        7:      printf("请输入一个十进制数:");
        8:      scanf("%d",&i);
        9:
        10:     printf("\n你输入的十进制数:%d\n\n",i);
        11:     printf("转换为十六进制数:%x\n\n",i);
        12:     printf("转换为八进制数:%o\n",i);
        13:     getch();
        14:     return 0;
        15: }

编译执行以上代码,得到如图4-10所示的结果。

图4-10 【程序4-5】执行结果

以上程序中,第8行接收用户输入的一个整数,保存到变量i中,接着,在第10行以十进制输出变量i,在第11行以十六进制输出变量i,在第12行以八进制输出变量i。

4.6 字符类型

字符类型的关键字char,用于存储字母、数字和一些特殊符号。在C语言中,char的本质是一个整数。在本章开始,介绍了字符的存储格式,在大部分计算机中,都使用一种称为ASCII码的编码方式保存字符。如小写字母a的ASCII编码为97,因此,计算机在保存字母a时,实际是保存数字97。从附录A中,可看出,标准ASCII码值的范围为0~127。对于这个范围的数值,只需要7位二进制位,就可完全表示所有可能。C语言的char类型使用一个字节(8个二进制位)保存一个字符,最多可表示256种字符。

4.6.1 字符常量

与整型常量不同,字符常量必须要使用指定的定界符来限制。字符常量是用单引号括起来的一个字符(不能是多个字符)。

例如,以下都是合法的字符常量:

'w'、'y'、'c'、'0'、'#'

在程序中输入字符常量时,需要注意以下几点:

字符常量的定界符为单引号,即只能用单引号括起来,不能用双引号或其他符号。

一对单引号中只能包括一个字符,不能是多个字符。

字符可以是ASCII码中的任意字符。

数字被定义为字符型之后,就不能代表数字字面的量,若参与数值运算,其值将是对应的ASCII码。如字符常量'8'和8是不同的。'8'是字符常量(其ASCII码值为56),这时如参与运算(如加上10),将得到整数值66。

4.6.2 字符变量及初始化

与声明其他类型变量类似,使用char关键字,可声明字符变量。例如,

        char c1, c2;
        char answer;

以上两条语句定义了3个字符变量,编译器将为每个字符变量分配1个字节的存储空间。可使用以下语句,为字符变量赋初值:

        c1= 'w'

编译器处理该语句时,将字符常量w对应的ASCII码数据119保存到为变量c1分配的存储单元中。

使用以下语句赋初值,都是不正确的:

        c1= "w"
        c1= w

第1行语句用双引号作为字符的定界符,C编译器将双引号括起的部分作为字符串(有关字符串的内容在本书第6章和第13章中,将进行介绍),若在程序前面已将变量c1声明为char类型,编译到该语句时将出错。

第2行语句,字符w无定界符,C编译器将其作为一个变量。执行该语句时,将变量w中的值赋给变量c1,如果变量w未定义,编译时将出错。

因为字符常量是作为一个整数保存的,也可以使用下面的语句,为字符变量赋值。

        c1= 119

使用整数值给字符变量赋值时,如果整数值超过取值范围,编译时,将会出现错误提示。有的 C 编译器将 char 作为有符号类型,这样其取值范围就为-128~127。而另一些 C编译器将char当作无符号类型,其取值范围就为0~255。

在某些情况,可以使用char来处理小整数。与int类型相比,char类型的内存开销要小几倍。还可以使用unsigned和signed,修饰char类型,使其表示的范围符合程序的要求。如果声明的变量只是处理字符,不需要使用这些修饰关键字。

4.6.3 转义字符

查看一下附录A的ASCII码表,可以看出,除了英文字母、数字和一些特殊符号能在屏幕上显示之外,还有许多不能显示的字符,如让计算机蜂鸣的ASCII值为7,怎样将这个字符赋值给一个变量?可使用两种方法来表示:

第一种方法就是将ASCII码直接赋值给char变量,例如:

        char beep=7;

这种方法可能带来一些问题:首先是不直观,另外,为程序的移植带来一些问题,例如,将程序移植到不支持ASCII的系统中时,让计算机蜂鸣的代码可能就不是7了。

第二种方法是使用转义字符。转义字符是一种特殊的字符常量,以反斜线“\”开头,后跟一个或几个字符。反斜线后面的字符不再具有原来的意义,所以称为“转义”字符。例如,在前面经常用到的printf函数的格式串中,就使用了一个转义字符“\n”,用来表示“回车换行”。表4-2列出了C语言中的转义字符及其意义。

表4-2 转义字符及其含义

广义地讲,C语言字符集中的任何一个字符均可用转义字符来表示。表中的\ddd和\xhh正是为此而提出的。ddd和hh分别为八进制和十六进制的ASCII码数值。如\111表示字母"I",\112表示字母"J",\x2a表示字符“*”等。

转义字符中的\t产生一个制表符(每个制表符产生7个字符的间隔),制表符常用来生成有一定排列规则的数据。例如,下面的代码将3个变量的值,按制表位输出。

【程序4-6】使用转义字符示例

        1:  #include <stdio.h>
        2:
        3:  int main()
        4:  {
        5:      int i=1,j=2,k=3;
        6:
        7:      printf("输出第一种形式:\n");
        8:      printf("变量i\t变量j\t变量k\n");
        9:      printf("%d\t%d\t%d\n",i,j,k);
        10:     printf("\n\n输出第二种形式:\n");
        11:     printf("变量i\t%d\n",i);
        12:     printf("变量j\t%d\n",j);
        13      printf("变量k\t%d\n",k);
        14:     getch();
        15:     return 0;
        16: }

编译执行以上代码,得到如图4-11所示的结果。

图4-11 【程序4-6】执行结果

第8行代码使用两个转义符“\t”,将三个数据分隔。

第9行代码中,printf中的内容看起来有点复杂,将其中的两个转义符“\t”分离出来,就只剩三个控制输出整数的控制符了。通过两个转义符“\t”将三个整数分隔,与上行的数据对齐。

第10行在输出字符之前,连续使用了两个转义符“\n\n”,将在屏幕上输出两个空行。

第11~13行的代码,分别将提示字符和变量值用一个转义符“\t”分隔。

4.6.4 字符型数据的输出

在C语言中,可对char类型变量赋以整型值(该值必须在允许范围之内),同样,也允许对整型变量赋以字符常量,这时,编译器将字符常量的ASCII码值保存到整型变量中。

既然 char 类型变量保存的也是整型数,则也可对 char 类型进行加减运算。当运算的结果超过char的取值范围时,程序将自动舍弃高位的值,而只保存低8位的数值。

在输出时,允许把字符变量按整型量输出,也允许把整型量按字符量输出。在C语言中,printf函数使用控制符“%c”输出字符。若要将字符变量输出为ASCII码值,可使用输出整数的控制符“%d”。

当程序中将 int 类型的变量中的值作为字符输出时(使用控制符“%c”),因 int 类型占用4字节存储空间,char类型只使用1个字节,这时,将只有低8位字节参与处理。

例如,下面的程序演示将int类型输出为字符、对char类型进行加法运算的效果。

【程序4-7】字符型数据输出示例

        1:  #include <stdio.h>
        2:
        3:  int main()
        4:  {
        5:      int i=65,j;
        6:      char c='a';
        7
        8:      j=i+1;
        9:      printf("整数i=%d,显示为字符:%c\n",i,i);
        10:     printf("整数j=%d,显示为字符:%c\n",j,j);
        11:     printf("字符c=%c,显示为整数:%d\n",c,c);
        12:     c=c+1;
        13:     printf("字符c=%c,显示为整数:%d\n",c,c);
        14:     getch();
        15:     return 0;
        16: }

编译执行以上代码,得到如图4-12所示的结果。

图4-12 【程序4-7】执行结果

第5行程序初始化int类型变量i,第6行代码初始化char类型变量c。

第9行和第10行使用%d和%c输出整型变量i和j,输出的结果为整数值和与该值对应的ASCII码字符(j为i的值加1,所以输出的字符为位于“A”后的一个字母“B”)。

第11行和第13行将char类型的变量c,分别输出为字符和整数。

4.7 实数类型

在大多数程序中,使用整数就能完成任务。但是,在实际应用中,经常还会遇到,如需要使用小数的情况处理财务数据等,这时就需要使用 C 语言的实数类型(又称为浮点数)。C语言中的实数包括float、double和long double三种类型。

4.7.1 实数类型及存储

1.实数类型及取值范围

实数包括float、double和long double三种类型。

float(单精度)类型,C语言标准规定,float类型至少能表示6位有效数字,取值范围为10-37至10+37。6位有效数字指float至少应能表示数据的6位有效值,超过该有效位时,系统采用近似的数表示,如3.1415926,共有8位有效位,保存到float中,将只保存6位有效位,保存的值为3.141593。通常,系统使用32位存储一个float类型的数据,其中,用8位表示指数及指数的符号,24位用于表示小数部分(称为尾数)及其符号。

double(双精度)类型,称为双精度实数。double 类型与 float 类型相比,增加了数据的精度,至少能表示10位有效数字。通常,系统使用64位存储double类型的数据。

long double(长双精度)类型,该类型可以满足比double更高的精度要求。不过, C语言要求long double类型至少应与double类型具有一样的精度。

为了知道在自己的计算机中各实数类型占用的字节,可运行以下程序:

【程序4-8】实数类型占用的字节示例

        1:  #include <stdio.h>
        2:
        3:  int main()
        4:  {
        5:      printf("float占用%d字节。\n",sizeof(float));
        6:      printf("double占用%d字节。\n",sizeof(double));
        7:      printf("long double占用%d字节。\n",sizeof(long double));
        8:      getch();
        9:      return 0;
        10: }

编译执行以上代码,在本书开发环境中,得到如图4-13所示的结果。其中float类型占用4个字节(32位),double类型占用8个字节(64)位,long double类型占用12个字节(96位)。

图4-13 【程序4-8】执行结果

实数在不同的系统中,其取值范围不太一样,表4-3列出典型。

表4-3 实数类型取值范围

2.实数的存储

实数类型的数据与整数类型的数据存储方式不同,实数保存到内存中时,将数据分为三部分,如图4-14所示。其中符号位用来表示正负数,小数部分表示实数的有效位,指数部分表示10的几次幂。

图4-14 实数的存储

注意

图4-14只是一个示意图,计算机实际保存时,使用的是二进制数的形式。

4.7.2 实型常量的表示

与整数类型相比,实数类型可以表示更大或更小的数据。在C语言中,整数是不含小数点的数,只要数据中包含有小数点,就将其表示为实数。例如,5 为一个整数,而 5.0就是一个实数。

1.常量的表示方法

实型常量一般有两种表示方式。

一种是直接书写的,包含小数点的实数。例如:

        +0.618
        .618   (省略整数部分)
        95.    (省略小数部分)
        -6.66

对于正数,可省略正号(也可不省略)。对于只有小数部分的数,可省略整数部分的0 (不能省略小数点),对于无小数部分的数,可省略小数点后的 0(同样不能省略小数点,否则就作为整数处理)。

另一种表示方法称为科学计数法,使用这种方法可以表示很大或很小的数据。例如,表示全球人口数量60亿,直接书写为以下形式:

        6000000000.0

而科学计数法表示,可写为以下形式:

        6.0×109

对比两种形式,显然,后一种表现形式更直观,要知道第一种表现形式数据的大小,需要仔细地去数后面有多少个0。

在C语言中,科学计数法使用以下的形式来表示:

        尾数E阶码

尾数和阶码都可包含符号,字母E可以为小写e。

注意

尾数、字母E、阶码之间不能有空格。

例如,将6.0×109编写为C语言程序中的科学计数法,可使用以下几种形式表示:

        6.0E9
        6.e+9
        +6.e+9

2.强制类型

在默认情况下,C编译器将实数常量按double类型存储。使用double的目的是因为该类型的有效位数多一些,能保证计算的精度。但是,因为double要占用8个字节,比float多占一倍的空间,将减慢程序的执行。

在使用float类型的精度就能满足要求的情况下,可以通过后缀f(或F)强制将实数常量按float类型存储。例如,

        float s
        s=3.14F*2.0F*2.0F;

以上代码中,将实数按float类型存储,最后的计算结果保存到float类型的变量中。如果使用以下语句:

        s=3.14*2.0*2.0;

编译器将首先使用 double 类型保存 3 个实数常量,经过运算后,再将其转换为 float类型保存。

若要将实数常量强制保存为long double类型,需在常量中添加后缀l(或L),例如:

        s=3.14L*2.0L*2.0L;

编译器先将常量按long double类型存储。

注意

如果是整型常量添加后缀l(或L),则表示该整数按long类型存储。

4.7.3 实型变量

实型变量的声明与初始化方法与整型变量相同。例如:

        float price=1.8;
        double star,end;
        long double ld_star=5.8333333L;

实型变量与整型变量不同,整型变量能精确地表示数据,而实型变量受有效位数的影响,在超过有效位数后,将采用近似值来表示数据。例如,以下程序将一个很大的 float数加 1,然后再减去该数,按数学公式(f+1-f)计算,得到的结果应该为 1,可在计算机中,却会得到一个不可预知的值。

【程序4-9】实型数据输出示例1

        1:  #include <stdio.h>
        2:
        3:  int main()
        4:  {
        5:      float f=3.14E30,f1;
        6:
        7:      f1=f+1-f;
        8:      printf("f=%f\n",f);
        9:      printf("f+1-f=%f\n",f1);
        10:     getch();
        11:     return 0;
        12: }

编译执行以上程序,得到如图4-15所示的结果。

图4-15 【程序4-9】执行结果

第5行声明变量f,并将其初始化为3.14乘以10的30次方(即314后面跟27个0)。经过第7行程序的计算,第8行和第9行将变量f和计算结果f1输出。

第8行输出变量f的值,应该为314后面跟27个0,但从图4-15可以看出,程序输出结果前面三位数不是314,而变成了313999…。使用的是近似值。

第9行输出变量f1的值,按数学公式(f+1-f)得到的结果应该是1。可程序输出的结果为0。这是因为float类型的数据只能保存6位有效数,而变量f的值按整数表示有31位,在最末一位加上1,因为最末位已经超过了有效位数,保存为float类型时将只取前6位有效位(数的大小由指数来表示),个位加上的1当然将被舍去,使用变量f的值仍未变化,所以再减去f后,得到的结果就为0。

如果 float 类型的数在有效位数范围内,使用(f+1-f)运算就可得到正确的结果。例如将【程序4-9】中变量f的值进行修改,具体程序如下:

【程序4-10】实型数据输出示例2

        1:  #include <stdio.h>
        2:
        3:  int main()
        4:  {
        5:      float f=3.14E5,f1;
        6:
        7:      f1=f+1-f;
        8:      printf("f=%f\n",f);
        9:      printf("f+1-f=%f\n",f1);
        10:     getch();
        11:     return 0;
        12: }

编译执行以上程序,得到如图4-16所示的结果。

图4-16 【程序4-10】执行结果

以上程序中,因变量f的值未超过有效位数,因而,输出结果中没有误差。

4.7.4 实型数据输出

在库函数printf中,可使用以下控制符输出实型数据:

%f,该控制符可用来按十进制记数法,输出float类型和double类型的数据。

%e,该控制符可用来按指数记数法,输出float类型和double类型的数据。

%Lf,%Le,用来输出long double类型的数据。

在printf函数中,若输出的是float类型数据,C语言自动将float类型转换为double类型。在【程序 4-9】和【程序 4-10】的输出结果中可看到,输出数据的有效位数超过了float类型的6位,就是这个原因。

下面的程序演示输出实型数据的方法:

【程序4-11】实型数据输出示例3

        1:  #include <stdio.h>
        2:
        3:  int main()
        4:  {
        5:      float f;
        6:      double d;
        7:
        8:      f=1234567890123.456789;
        9:      d=1234567890123.456789;
        10:     printf("f=%f\t科学计数法f=%e\n",f,f);
        11:     printf("d=%f\t科学计数法d=%e\n",d,d);
        12:     getch();
        13:     return 0;
        14: }

编译执行以上程序,得到如图4-17所示的结果。

图4-17 【程序4-11】执行结果

程序第8行和第9行为float类型变量f和double类型变量d初始化为相同的数据。因为float类型和double类型保存数据的精度不同,所以,在第10行和第11行输出数据时,显示的结果不同。在第9行中,输出float类型的数据,因float类型只能保存6~7位有效数,从第9位开始显示的就是近似数了。第11行中输出double类型的数据,其有效位数为16位,从第17位开始输出的就是近似数。

4.8 混合运算及类型转换

在C语言中,如果表达式中使用了多种类型的数据,计算的结果中,将返回什么类型的数据?例如,用一个 int 类型的整数除以一个 float 类型的实数时,计算结果是返回 int类型还是返回float类型。

4.8.1 混合运算

当不同的数据类型混合在一起运算时,就要出现类型转换。其变换规则很简单,各基本数据类型可排出以下等级:

        long double > double > float > long long > long > int > short > char

左边的类型比右边的类型等级高。

当不同的数据类型参与运算时,C总是将等级低的类型转换为等级高的类型,这种转换称为自动转换,是由编译器自动完成的。

自动转换遵循以下规则:

若参与运算数据的类型不同,则先转换成同一类型,然后进行运算。

转换按数据长度增加的方向进行,以保证精度不降低。如int类型和long类型运算时,先把int类型数据转成long类型数据,然后再进行运算。

所有的浮点运算都是以双精度进行的,即使仅含float单精度运算的表达式,也要先转换成double型,再作运算。

char型和short型参与运算时,必须先转换成int型。

在赋值运算中,无论赋值符右侧的类型如何,都将转换为左侧变量的类型。若右侧数据精度具有较高等级,则将被截短或舍入,使结果与左侧变量的类型相同。

以下代码演示char类型和int类型之间的转换。

【程序4-12】char类型和int类型之间的转换示例

        1:  #include <stdio.h>
        2:
        3:  int main()
        4:  {
        5:      int i1;
        6:      char c1='a',c2;
        7:
        8:      i1=c1;
        9:      c2=i1;
        10:     printf("c1=%d\ti1=%d\tc2=%d\n",c1,i1,c2);
        11:     getch();
        12:     return 0;
        13: }

编译执行以上代码,得到如图4-18所示的结果。

图4-18 【程序4-12】执行结果

以上代码中,第8行将char类型变量c1的值赋给int类型变量i1,此时,char将自动转换为int。

第9行将int型数据转换为char型,此时,只将int型的低位保存到char型中,超出的高位直接丢弃。

以下代码将float转换为int类型,不可进行逆运算,将【程序4-12】的代码修改为以下形式:

【程序4-13】将float转换为int类型示例

        1:  #include <stdio.h>
        2:
        3:  int main()
        4:  {
        5:      int i1;
        6:      float f1=6.18,f2;
        7:
        8:      i1=f1;
        9:      f2=i1;
        10:     printf("f1=%f\ti1=%d\tf2=%f\n",f1,i1,f2);
        11:     getch();
        12:     return 0;
        13: }

编译执行以上代码,得到如图4-19所示的结果。

图4-19 【程序4-13】执行结果

第7行代码将float类型数据赋值给int类型时,小数部分将被截掉。第8行代码将int类型数据赋值给float类型时,变量i1中保存的只是整数值。

4.8.2 强制数据类型转换

在C的表达式中,可使用强制类型转换来显式转换数据类型。强制类型转换是通过类型转换运算来实现的。其使用格式为:

            (类型名)  (表达式)

以上格式将表达式的运算结果转换成类型名所规定的类型。类似于将表达式的结果指定给一个指定类型的变量,然后,再用该变量来参加运算。

例如:

            (double) i      把变量i的值转换为double类型。
            (short)(a+b)    把a+b的结果转换为short类型。

下面的程序演示强制类型转换的效果。

【程序4-14】强制类型转换示例

        1:  #include <stdio.h>
        2
        3:  int main()
        4:  {
        5:      float f=6.18;
        6:
        7:      printf("f=%f\t(int)f=%d\n",f,(int)f);
        8:      getch();
        9:      return 0;
        10: }

第7行代码中,通过(int)f将变量f强制转换为整数输出。转换过后,变量f的值不会改变,仍然为原来的6.18。

4.9 C99新增数据类型

C99 中新增了一些内置数据类型,包括逻辑型(_Bool)和复数类型(_Complex 和_Imaginary),本节介绍这些新增的类型。

4.9.1 逻辑型(_Bool)

C99中增加了_Bool类型,可用来存储数据值0和1(即false和true)。_Bool类型是一个整数类型。

4.9.2 复数类型(_Complex和_Imaginary)

许多科学和工程计算需要复数和虚数。复数具有一个实部和一个虚部,实部就是普通的实数,虚部表示一个虚数,虚数是-1的平方根的倍数。在数学中,复数常写为以下格式:

        2.2+1.8i

其中的i表示-1的平方根。

C99标准支持这些类型,提供了关键字_Complex和_Imaginary,以及附加的头文件和几个新的库函数。

复数类型有三种,分别是:

float _Complex

double _Complex

long double _Complex

注意

基本数据类型和_Complex之间有一个空格。

float _Complex类型的数据在保存时,采用两个相邻的float内存空间,实部的值保存在第1个元素中,虚部的值保存在第2个元素中。C99中也支持三种虚数类型,分别是:

float _Imaginary

double_Imaginary

long double_Imaginary

下面的程序演示了复数相关的操作。

【程序4-15】复数操作示例

        1:  #include <stdio.h>
        2:
        3:  int main()
        4:  {
        5:      double _Complex a,b;
        6:
        7:      a=2+5i;
        8:      b=a+1+2i;
        9:      printf("(%g+%gi)+(%g+%gi)=(%g+%gi)\n",a,(double _Complex)(1+2i),b);
        10:     getch();
        11:     return 0;
        12: }

编译执行以上代码,得到如图4-20所示结果。

图4-20 【程序4-15】执行结果

程序中,第7行给double_Complex变量a赋初值,第8行将变量a加上另一个复数赋值给变量b,第9行使用printf函数输出复数,其中控制符“%g+%gi”对应一个复数的实部和虚部。

4.10 总结

C语言提供了丰富的数据类型,本章详细介绍了基本数据类型的使用。基本数据类型包含两大类:整数类型和实数类型。

1.整数类型

整数类型包括char、short、int、long、long long5种,在C99中,_Bool类型也属于整数类型。

在程序中,整数可以写为十进制、八进制和十六进制的形式。通过在常量前添加前缀来识别其数制。默认情况下,不使用任何前缀时,表示数字常量为十进制数,前缀0表示为八进制数,前缀0x(或0X)表示为十六进制数。

对于数值常量,编译器通常按 int 类型分配存储空间。若需要将数值常量以其他类型存储,需为其添加后缀。后缀l(或L)表示按long类型存储,后缀ll(或LL)表示按long long类型存储。

字符常量必须使用单引号作为定界符,单引号中只能包含一个字符。在单引号中,可包括斜线和一个字母或多个数字来表示一个字符,这种情况称为转义符。

2.实数类型

实数类型包括float、double、long double3种。实数类型与整数类型的存储格式不同,实数类型不能精确地表示一个数,主要用来表示小数、非常大的数和非常小的数。

实数常量可用固定小数点形式表示,如61.8;也可使用科学计数法表示(小数点浮动),如0.618E2。