购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

5.4 进制

5.4.1 什么是进制

进制是学习计算机语言最基本的知识,所以一定要掌握。其实它很简单,我们日常生活中有很多进制的例子,如一分钟六十秒,逢六十进一,就是六十进制;一天二十四小时,逢二十四进一,就是二十四进制;一星期七天,逢七进一,就是七进制;一年十二个月,逢十二进一,就是十二进制;小学数学是逢十进一,就是十进制;而计算机中的数据只有0和1,逢二进一,就是二进制。

所以进制就是逢几进一,r进制就是逢r进一。计算机只能识别二进制,人类最习惯使用的是十进制,而为了实际需要,又建立了八进制和十六进制。八进制就是逢八进一,十六进制就是逢十六进一。

C语言中规定了八进制数前面要加0(注意是数字零而不是字母o),十六进制数前面要加0x或0X,而十进制前面什么都不加。这是为什么呢?比如5,到底是十进制、八进制还是十六进制?什么都不加就默认是十进制。如果希望5是八进制,那么前面就加上0;如果希望5是十六进制,那么前面就加上0x或0X。

十进制为逢十进一,它只有0、1、2、3、4、5、6、7、8、9这十个基数。逢十进一的意思就是:9再加1就变为10,即向十位进了一位,原来个位回归0。

二进制为逢二进一,它只有0和1两个基数。逢二进一的意思就是:1再加1就变成10,即向前进了一位,原来的1变成0;再加1就是11;再加1又逢二,再往前进一位,进一位后第二个1又逢二再进位,就是100了;再加1变成101,再加1变成110,再加1变成111,再加1变成1000……

二进制和十进制有一个对应的关系:

那么十进制的5和二进制的101代表的是不是同一个数字?答案是“是”,它们本质上是同一个数字。无论是十进制、二进制、八进制还是十六进制,都只是计数的一种方式,只不过它们用的是不同的进制,所以表现形式不一样,但本质上都是同一个数字。理解了上面内容,后面很多知识就很容易理解了。

八进制就是逢八进一,它只有0、1、2、3、4、5、6、7这八个基数。

由上可以总结出,r进制有r个基数,而且基数里面最大的是r–1,因为基数都是从0开始的。比如五进制中,基数最大的是4,基数分别为0、1、2、3、4。

十六进制肯定有十六个基数。它的基数除了十进制的0~9之外,还有字母A~F,总共加起来是十六个。注意,字母不区分大小写。十六进制是逢十六进一,F是十五,加1就变成十六了,逢十六就进一,即0x10。

下面是常用进制对照表,大家可以看一下。

5.4.2 进制转换口算法

我们来思考一个问题:为什么八进制数17对应的十进制数是15?我们看八进制数17中的7,这个7是没有进位的,它同十进制数7是一样的,因为它是在个位。而八进制数17中的1,它能进一是因为有8才进一的,所以这个1代表的就是十进制的8。所以一个8和一个7加起来就是15,这就是为什么八进制数17对应的十进制数是15的原因。

现在我来考考读者,看读者能不能立刻回答出来。八进制数23对应的十进制数是多少?十进制数34对应的八进制数是多少?

同样,八进制数23中的3和十进制数3是一样的,而2说明进了两次位,有8才能进一次,进了两次说明是十进制的16,所以16和3加起来就是19。因此八进制数23对应的就是十进制数19。

十进制数34里有4个8,余2,所以十进制数34对应的就是八进制的42。

我再来问问大家,看大家能不能举一反三:十六进制的3D对应的十进制是多少?十进制的83对应的十六进制是多少?

方法是相同的,十六进制数3D中的D表示的是十进制的13,而十六进制达到16才能进一次,数字3说明进了3次,即48,13和48加起来就是61。因此,十六进制数3D就对应十进制数61。同样,十进制83中有5个16,余3,所以十进制数83就是十六进制数53。

如果是八进制和十六进制相互转换的话,因为它们都跟十进制有关系,所以可以将十进制当作一个桥梁,先转换成十进制,然后再转换成另一个进制。

