C语言的本质(3)——整数的本质与运算

计算机存储的最小单位是字节(Byte),一个字节通常是8个bit。C语言规定char型占一个字节的存储空间。如果这8个bit按无符号整数来解释,则取值范围是0~255,如果按有符号整数来解释,则取值范围是-128~127。C语言规定了signed和unsigned两个关键字,unsigned char型表示无符号数,signed char型表示有符号数。

对于char类型,编译器可以定义char型是无符号的,也可以定义char型是有符号的,在该编译器所对应的体系结构上哪种实现效率高就可以采用哪种实现,x86平台的gcc定义char是有符号的。

这是C标准的规则之一:优先考虑效率,而可移植性尚在其次。这就要求程序员非常清楚这些规则,如果你要写可移植的代码,就必须清楚哪些写法是不可移植的,应该避免使用。另一方面,写不可移植的代码有时候也是必要的,比如Linux内核代码使用了很多gcc特性以得到最佳的执行效率,在写的时候就没打算用别的编译器编译,也就没考虑可移植性的问题。如果要写不可移植的代码,你也必须清楚代码中的哪些部分是不可移植的,以及为什么要这样写,如果不是为了效率,一般来说就没有理由故意写不可移植的代码。

C语言与平台和编译器是密不可分的,离开了具体的平台和编译器讨论C语言,就只能讨论到本书第一部分的程度了。注意,ASCII码的取值范围是0~127,所以不管char型是有符号的还是无符号的,存一个ASCII码都没有问题,一般来说,如果用char型存ASCII码字符,就不必明确写signed还是unsigned,如果把char型当作8位的整数来用,为了可移植性就必须写明是signed还是unsigned。

ANSI C99标准中定义了两类(四个)类型修饰符:long/short和unsigned/signed。

  C99标准规定,long类型不能比变通类型短,short类型不能比普通类型长。而unsigned与signed的区别在实现上是有无符号的区别,而是使用上是取值范围的区别,两者表示范围相同,但前者全是正数,后者关于0对称。

 说明:

long/short可以修饰int,long还可以修饰double。

unsigned/signed可以修饰int, char,不可以修饰浮点型。

int长度是机器的字长,short int是半个字长,long int是一个或两个字长。

unsigned/signed长度与普通类型一样,只是表示区间不同。

各种整数数据类型的表示和取值范围:

整型

[signed]int

-2147483648~+2147483648

无符号整型

unsigned[int]

0~4294967295

短整型

short [int]

-32768~32768

无符号短整型

unsigned short[int]

0~65535

长整型

Long int

-2147483648~+2147483648

无符号长整型

unsigned [int]

0~4294967295

字符型

[signed] char

-128~+127

无符号字符型

unsigned char

0~255

我们已经了解了计算机中正整数如何表示,加法如何计算,那么负数如何表示呢?减法又如何计算呢?为了书写方便,下面所有的例子都用8个bit表示一个数,实际计算机算术运算的操作数可以是8位、16位、32位甚至64位的。

要用8个bit表示正数和负数,一种简单的思路是把最高位当作符号位(Sign Bit),0表示正1表示负,剩下的七位表示绝对值的大小。例如-1表示成10000001,+1表示成00000001。

计算机要对这样的两个数做加法运算需要处理以下逻辑:

1、如果两数符号位相同,就把它们的低7位相加,符号位不变。如果低7位相加时在最高位产生进位,则结果超出7位所能表示的数值范围,这称为溢出(Overflow),通常把计算机中的一个标志位置1表示产生溢出。

2、如果两数符号位不同,首先比较它们的低7位谁大,然后用大数减小数,结果的符号位和大数相同。

减法运算需要处理以下逻辑:

1、如果两数符号位相同,并且低7位是大数减小数,则符号位不变,如果低7位是小数减大数,则按大数减小数计算,结果要变号。

2、如果两数符号位不同,把低7位相加,如果是正数减负数则结果为正,如果是负数减正数则结果为负,低7位在相加时可能产生溢出。

这其实和手算加减法的逻辑是相同的。算加减法需要处理这么多逻辑:比较符号位,比较绝对值,加法改减法,减法改加法,小数减大数改成大数减小数……这是非常低效率的。还有一个缺点是0的表示不唯一,既可以表示成10000000也可以表示成00000000,进一步增加了逻辑的复杂性,所以我们迫切需要重新设计数的表示方法,以使计算过程更简单。

有一种方法可以把减法全部转化成加法来计算,这样就不必设计加法器和减法器两套电路了。我们以十进制减法为例来理解一下这种方法。比如:

167-52=167+(999-52)-1000+1=167+947-1000+1=1114-1000+1=114+1=115

