欢迎光临散文网 会员登陆 & 注册

第 2 讲:数据类型初步

2021-08-16 08:15 作者:SunnieShine  | 我要投稿

我们先要掌握一个概念:数据类型(Data Type)。世间万物都有自己独特的表示方式。例如人的性别只有男、女、其它三种方式就可以完整表达出来,再比如身高只需要是一个正整数来表示就比较合适了,又比如说话的文字可以用语音的形式呈现,也可以用文字形式呈现。在电脑里,每一种想要表示出来的数据都有他们自己的数据表示机制,这种机制就称为数据类型。

在 C 语言里,我们提供了如下一些类型。

布尔型数据在 C 语言委员会在 1999 年才提出该类型的文案,所以 C99 标准才开始支持布尔类型,而现在大多教材使用的还是 ANSI C(C89)标准,即 1989 年的标准,所以教材上给的 C 语言是不支持布尔类型的。

头晕是吧,我们先来说说,什么叫无符号、什么叫带符号(有符号)、什么叫字符、什么叫字符串、什么叫布尔、什么叫二进制小数、什么叫浮点小数、什么又叫精度。

  • 无符号(Unsigned):非负的;

  • 带符号(或有符号,Signed):可以表示负数的;

  • 字符(Character):可以表示出一个普通的英文文本信息的最小单元。这样说不好理解,你可以认为是“英文所有大小写字母 + 数字 + 各种英文标点符号”的集合;

  • 字符串(简称,String):一系列字符;

  • 布尔(Boolean):只用来表示真假情况的;

  • 二进制小数(Binary Decimal):使用二进制格式表达出来的小数;

  • 浮点小数(Floating Point Decimal):二进制小数存储在电脑里的一种存储机制,类似于科学计数法,不过是表达为 a \times 2^b 的格式;

  • 精度(Accuracy):表示表达出一个完整数据和原始数据比较起来的完全精确的程度,在 C 语言里特指小数转为二进制小数后,在存放为浮点小数时,表达出来的数值和原始数值相差多少个小数位。

下面我们来说一下精度和二进制小数的东西。


精度和二进制小数

一般在学校里,老师都会讲解如何把一个数字利用二进制表达出来。可它们并没有讲解,如何表示一个小数,以及一个负数。不过,我会带领大家认识一下,如何表示一个二进制小数,使用 0 和 1。首先,我们举个例子。

将 0.15625 转为 0 和 1

表示小数的方式很简单。既然表示整数利用的是除2取整的模式,那么小数自然就是乘2取余的模式。

%5Cbegin%7Bmatrix%7D%0A0.15625%20%26%20%5Ctimes%20%26%202%20%26%20%3D%20%26%200.3125%20%26%20-%20%26%200%20%26%20%3D%20%26%200.3125%5C%5C%0A0.3125%20%20%26%20%5Ctimes%20%26%202%20%26%20%3D%20%26%200.625%20%20%26%20-%20%26%200%20%26%20%3D%20%26%200.625%5C%5C%0A0.625%20%20%20%26%20%5Ctimes%20%26%202%20%26%20%3D%20%26%201.25%20%20%20%26%20-%20%26%201%20%26%20%3D%20%26%200.25%5C%5C%0A0.25%20%20%20%20%26%20%5Ctimes%20%26%202%20%26%20%3D%20%26%200.5%20%20%20%20%26%20-%20%26%200%20%26%20%3D%20%26%200.5%5C%5C%0A0.5%20%20%20%20%20%26%20%5Ctimes%20%26%202%20%26%20%3D%20%26%201%20%20%20%20%20%20%26%20-%20%26%201%20%26%20%3D%20%26%200%20%26%20(%5Ctext%7B%E7%BB%93%E6%9D%9F%7D)%5C%5C%0A%5Cend%7Bmatrix%7D

大致说一下。从第一步开始,每一次都把数字乘以 2,得到的结果的整数部分提取出来,然后做减法,使得数字一直要保持小于 0 的状态。将得到的结果再一次迭代到下一轮计算里。