以上的口算方法实际上就是进制转换的本质和奥秘的总结。但是用口算方法只能计算比较小的数字,当数字比较大的时候还是得在纸上算。之所以没有讲如何用口算进行二进制转换,原因就是二进制的计算量很大,不适合口算。介绍口算方法的主要目的是想让大家体会进制转换的本质,从而能够深刻理解下面所讲的公式法。

5.4.3 进制转换公式法

以上讲的是进制转换的本质,下面系统地讲一下进制转换。在阅读本节之前建议大家先掌握上节的内容,这样效率会更高,理解会更深刻。

1.r进制转换成十进制

r进制数a n a n–1 …a 1 a 0 对应的十进制数为:a n ×r n +a n–1 ×r n–1 +…+a 1 ×r 1 +a 0 ×r 0

下面给大家举几个例子:

(1011011) 2 =1×2 6 +0×2 5 +1×2 4 +1×2 3 +0×2 2 +1×2 1 +1×2 0 =64+0+16+8+0+2+1=91

(356) 8 =3×8 2 +5×8 1 +6×8 0 =192+40+6=238

(2FB) 16 =2×16 2 +15×16 1 +11×16 0 =512+240+11=763

2.十进制转换成r进制

方法:除r取余数,直至商为零,余数倒序排序。

下面给大家举个例子:十进制185分别转换成二进制、八进制和十六进制。

所以(185) 10 =(10111001) 2

所以(185) 10 =(271) 8

(185) 10 =(B9) 16

3.进制之间的转换

上面讲了十进制和r进制之间的相互转换。可以说十进制是任意进制间相互转换的桥梁,任何进制都可以先转换成十进制,然后再转换成需要的进制。

但二进制和八进制、二进制和十六进制之间的相互转换可以直接计算。二进制的运算首先要记住窍门:8421。(1111) 2 =1×2 3 +1×2 2 +1×2 1 +1×2 0 =8+4+2+1,即二进制数1111从左到右每一位分别代表十进制的8、4、2、1。

(1)二进制转换为八进制

将二进制数从右到左,每三位组成一组,最左边不足三位的补零。然后对每组分别运用8421法则快速运算。如果二进制是1则保留,如果是0则舍去。比如:

(1111) 2 =8+4+2+1=15

(1010) 2 =8+0+2+0=10

(1100) 2 =8+4+0+0=12

(0101) 2 =0+4+0+1=5

所有的二进制转其他进制的运算都要记住这个法则。如果是二进制转十进制,且二进制数多于四位,那么其他位依次为16、32、64、128……

只不过二进制转八进制时,因为是每三位为一组,所以就不存在第四位,这样8就都为0了,所以其实是421法则,但统一记为“8421”更顺口。

练习:(11001011) 2 =(?) 8

首先,从右到左分成三组,最左边不足三位的补零,即011001011。然后对每组分别运用“8421”快速运算即313。所以(11001011) 2 =(313) 8

(2)二进制转换为十六进制

将二进制数从右到左,每四位组成一组,最左边不足四位的补零。然后对每组分别运用“8421”法则快速运算。

练习:(1011001011) 2 =(?) 16

首先,从右到左分成四组,最左边不足四位的补零,即001011001011。然后对每组分别运用“8421”法则快速运算即2 C B。所以(11001011) 2 =(2CB) 16

(3)八进制转换为二进制

对于每一位八进制数,分别运用“8421”法则快速运算,逐位展开成三位二进制数,不足三位的补零,最后最左边的零可省略。

练习:(3754) 8 =(?) 2

(3) 8 =(011) 2 ,(7) 8 =(111) 2 ,(5) 8 =(101) 2 ,(4) 8 =(100) 2 ,所以(3754) 8 =(11111101100) 2

(4)十六进制转换为二进制

对于每一位十六进制数,分别运用“8421”法则快速运算,逐位展开成四位二进制数,不足四位的补零,最后最左边的零可省略。

练习:(4B39F) 16 =(?) 2

(4) 16 =(0100) 2 ,(B) 16 =(1011) 2 ,(3) 16 =(0011) 2 ,(9) 16 =(1001) 2 ,(F) 16 =(1111) 2 ,所以(4B39F) 16 =(1001011001110011111) 2

最后还有一个“小数部分的进制转换”,这个几乎用不到,所以就不讲了,要是以后用到了再看也不迟。

