数据和C——万字详解(C Primer Plus)
前言
程序离不开数据。把数据、字母和文字输入计算机,就是希望它利用这些数据完成某些任务。例如,需要计算一份利息或显示一份葡萄酒商的排序列表。本篇文章除了介绍如何读取数据外,还将教会读者如何操控数据。
C语言提供了两大系列的多种数据类型。本篇文章将详细介绍两大数据类型:整数类型和浮点数类型,详细介绍这些数据类型是什么、如何声明它们、如何及何时使用它们。除此之外,还将介绍变量和常量的区别。
1 变量和常量数据
在程序得指导下,计算机可以做许多事情,如数值计算、画画、计算彗星轨道或其他你想做的事情。要完成这任务,程序需要使用数据,即承载信息的数字或字符。有些数据类型在程序使用之前预先设定好了,在整个运行过程中没有变化,这些称为常量。其他数据类型在运行期间可能或发生改变或赋值,这些称为变量。例如3.1415是一个常量,而weight是一个变量,可以通过赋值等方式发生改变。
2 数据:数据类型关键字
不仅变量和常量不同,不同的数据类型之间也有差异(一些数据类型表示数值,一些数据类型表示字符)。C通过识别一些基本的数据类型来区分和使用这些不同的数据类型。如果数据类型是常量,编译器会通过用户书写的形式来识别类型(如,43是整数,43.0000是浮点数)。但对于变量而言,需要在声明时指定其类型,稍后将详细介绍。现在先来看看C语言的基本类型关键字。
在C语言中,用int关键字来表示基本的数据类型。后3个关键字(long、short 和 unsigned)和C90新增的signed用于提供基本整数类型的变式。例如unsigned short int 和 long long int。char关键字用于指定字母和其他字符(如#,&,@和*)。另外,char类型也可以表示较小的整数.。float、double和long double表示带小数点的数。_Bool 类型表示布尔值(true 或false),_Complex和_Imaginary分别表示复数和虚数。
通过这些关键字创建的类型,按计算机的存储方式可分为两大基本数据类型:整数类型和浮点数类型。
2.1 整数和浮点数
整数类型?浮点数类型?
对我们而言,整数和浮点数的区别是它们的书写形式不同。对计算机而言,它们的区别就是存储方式不同。
2.2 整数
和数学概念一样,在C语言中,整数是没有小数的数。同时,计算机以二进制的方式数字来存储整数。例如,整数7以二进制写是111.因此,要在8位字节中存储该数字,需要把前5位都设置位0,后3位设置位1(如图3.2)
2.3 浮点数
浮点数与数学中的实数概念差不多。2.34、3.43E7、6.00、和2e-4都是浮点数。注意,在一个值后面加一个小数点,该值就变成了浮点数。因此,6是整数,6.00是浮点数。
但需要注意的是,整数和浮点数在计算机中的存储方式不同。计算机把浮点数分成小数部分和指数部分来表示,而且分开存储这两部分。因此,虽然6.00和6在数值上相等,但它们的存储方式不同。在十进制下,6.0可以写成0.6E1。这里0.6是小数部分,1是指数部分。
Tips:
- 整数没有小数部分,浮点数有小数部分。
- 浮点数可以表示的范围比整数大。
- 对于一些算数计算(如,两个很大的数相减),浮点数损失的精度更多。
- 因为在任何区间(如。1.0到2.0之间)都存在无穷多个实数,所以计算机的浮点数不能表示区间的所有值,浮点数通常只是实际值的近似值。例如,6.0可能被存储为浮点值5.99999。
- 在过去,浮点数运算比整数慢。不过现在许多CPU都包含浮点处理器,缩小了速度上的差距。
3 C语言基本数据类型
3.1 int类型
C语言提供了许多数据类型,为什么一种类型不够用?因为C语言让程序员针对不同情况选择不同类型。一般情况使用int类型即可,但是为满足特定任务和机器的要求,还可以选择其它类型。
int类型是有符号整型,其取值范围依计算机系统而异。一般而言,存储一个int要占用一个机器字长。一般而言,系统用一个特殊位的值来表示有符号整数的正负号。
3.1.1 声明int变量
//int 两种声明方式
int num;
int hogs,cows,goats;
3.1.2 初始化变量
以上声明只是创建了变量,但是并没有给他们提供值。变量如何获得值?C语言提供了3中实现方式。第一种是赋值,第二种是通过scanf()来获得值。第三种则是初始化。
初始化变量就是为变量赋一个初始值。在C语言中,初始化可以直接在声明中完成。只需在变量名后加上赋值运算数(=)和待赋给变量的值。
int hogs=23;
int cows 123 , goats=12;
int dogs , cats=100; //有效,但这种代码风格很糟糕
简而言之,初始化为变量创建和标记存储空间,并为其指定初始值。
3.1.3 打印int值
我们可以用printf()函数来打印int的值,同时用%d(转换说明)来指定printf()以什么格式来显示一个值。这个值可以是int类型的变量、常量或int类型的常量表达式。
#include <stdio.h>
int main()
{
int a = 10;
int b = 2;
printf("%d %d %d", a, 2, a - b);//变量、常量、表达式
return 0;
}
3.1.4 八进制和十六进制
通常,C语言都假定整型常量是十进制。但在表达与计算机相关的值时,显然八进制和十六进制更方便(都是2的幂)。在C语言中,用特定的前缀来表示时使用那种进制。0x和0X前缀表示十六进制(十进制16表示成十六进制是0x10或0X10),0表示八进制(十进制16表示成八进制是020)。
3.1.5 显示八进制和十六进制
在C语言中,既可以使用使用也可以显示不同进制的数。不同的进制需要使用不同的转换说明。以十进制显示数字,使用%d;以八进制显示数字,使用%o;以十六进制显示数字,使用%x。另外要显示各进制数的前缀0、0x、0X,必须分别使用%#o、%#x、%#X。
#include <stdio.h>
int main()
{
int x = 100;
printf("dec=%d; octal=%o; hex=%x\n", x, x, x);
printf("dec=%d; octal=%#o; hex=%#x\n", x, x, x);
return 0;
}
3.2 其他数据类型
在C语言中,除int外,还有其它整数类型需要了解。
C语言提供3个附属关键字修饰基本整数类型:short、long和unsigned。
- short int类型(简写为short)占用的存储空间可能比int类型少,常用于较小数值的场合以节省空间。(有符号类型)
- long int或long占用的存储空间可能比int多,适用于较大数值的
场合。(有符号类型)- long long int或long long(C99标准加入)占用的储存空间可能
比long多,适用于更大数值的场合。该类型至少占64位。(有符号类型)- unsigned int或unsigned只用于非负值的场合。这种类型与有符
号类型表示的范围不同。例如,16位unsigned int允许的取值范围是0
~65535,而不是-32768~32767。用于表示正负号的位现在用于表示
另一个二进制位,所以无符号整型可以表示更大的数。在C90标准中,添加了unsigned long int或unsigned long和unsigned int或unsigned short类型。C99标准又添加了unsigned long long int或unsigned long long。
Tips:
C语言只规定了short占用的存储空间不能多于int,long占用的存储空间不能少于int,以适应不同的机器。
3.3 整数溢出
如果整数溢出会发生什么呢?
//2147483647和2147483647分别为int和unsigned int所能表达最大值
#include <stdio.h>
int main()
{
int i = 2147483647;
unsigned int j = 2147483647;
printf("%d %d %d\n", i, i + 1, i + 2);
printf("%u %u %u\n", j, j + 1, j + 2);
return 0;
}
读者可以把无符号整数j看作是汽车的里程表。当达到它能表示的最大值时,会重新从起始点开始。整数 i 也是类似的情况。它们主要的区别是,在超过最大值时,unsigned int 类型的变量 j 从 0开始;而int类型的变量i则从−2147483648开始。注意,当i超出(溢出)其相应类型所能表示的最大值时,系统并未通知用户。因此,在编程时必须自己注意这类问题。溢出行为是未定义的行为,C 标准并未定义有符号类型的溢出规则。以上描述的溢出行为比较有代表性,但是也可能会出现其他情况。
3.4 打印short、long、long long和unsigned类型
打印unsigned int类型的值,使用%u转换说明;打印long类型的值,使用%ld转换说明。%lx表示以十六进制格式打印long类型整数,%lo表示以八进制格式打印long类型整数。注意,虽然C允许使用大写或小写的常量后缀,但是在转换说明中只能用小写。
对于short类型,可以使用h前缀。%hd表示以十进制显示short类型的整数,%ho表示以八进制显示short类型的整数。h和l前缀都可以和u一起使用,用于表示无符号类型。例如,%lu表示打印unsigned long类型的值。对于支持long long类型的系统,%lld和%llu分别表示有符号和无符号类型。
#include <stdio.h>
int main()
{
short end = 200;
long big = 65537;
long long verybig = 12345678908642;
printf("end=%u and not %d\n", end, end);
printf("big=%ld and not %hd\n", big,big );
printf("verybig=%lld and not %ld\n", verybig,verybig );
return 0;
}
- 对于short类型的变量end,在printf()中无论指定以short类型(%hd)还是int类型(%d)打印,打印出来的值都相同。这是因为在给函数传递参数时,C编译器把short类型的值自动转换成int类型的值。
为什么要为什么要进行转换?h修饰符有什么用?
进行转换是因为int类型被认为是计算机处理整数类型时最高效的类型。因此,在short和int类型的大小不同的计算机中,用int类型的参数传递速度更快。
使用h修饰符可以显示较大整数被截断成 short 类型值的情况。
4 使用字符:char类型
char类型用于储存字符(如,字母或标点符号),但是从技术层面看,char是整数类型。因为char类型实际上储存的是整数而不是字符。计算机使用数字编码(ASCII编码)来处理字符。
4.1 声明char类型变量
//char类型变量的声明方式与其他类型变量的声明方式相同
char ibs;
char its,ics;
//以上声明创建了3个char类型的变量:ibs、its、ics
4.2 字符常量和初始化
如果要把一个字符常量初始化为字母 A,不必背下 ASCII 码,只需通过以下初始化把字母A赋给变量即可:
//以变量名为grade为例
char grade = 'A';
在 C 语 言 中 , 用 单 引 号 括 起 来 的 单 个 字 符 被 称 为 字 符 常 量。编译器一发现’A’,就会将其转换成相应的代码值。
char ibs; //声明一个char类型变量
char ibs='T'; //正确赋值
char ibs=T; //错误!T为变量
char ibs="T"; //错误!"T"是一个字符串
4.3 非打印字符
单引号只适用于字符、数字和标点符号,浏览ASCII表会发现,有些ASCII字符打印不出来。例如,一些代表行为的字符(如,退格、换行、终端响铃或蜂鸣)。C语言提供了3种方法表示这些字符。第1种方法前面介绍过——使用ASCII码。例如,蜂鸣字符的ASCII值是7,因此可以这样写:char beep = 7; 第 2 种方法是,使用特殊的符号序列表示一些特殊的字符。这些符号序列叫作转义序列
4.4 打印字符
printf()函数用%c指明待打印的字符。一个字符变量实际上被储存为1字节的整数值因此,如果用%d转换说明打印类型变量的值,打印的是一个整数。而%c转换说明告诉printf()打印该整数值对应的字符。
#include <stdio.h>
int main()
{ //以字符C为例
char ch;
scanf("%c", &ch);
printf("%d %c", ch, ch);
return 0;
}
5 _Bool类型
C99标准添加了_Bool类型,用于表示布尔值,即逻辑值true和false。因为C语言用值1表示true,值0表示false,所以_Bool类型实际上也是一种整数类型。但原则上它仅占用1位存储空间,因为对0和1而言,1位的存储空间足够了。
6 float、double和long double
各种整数类型对大多数软件开发项目而言够用了。然而,面向金融和数学的程序经常使用浮点数。C语言中的浮点类型有float、double和long double类型。浮点类型能表示包括小数在内更大范围的数。浮点数的表示类似于科学记数法(即用小数乘以10的幂来表示数字)。该记数系统常用于表示非常大或非常小的数。表3.3列出了一些示例。第1列是一般记数法;第2列是科学记数法;第3列是指数记数法(或称为e记数法),这是科学记数法在计算机中的写法,e后面的数字代表10的指数。
C标准规定,float类型必须至少能表示6位有效数字,且取值范围至少是10-37~10+37。前一项规定指float类型必须能够表示33.333333的前6为数字,而不是精确到小数点后6位。后一项规定用于方便地表示诸如太阳质量(2.0e30千克)、一个质子的电荷量(1.6e-19库仑。通常,系统储存一个浮点数要占用32位。其中8位用于表示指数的值和符号,剩下24位用于表示非指数部分(也叫作尾数或有效数)及其符号。
C语言提供的另一种浮点类型是double(意为双精度)。double类型和float类型的最小取值范围相同,但至少必须能表示10位有效数字。一般情况下,double占用64位而不是32位。一些系统将多出的 32位全部用来表示非指数部分,这不仅增加了有效数字的位数(即提高了精度),而且还减少了舍入误差。另一些系统把其中的一些位分配给指数部分,以容纳更大的指数,从而增加了可表示数的范围。无论哪种方法,double类型的值至少有13位有效数字,超过了标准的最低位数规定。
C语言的第3种浮点类型是long double,以满足比double类型更高的精度要求。不过,C只保证long double类型至少与double类型的精度相同。
6.1 声明浮点型变量
float ibc,ibs;
double ibd;
float ibf=6.63e-34;
long double ibg;
6.2 浮点型常量
在代码中,可以用多种形式书写浮点型常量。可以没有小数点(如,2E5)或指数部分(如,19.28),但是不能同时省略两者。可以省略小数部分(如,3.E16)或整数部分(如,.45E-6),但是不能同时省略两者。
3.14159
.2
4e16
.8e-2
123.
Tips:
默认情况下,编译器假定浮点型常量是double类型的精度。
//假设some是浮点型变量
some=3.0*4.0;
通常,3.0和4.0被储存为64位的double类型,使用双精度进行乘法运算,然后将乘积截断成float类型的宽度。这样做虽然计算精度更高,但是会减慢程序的运行速度。
C99 标准添加了一种新的浮点型常量格式——用十六进制表示浮点型常量,即在十六进制数前加上十六进制前缀(0x或0X),用p和P分别代替e和E,用2的幂代替10的幂(即,p计数法)。如下所示:
0xa.1fp10
十六进制a等于十进制10,.1f是1/16加上15/256,p10是2^10或1024。0xa.1fp10表示的值是(10+1/16+15/256)×1024(即十进制10364.0)。
6.3 打印浮点值
printf()函数使用%f和%lf转换说明打印十进制记数法的float和double类型浮数,用%e打印指数记数法的浮点数。如果系统支持十六进制格式的浮点数,可用a和A分别代替e和E。打印long double类型要使用%Lf、%Le或%La转换明。给那些未在函数原型中显式说明参数类型的函数(如,printf())传递参时,C编译器会把float类型的值自动转换成double类型。
#include <stdio.h>
int main()
{
float ibs = 32000.0;
double ibc = 2.14e9;
long double ibf = 5.32e-5;
printf("%f can be written %e\n", ibs, ibs);
printf("And it's %a in hexadecimal,powers of 2 notation\n", ibs);
printf("%f can be weitten %e\n", ibc, ibc);
printf("%Lf can be written %Le\n", ibf, ibf);
return 0;
}
6.4 浮点值上溢和下溢
首先,我们来看看这段代码
#include <stdio.h>
int main()
{
float toobig = 3.12E123*10000000;
printf("%f", toobig);
return 0;
}
这是一个上溢的示例。当计算导致数字过大,超过当前类型能表达的范围时,就会发生上溢。这种行为在过去是未定义的,不过现在C语言规定,在这种情况下会给toobig赋一个表示无穷大的特定值,而且printf()显示该值为inf或infinity(或者具有无穷含义的其他内容)。
接下来在来看看这段代码
#include <stdio.h>
int main()
{
float x = 0.1234E-2;
printf("%f", x/10);
return 0;
}
当除以一个很小的数时,情况更为复杂。float类型的数以指数和尾数部分来储存。存在这样一个数,它的指数部分是最小值,即由全部可用位表示的最小尾数值。该数字是float类型能用全部精度表示的最小数字。现在把它除以 2。通常,这个操作会减小指数部分,但是假设的情况中,指数已经是最小值了。所以计算机只好把尾数部分的位向右移,空出第 1 个二进制位,并丢弃最后一个二进制数。以十进制为例,把一个有4位有效数字的数(如,0.1234E-10)除以10,得到的结果是0.0123E-10。虽然得到了结果,但是在计算过程中 却 损 失 了 原 末 尾 有 效 位 上 的 数 字 。 这 种 情 况 叫 作 下 溢(underflow)。C语言把损失了类型全精度的浮点值称为低于正常的(subnormal)浮点值。因此,把最小的正浮点数除以 2将得到一个低于正常的值。如果除以一个非常大的值,会导致所有的位都为0。
7 复数和虚数类型
许多科学和工程计算都要用到复数和虚数。C99 标准支持复数类型和虚数类型,但是有所保留。一般而言,虚数类型都是可选项。C11标准把整个复数软件包都作为可选项。
简 而 言 之 , C 语 言 有 3 种 复 数 类 型 : float_Complex 、double_Complex和long double _Complex。例如,float _Complex类型的变量应包含两个float类型的值,分别表示复数的实部和虚部。类似 地 , C 语 言 的 3 种 虚 数 类 型 是 float _Imaginary 、 double_Imaginary和long double _Imaginary。
结尾
本片文章到此就结束了。如果对你有帮助,记得点赞加关注哦!感谢你的支持!