比如这个示例里,我们把 0.15625 乘以 2,得到 0.3125,整数部分为 0,把它提取出来,做减法得到 0.3125(因为是减去 0,所以数字并没变化),然后把这个数继续乘以 2,得到 0.625,整数部分为 0,继续提取出来,然后用 0.625 乘以 2,得到 1.25,提取整数部分 1,再将 0.25 乘以 2,继续往下做,直到我们得到 0 结束计算。

最终,我们得到的数字 0、0、1、0、1 即是二进制小数表达的小数部分,由于这个数的整数部分本来就是 0,所以整数部分无需我们作更多的二进制表达的转换,所以这个数的二进制表示就是 0.00101_2


能遇到十进制表示里的循环小数类似的情况吗?

如果我们尝试把 0.3 按上述逻辑转换为二进制小数的时候,就会发现异样:

%5Cbegin%7Bmatrix%7D%0A0.3%20%26%20%5Ctimes%20%26%202%20%26%20%3D%20%26%200.6%20%26%20-%20%26%200%20%26%20%3D%20%26%200.6%5C%5C%0A0.6%20%26%20%5Ctimes%20%26%202%20%26%20%3D%20%26%201.2%20%26%20-%20%26%201%20%26%20%3D%20%26%200.2%5C%5C%0A0.2%20%26%20%5Ctimes%20%26%202%20%26%20%3D%20%26%200.4%20%26%20-%20%26%200%20%26%20%3D%20%26%200.4%5C%5C%0A0.4%20%26%20%5Ctimes%20%26%202%20%26%20%3D%20%26%200.8%20%26%20-%20%26%200%20%26%20%3D%20%26%200.8%5C%5C%0A0.8%20%26%20%5Ctimes%20%26%202%20%26%20%3D%20%26%201.6%20%26%20-%20%26%201%20%26%20%3D%20%26%200.6%20%26%20%5Ctext%7B(%E5%9B%9E%E5%88%B0%200.6%20%E7%9A%84%E5%BE%AA%E7%8E%AF)%7D%5C%5C%0A%20%20%20%20%26%20%20%20%20%20%20%20%20%26%20%20%20%26%20%20%20%26%20%5Ctext%7B...%7D%0A%5Cend%7Bmatrix%7D

可以看到,在计算 0.3 的时候,运算总能得到 0.3、0.6、0.2、0.4、0.8、0.6、0.2、0.4、……的序列,后面无穷无尽。所以,0.3 的二进制表示就是 0.0\dot100\dot1_2


现在再来说精度的问题

正是因为数字并不能完整转换为准确的二进制表示,比如 0.3,所以表示上一定会存在精度的偏差。比如 0.3 在二进制转换后,由于无穷无尽的关系,我们只能在 floatdoublelong double 类型表示完整的二进制位数后,舍弃掉后面的部分,于是这个数肯定就不等于原始数值了。

不过,二进制表达是很复杂的,所以这里我不打算展开讲解,有兴趣的小伙伴可以看下网上有关 IEEE 754 的标准规范,这个就是专门规定如何把小数表示为二进制表示,存储到程序里的规范。

这里告诉大家的是,经过这种精心转换的模式后,我们有了如下的结论:

  • float 类型的小数,精度是表示为十进制数的情况下的 6 或 7 位有效数字(6 或 7 的其一,详细情况则取决于二进制表示,这里不展开说明);

  • double 类型的小数,精度是表示为十进制数的情况下的 14 或 15 位有效数字;

  • long double 类型取决于系统实现,所以这里无法给出精确的范围,大约精确到 19 位有效数字。


所占字节大小

大家都知道,数据存储到电脑里都是以一个叫做“字节”的单位作为衡量依据的,那么这些数据类型都对应了一个固定的、绝对的字节数,它就表示元素存进去时,用这个类型就需要占据多大的内存空间的容量。表格如下:

如果你有时候记不住它们的大小,可以用代码写成这样来得到结果:

我们把上面提到的类型代号写到这里,就可以输出对应的结果了。比如

因为这些所占字节数是可以测出来的,所以一般不要求记住它们,但考试会考,所以这些数值还是希望你记住,就好像化学元素的相对原子质量,常见的是需要作出记忆的。