接下来给大家写一个程序。这个程序的功能是将同一个十进制数以不同的进制显示出来。这个程序大家暂时还看不懂,没关系,等学到后面再来看这个程序就很简单了。


# include <stdio.h>
int main(void)
{
    int i = 63;
    printf("i = %d\n", i); 
    printf("i = %o\n", i);
    printf("i = %x\n", i);
    printf("i = %X\n", i);
    return 0;
}
/*VC++ 6.0中的输出结果是:
--------------------------------------
i = 63
i = 77
i = 3f
i = 3F
--------------------------------------
*/

其中:%d表示以十进制输出;%o表示以八进制输出,注意是字母o不是数字0,而且一定是小写字母。这与前面讲的八进制数是数字0而不是字母o正好是相反的,千万不要弄混了。

%x和%X表示以十六进制输出。那么它们有什么区别呢?如果是%x那么字母就是以小写的形式输出,如果是%X那么字母就是以大写的形式输出。这也是八进制中只有%o没有%O的原因,因为八进制中根本没有字母,所以不需要区分大小写。

5.4.4 人类为什么最习惯用十进制

人类为什么最习惯用逢十进一的十进制?对于世界上大部分的国家,即便之前因为交通不发达,他们彼此之间并不知道在世界的某一个角落还有另一个民族的存在,但是他们使用的都是十进制!这是为什么?原因很简单,就是因为我们都有十个手指!进制的起源是用于记数的,人类刚开始都是用手指计数的。即使是现在的小朋友算数也还是喜欢用手指,所以人类最习惯用十进制。

5.4.5 计算机为什么用的是二进制

那么为什么计算机使用的是二进制呢?

因为二进制从硬件上比较容易实现。任何事物最少也有两种不同的状态,所以区分成两种状态比较容易。但是要将一个硬件硬生生地区分成十种不同的状态,这个就太难、太复杂了。

5.4.6 小结

进制是学习计算机必须要掌握的内容,也是一个重点。

1)掌握进制的概念,区分八进制数、十进制数和十六进制数的表示方法。注意八进制数是以0开头,十六进制数是以0x或0X开头,以及了解其原因。

2)掌握进制只是数字的不同表现形式,它们本质上表示的是同一个数。

3)熟练掌握如何用口算快速进行八进制和十进制之间的相互转换、十六进制和十进制之间的相互转换。

4)掌握十进制和二进制之间如何用算术进行相互转换。

5)掌握八进制和二进制、十六进制和二进制之间如何用“8421”口诀进行相互转换。

6)了解为什么人类习惯使用十进制而计算机使用的却是二进制。 kj6WIGZDb6urm0IbyTaBMYnkZoGjOHH41YmLyFi6Vwa4VAJwjrc2pWhmAXxQvAte



5.5 数据类型

5.5.1 数据类型的分类

C语言是一门编程语言。编程要解决实际的问题,而解决实际问题的第一步是要将数据保存到计算机中。比如做一个图书管理系统,那么首先要将所有图书的信息保存到计算中。再比如做一个人事管理系统,那么首先要将所有人的信息全部保存到计算机中。所以编程的第一步是数据的存储,而存储数据首先要对数据进行分类,如数学中有整数、实数、有理数、无理数、字符。每一种分类里面还可以划分得更详细。C语言里面也是一样的,也需要对这些数据进行分类,但C语言里面没有有理数和无理数。

C语言中“常用数据”的分类如图5-1所示。其他不常用或不常见的就不列出来了,比如“共用体”和“枚举”。“共用体”早就被淘汰,所以我们不讲。“枚举”用得也不多,几乎用不到。

图5-1 数据类型的分类

本节主要介绍基本数据类型。那么这些类型的数据是怎么保存到计算机中的呢?就是通过变量。变量是存储数据的容器。存储什么类型的数据就需要定义什么类型的变量。关于变量后面会详细地讲,这里大家先暂时这样简单理解一下。

数学中我们称小数叫实数,而在C语言中换了一个名字,叫浮点数,但意思与实数是一样的。