首先把52换成999-52,也就是947,这称为取9的补码(9's Complement),虽然这也是减法但它不需要借位,只需要对每一位数字分别取补码,所以比一般的减法要简单得多。然后把167和947相加,百位上的进位舍去,得到114,然后再加1得到115[21],这就是最终结果了。一句话概括就是:减去一个数等于加上这个数取9的补码再加1(忽略最高位的进位)。

这种方法也可以类推到二进制加减法:减去一个数等于加上这个数取1的补码(1's Complement)再加1(忽略MSB的进位)。取1的补码就是1-1=0,1-0=1,其实相当于把每一位数字取反了,以后将1的补码简称为反码。比如

00001000-00000100->00001000+11111011+1->00000011+1=00000100

上式的前两步不是等价变换,所以没有用=号而是用->表示,第一步多加了一个100000000,第二步少加了一个100000000,效果相互抵消,所以最终结果正是00001000-00000100的结果。现在我们发现,如果把第一步写成00001000+(-00000100)->00001000+11111011+1,则11111011+1就可以用来表示负数-00000100。所以,补码表示法不仅可以把减法转化为加法,而且合理地规定了负数的表示方法,就是“先取反码再加1”。负数的这种表示称为2的补码(2'sComplement),以后简称为补码。为什么称为2的补码呢?因为如果对一位数取补码,则1的补码是1-1+1=10-1=1,相当于从2里面减去1。类似地,对00000100取补码是11111111-00000100+1=100000000-00000100,相当于从100000000(十进制的256)里面减去00000100。

将负数全部用补码表示之后,8个bit可以表示的正数有00000000~01111111(十进制的0~127),负数有10000000~11111111(十进制的-128~-1),合起来是十进制的-128~127,一共256个数,而8个bit最多可以表示28=256个不同的数,所以已经充分利用了这8个bit,每个数都只有一种表示,0也只有一种表示就是00000000。我们还发现,所有正数的最高位是0,所有负数的最高位是1,因此最高位仍然具有符号位的含义,要检查一个数是正是负只要看最高位就可以了,但在计算时却可以把符号位和数放在一起做加法运算,而不必像Sign and Magnitude表示法那样对符号位单独处理。

采用补码做加减运算时总是忽略MSB的进位,如果在计算过程中忽略进位的效果没有相互抵消,最后的结果肯定是错的,这种情况一定是由溢出引起的。只要我们有办法判断哪些情况会产生溢出,其它情况下都可以放心地忽略MSB的进位。判断溢出的办法是这样的:在相加过程中最高位产生的进位和次高位产生的进位如果相同则没有溢出,否则就说明产生了溢出。逻辑电路的实现可以把这两个进位连接到一个异或门,把异或门的输出连接到溢出标志位。对于8位二进制数的加减运算来说,当计算结果超出-128~127的范围时就会溢出,例如:

有符号数加法溢出

最高位产生的进位是1,次高位产生的进位是0,说明溢出了,计算结果换算成十进制是122,这显然不对,根本原因是(-126)+(-8)=-134超出了8位二进制数能表示的范围。

用8个bit既表示正数又表示负数,则能够表示的范围是-128~127,如果8个bit全部表示正数,则能够表示的范围是0~255,前者称为有符号数(Signed Number),后者称为无符号数。但是计算机在做加法时并不区分操作数是有符号数还是无符号数,计算过程都是一样的,所以上面的例子也可以看作无符号数的加法:

无符号数加法进位

把两个操作数看作无符号数分别是130和248,计算结果换算成十进制是122,最高位的一个进位相当于256,122+256这个结果是对的。计算机的加法器在做完计算之后,根据最高位产生的进位设置进位标志,同时根据最高位和次高位产生的进位的异或设置溢出标志。至于这个加法到底是有符号数加法还是无符号数加法则取决于程序怎么理解了,如果程序把它理解成有符号数加法,就去检查溢出标志,如果程序把它理解成无符号数加法,就去检查进位标志。通常计算机在做算术运算之后还可能设置另外两个标志,如果结果为零则设置零标志,如果结果的最高位是1则设置负数标志(只有当理解成有符号数运算时才去检查这个标志)。

转载于:https://www.cnblogs.com/new0801/p/6177122.html

C语言的本质(3)——整数的本质与运算相关推荐

  1. C语言把字符转换为整数的方法和示例

    文章目录 1.程序要求 2.实现思路 3.获取视频教程 4.版权声明 1.程序要求 把字符'0'转换为数字的0: 把字符'1'转换为数字的1: - 把字符'9'转换为数字的9. 2.实现思路 字符的本 ...

  2. 【C 语言】变量本质 ( 变量概念 | 变量本质 - 内存空间别名 | 变量存储位置 - 代码区 | 变量三要素 )

    文章目录 一.变量概念 二.变量本质 1.变量本质 - 内存别名 2.变量存储位置 - 代码区 3.变量三要素 一.变量概念 变量概念 : 变量 是 既能读 , 又能写 的 内存对象 ; 与 变量 相 ...

  3. 【C语言进阶深度学习记录】一 数据类型的本质与变量的本质

    今天学习C语言中的数据类型的本质与变量的本质 文章目录 1 什么是数据类型 2 变量的本质 3 数据类型与变量的关系 4 自定义数据类型与创建变量 5 总结 1 什么是数据类型 数据类型可以理解为固定 ...

  4. C语言中动态内存分配的本质是什么?

    摘要:C语言中比较重要的就是指针,它可以用来链表操作,谈到链表,很多时候为此分配内存采用动态分配而不是静态分配. 本文分享自华为云社区<[云驻共创]C语言中动态内存分配的本质>,作者: G ...

  5. c语言中浮点数和整数转换_C中的数据类型-整数,浮点数和空隙说明

    c语言中浮点数和整数转换 C中的数据类型 (Data Types in C) There are several different ways to store data in C, and they ...

  6. c 语言比较三个整数大小,C 语言 —— 比较三个整数的大小并从大到小输出

    思路一:定义两个比较大小的函数,依次两两比较输出最大或最小的数字(较为繁琐,最易分析) /* C 语言比较三个整数的大小并从大到小输出 思路:定义两个比较大小的函数,依次两两比较输出最大或最小的数字 ...

  7. (c语言)输入两个整数a和n,计算下面表达式的值。Sn=a+aa+aaa+aaaa+.....+aa..aa(n个a)

    (c语言)输入两个整数a和n,计算下面表达式的值.Sn=a+aa+aaa+aaaa+-+aa-aa(n个a) 方法一: #include<stdio.h> #include<stdl ...

  8. python求1到n的奇数和_编写程序。输入任意整数n,计算1到n的奇数和。C语言编写程序 输入整数N 显...

    编写程序.输入任意整数n,计算1到n的奇数和. C语言编写程序 输入整数N 显 www.zhiqu.org 时间: 2020-11-23 解题思路:循环判断1到N的每一个数: 若除以2是结果为整数,也 ...

  9. C语言 正序分解整数

    C语言 正序分解整数 输入一个非负整数 , 正序输出它的每一位数字 输入 13526 输出 1 3 5 2 6 分析思路: 需要正序输出每一位数字 ,我们可以先把数逆序一遍,再逆序输出每一位数字. 这 ...

最新文章

  1. 新乡职业学院对口计算机分数线,新乡职业技术学院2020年录取分数线(附2017-2020年分数线)...
  2. 【Paper】2013_Autonomous Cooperation Between UAV and UGV to Improve Navigation and Environmental
  3. @select注解模糊查询_mybatis @SelectProvider 注解, 打赌你没有用过
  4. k8s提交镜像到harbor仓库
  5. firefox自动化测试的常用插件
  6. [Kaggle] Digit Recognizer 手写数字识别(卷积神经网络)
  7. C++学习之路 | PTA乙级—— 1025 反转链表 (20分)(精简)
  8. 关于ubuntu 是否需要使用std::到问题。
  9. bootstrap弹出框居中
  10. 数字化转型、智能制造、工业软件及其应用案例资源列表
  11. 如何添加CDN加速域名
  12. [嵌入式开发模块]深度传感器解算板(MS5837转串口板) 驱动模块
  13. ae合成设置快捷键_【教程】你不知道的全网最全ae快捷键【基础篇】
  14. MySQL garbled code
  15. Linux性能优化(七)——网络流量监控工具
  16. 计算机博弈围棋,计算机博弈:“不围棋”入门教程
  17. 冒险岛linux_062服务端,支持arm64_armhf_amd_i386
  18. 找手机ic库存回收公司
  19. 超级牛的网站同步工具软件—端端Clouduolc
  20. 手写识别ocr java,OCR 指的是手写文字技术_学小易找答案

热门文章

  1. Linux vim替换操作
  2. Dubbo Admin服务测试功能
  3. spring boot整合mybatis步骤
  4. eclipse关闭java程序_如何为Java代码的某些部分关闭Eclipse代码格式化程序?
  5. html解析的简单方式,HTML解析看我就够了,不依赖任何第三方,两个方法搞定
  6. 多序列比对的c语言程序,多序列比对的实际应用
  7. Spring Session源码
  8. Coding:取若干个1到n的整数可求和等于整数m,求出所有组合的个数
  9. fprintf与fwrite的区别
  10. 强化学习6——Value-based RL和Policy-based RL 的区别