然后,先不要管这个代码里的 %d 是啥,稍后会作出解释。


使用

变量的定义和声明

在数学里,表示一个可以变化的数据信息是通过变量来表达的,比如 f(x) = 3x^2 + 5x + 7 这个表达式里,x 就是所谓的自变量,而 f(x) 称为函数,如果写成 y 则又称为因变量。在 C 语言里,我们不区分所谓的自变量和因变量,只要能写成一个数值的表示形式,就称为变量。

变量的形式一般是这样的:

  1. 数据类型 变量名;(简单声明)

  2. 数据类型 变量名 = 数值;(赋值数值常量)

  3. 数据类型 变量名 = 计算表达式;(赋值表达式)

  4. 数据类型 变量1, 变量2, 变量3;(不赋值)

  5. 数据类型 变量1, 变量2 = 多少, 变量3;(不全赋值)

  6. 数据类型 变量1 = 多少, 变量2 = 多少, 变量3 = 多少;(全赋值)

比如,一个程序写成这样

将得到

其中的 \' 在前文提到过,以 \ 开头的、写在双引号里的内容可以表示一个另外意义的表达结果,这里 \'` 表示输出一个 `'` 符号。然后 `%d` 则在这里表示 `printf` 语句小括号里第一个逗号后的变量的信息结果。这里就表示 `age` 这个变量的数值(23),然后把 23 替换到双引号的 `m` 和 `y 之间。