C语言中的整数和数学中的整数是一样的。但C语言中根据整数的大小,存储整数的变量的类型又分为基本整型、短整型和长整型。基本整型简称整型,用int表示;短整型用short int表示;长整型用long int表示。长短表示变量所能存储的整数的大小。比如short int能存储2字节的数据、int能存储4字节的数据、long int能存储8字节的数据。那为什么要这么麻烦分成short int、int和long int呢?全部放在long int里面不就行了吗?这个涉及内存使用的问题。内存和硬盘相比容量是很小的。所以在编程的时候一定要考虑“内存节约”的问题。比如存储数字10只需要用2字节的short int就够了,而如果用8字节的long int就会很浪费。所以C语言就划分了多种长度的数据类型,使用原则就是节约内存。即能用short int的就不要用int,能用int的就不要用long int。

此外只有int型才有long和short区分,所以对于long int和short int,可以省略int而直接写成long和short,系统自动识别为long int和short int。下面写一个程序:


# include <stdio.h>
int main(void)
{
    short i = 10;
    printf("%d\n", i);
    return 0;
}

此外,int、short、long按正负又可分为有符号型(signed)和无符号型(unsigned)。有符号型表示定义的变量既可以存放正整数,也可以存放负整数。无符号型表示定义的变量只能存放正整数,不能存放负整数。通常定义的整型变量默认是有符号型的,所以signed可以省略。但是如果要将变量定义为无符号型的,那么就必须要加上unsigned。如unsigned int、unsigned short、unsigned long。但是同样,只有int型才有unsigned,所以unsigned int也可以省略int而直接写成unsigned。

5.5.2 基本数据类型及其所占的字节数

下面有一个问题:我们怎么知道图5-1中所标的基本数据类型的字节数是不是正确的呢?因为不同的操作系统为同一数据类型分配的字节数是不同的,这主要取决于操作系统是多少位的。有人说int是2字节的,有人说int是4字节的,那么int到底是多少字节呢?在C语言中有一个关键字叫sizeof,它的用法是:


sizeof (对象)

它的功能是“求出对象在计算机内存中所占的字节数”。对象可以是定义好的变量名、数组名或直接是数据类型。如果是变量名或数组名,那么可以不加括号,但与sizeof之间必须要用空格隔开;如果是数据类型,那么必须用括号括起来。

这里需要注意的是,sizeof是C语言里面的关键字,不是函数,所以也没有头文件。下面编一个程序看看系统给每个数据类型分别分配了多少字节的存储空间,笔者的计算机是32位的。


/*
    时间:20152814:36:07
    功能:系统给每个数据类型分别分配了多少字节的空间
*/
# include <stdio.h>
int main(void)
{
    int a;
    short int b;
    long int c;
    float d;
    double e;
    char f;
    printf("      int = %d\n", sizeof(a));
    printf("short int = %d\n", sizeof(b));
    printf(" long int = %d\n", sizeof(c));
    printf("    float = %d\n", sizeof(d));
    printf("   double = %d\n", sizeof(e));
    printf("     char = %d\n", sizeof(f));
    return 0;
}
/*VC++ 6.0中的输出结果是:
--------------------------------------
      int = 4
short int = 2
 long int = 4
    float = 4
   double = 8
     char = 1
--------------------------------------
*/

前面介绍long int是8字节的,而笔者的计算机却只给它分配了4字节,所以大家在编程之前一定要看一下每个数据类型分别分配了多少字节的存储空间。

上面这个程序中对象用的是“变量名”,也可以直接用“数据类型”,结果是一样的: kj6WIGZDb6urm0IbyTaBMYnkZoGjOHH41YmLyFi6Vwa4VAJwjrc2pWhmAXxQvAte


/*
    时间:20153314:31:37
    功能:系统给每个数据类型分别分配了多少字节的空间
*/
# include <stdio.h>
int main(void)
{    
    printf("      int = %d\n", sizeof(int));
    printf("short int = %d\n", sizeof(short int));
    printf(" long int = %d\n", sizeof(long int));
    printf("    float = %d\n", sizeof(float));
    printf("   double = %d\n", sizeof(double));
    printf("     char = %d\n", sizeof(char));
    return 0;
}
/*VC++ 6.0中的输出结果是:
--------------------------------------
      int = 4
short int = 2
 long int = 4
    float = 4
   double = 8
     char = 1
--------------------------------------
*/

点击中间区域
呼出菜单
上一章
目录
下一章
×