顺带一说,双引号的内容就称为一个字符串,而每一个字母(包括空格、\' 等等符号,都表示一个字符)。


变量的自定义输入

再拿出 int 作为数据类型举例。既然有输出就应该由输入。如果需要我们自己输进去一个数字,然后程序输出这个结果,就需要用到新的写法:scanf 语句。

这样就可以了。注意 scanf 语句的写法,逗号后跟的 age 变量必须要在前面加前缀符号 &,这一点在后面再说。

可以发现,C 语言是一种比较奇特的语言,它的大多数语法的耦合度较高,即前面的知识点要用超纲的内容做铺垫,而后面的知识点却又依赖于前面的知识点,所以……写教程真的很伤脑筋啊……

当然,scanf 语句也可以同时为多个变量赋值。比如

请注意,每一个变量后都得加 & 前缀符号,而且在运行程序的时候,你必须得跟着这个字符串里写的格式写,比如这个字符串 %d, %d 里含有一个数、逗号、空格和一个数,所以你在输入的时候必须格外小心,照着这个格式写内容:

上述四种格式都是正确的。另外需要注意的是,scanf 语句甚至不以换行作为分割,所以你可以这么输入:

这样的书写格式里所有的换行都是不被读取的,所以你的所有换行都会被“抹掉”,故这种格式最终还是

但如果没有严格按照格式书写,比如

这些格式都是错的,最终只有 a 会正确得到 12,而 b 依旧是没有数值的。

当然,你也可以分写两句:

不过后者写起来怪麻烦的。

切记,字符串里带有空格的 scanf 语句,在运行程序时,你也要在输入完第一个数据时,空一格输入第二个数据。当然,后者这种格式,在输入的时候可以用回车的方式分割两个输入数据。


格式化字符串的定义和使用

下面还要来看看,什么是格式化字符串(Formatting String)。其实,说白了,前文出现的 %d 就是格式化字符串的一种。但请注意,它只是其中一种,如果你把它认为是输出数值结果的话,那就错了。所有的格式化字符串如下。

要把数据输入到电脑里面去,我们需要用到的是C语言自带的输入功能,写法也有两种不同的类型:

这样就表示输入一个小数,然后利用这个叫做格式化字符串的东西给它把输入的“黑框”上面显示的那个数字字符改变为小数的数据,便于放置到prize所在的位置上去,既然是所在位置,所以需要用到&了,这个符号就是专门用来找位置的。

另需要你记忆一些格式化字符串的写法:

大家只需要记住前面四种和最后两种就可以了。

如果我又定义了一个变量,是 int,就用 %d 转为整数存放到电脑之中。

比如

就将整数变量 a 输入到电脑里面用整数规范存储了。如果格式化字符串用成其他的,比如 %f,结果就很怪异。所以一定要配套。

输入两次的话,可以简写成一次输入:

注意,scanf 语句的字符串里中间最好要有字符。说白了就是不要写成 %d%d 挨在一起的写法。如果不给空格虽然程序不会出错,但是这样让容易出现安全漏洞,诸如缓存区溢出等。


格式化字符串的排版控制

因为是在控制台界面运行的,所以空格就可以用来排版了,不过有些时候,比如我们想让某个数输出的时候有对齐的效果,就会按情况来计算占据的显示的格数。比如一个两位数一定是占两格的,如果我们强制让所有输出的数值都占三格呢?

我们可以在 %d 之间添加数值以控制占的格数。比如 %2d 表示占两格,%6d 表示占六格。如果位数不够的,前面会有空格占位。比如说

输出的样子就是

就很好看。小数的话,除了可以控制输出的占格数,还可以控制输出的小数位数,如果你不希望小数位后很多零的话,可以这么写:%占格数.小数位格数f,是可以的。占格数是计算这个输入的字符一共有多少个,而不是整数位多少位。比如

将输出

特别要注意前面的空格。

如果是输入里插入这样的格式化字符串的话,则会严格按照这样的标准执行输入,如果超出范围的部分会被直接舍弃掉,或是拿给下一个部分使用。比如

那么提示输入的时候,如果输入的是 12345 的话,由于前面 %3d 的控制,后面的 45 会被切掉,而 b 恰好要两位数,所以 a 为 123,b 是 45,输出也是一样。

但如果超出了范围,比如输入了 123456,则依然结果是 123 和 45,6 会被舍弃。

最后讲一下这些语句的写法规范。


规范

代码格式规范

其实呢,编译器这位翻译官将你的 C 语言代码给它转变为系统能够识别和操作的语言之前,会给你的代码进行排版,这个时候,分号这些就显得额外重要。

一句话一定要用分号结尾,表示你这个操作做完了。不过换行不是必须有的,你可以把代码全写在一行,也可以换行写,只要有分号怎么写都无所谓,但是为了代码好看整洁,建议换行。

这样当然可以了,不过肯定不好看!


标识符命名规范

在前文里我们提到了标签和变量,它们都遵循一个命名规则。这个规则保证了程序正确的执行。命名规范其实在前文已经说到了。

  1. 数字、字母、下划线 _ 三者的组合;

  2. 第一个字符不能是数字;

  3. 不能是前文用到过的那些 C 语言自带的字符组合,如 int 等(注意,此时 scanf 还不是 C 语言固有的东西,也就是说你可以把 scanfprintfmain 这种东西定义为变量名,但完全不建议这么做)。

只要满足这 3 点就可以了。比如 helloagetest_01T_To_O 都是正确的标识符,而 2_4^_^ 则不行。但建议名字取得有意义,因为下文可能会用到它们,取一个有意义的名字对语义理解有帮助。


类型规范

可以在上文看到很多有关变量的定义的语句。但可以发现到的是,大多整数类型的变量全部被定义为了 int,而在后面的很多内容里也会这么干。你可能会问,虽然 int 的范围较大,且包含大多我们常见的元素的量,但 int 存储的字节数(占的空间大小)显然比 short 这些类型要大上一倍,甚至比 char 大上三倍,那为什么像是年龄这种东西不用 short 类型,甚至是更精确的 unsigned short 类型存储呢?年龄一般在 150 以下,而且 short 的范围最大到 32767,显然是够用的。

原因在于,int 是非常常用的数据类型,这其实算作一种习惯性使用。上文提到的说法是正确而且合理的,但一般我们出于计算和兼容性而言,用 int 居多。所以,如果你觉得确实需要降低内存使用,或者 int 不够用的话,才确定具体的使用类型,而一般都尽量使用 int 类型。


第 2 讲:数据类型初步的评论 (共 条)

分享到微博请遵守国家法律