ANSI C


 

原码、反码和补码的解释

一、官方定义

原码:原码是一种计算机中对数字的二进制表示方法,数码序列中最高位为符号位,符号位为0表示正数,符号位为1表示负数;其余有效值部分用二进制的绝对值表示。 

反码:如果机器数是正数,则该机器数的反码与原码一样;如果机器数是负数,则该机器数的反码是对它的原码(符号位除外)各位取反而得到的。 

补码:如果机器数是正数,则该机器数的补码与原码一样;如果机器数是负数,则该机器数的补码是对它的原码(除符号位外)各位取反,并在末位加1。

 

二、解释说明

为了方便说明问题,假定用4位二进制数表示一个整数(一般32位电脑上主流C语言编译器用32位表示一个整数,不过原理都是一致的)。 

2.1 有符号数的引入和原码表示的局限性 

图1说明了无符号数的原码表示方法,其中内圈的数字为二进制数,外圈的数字是内圈二进制数所对应的十进制数。

 

 

 图1 无符号数的原码表示方法 

顾名思义,无符号数不能表示负数。为了解决这个问题,我们把最高位定义为符号位。图2说明了有符号数的原码表示方法,最高位为1表示负数,其中内圈的数字为二进制数,外圈的数字是内圈二进制数所对应的十进制数。

 

 图2 有符号数的原码表示方法 

用原码来表示一个有符号数会带来两个问题。 

  • 第一个问题就是正负相加不等于零。如图2所示,十进制运算 2+(-2) 对应的二进制运算为 0010+1010=1100 ,按照原码表示等于 -4 。
  • 第二个问题就是有两个零存在,分别为0000 和1000。 

可见,原码不适合用来表示有符号数! 

2.2 反码和补码的引入 

为了解决用原码表示有符号数时出现的第一个问题,我们引入了反码的概念。图3说明了有符号数的反码表示方法。如果把二进制数想象成12点,把二进制数想象成6点,原码就是从12点开始顺时针排列到,而反码就是从6点开始逆时针排列到。 

这样做的好处就在于现在正负数相加等于零了。例如,十进制运算 2+(-2) 对应的二进制运算为 0010+1101=1111,按照反码表示等于 -0。 

  图3 有符号数的反码表示方法

在用反码表示有符号数的图3中,依然有两个零存在,分别为 0000 和 1111。为了解决用原码和反码表示有符号数时出现的第二个问题,我们引入了补码的概念。图4说明了有符号数的补码表示方法。按照补码的定义,-0 的反码为 1111,不过现在必须在末位加1,那现在就是 0000 了,以此类推。与反码表示不一样的是,补码在负数上从 -1 表示到 -8,-0 不再存在了。经过验证,正负相加也等于零!

 图4 有符号数的补码表示方法 

而对于无符号数,原码、反码、补码都是一致的。因此,我们可以说:整型数在计算机中,使用补码表示。 

三、补充说明

溢出。对于无符号数,溢出发生的地方在6点钟方向;而对于有符号数,溢出的边界在12点方向,
无符号数。在实际的应用中,有很多种情况是不会出现负数的。比如说我们的年龄,一个班级的课程数,一个国家的人数等。如果用有符号数来保存这些值,那么永远不会用到表示负数的那一半范围,这样就被白白浪费了,而且还使得正数的表示范围被占用了一半。针对这个问题,C语言中引入了无符号数的概念。
char。当要在不同平台移植我们的程序时,字符是否有符号的这种歧义性会给我们带来很大的麻烦。如果移植性的要求很高,那么你就需要确保你的字符变量中保存的值的范围在0到127之间,这样无论字符类型是否有符号,都可以正确地表示这个范围之内的值。
浮点数。浮点数通常用一个分数和以某个基数的指数来组成,以 m×b的方式保存在电脑中,一般电脑中基数 和分数 都是用二进制表示的。
数制和码制的区别。由于教材对码制的解释比较严谨全面,因而比较难懂。我这里不太严谨但是通俗易懂解释一下:数制规定了每一位的权重和系数,码制规定了有几位以及这几位不同排列代表的含义。

  

二维数组作为函数参数来传递的三种方法

参考链接:深入理解数组指针与指针数组的区别

 

方法一:形参给出第二维的长度

#include <stdio.h>
void func(int n, char str[][5])
{
  int i;   for(i = 0; i < n; i++)   printf("/nstr[%d]=%s/n",i,str[i]); } void main() {   char* p[3];   char str[][5]={"abc","def","ghi"};   func(3,str); }

方法二:形参声明为指向数组的指针

#include <stdio.h>
void func(int n,char(*str)[5])
{
  int i;
  for(i=0;i<n;i++)
  printf("/nstr[%d]=%s/n",i,str[i]);
}
void main()
{
  char* p[3];
  char str[][5] = {"abc","def","ghi"};
  func(3,str);
}

数组指针的理解(重要!)

int *p; 可以理解为步长为1的指针,int (*p)[4]; 可以理解为步长为4的指针。本质和二维数组名一样都是一维的。

方法三:形参声明为指针的指针

#include <stdio.h>
void func(int n, char **str)
{
  int i;
  for(i=0;i<n;i++)
  printf("/nstr[%d]=%s/n",i,str[i]);
}
void main()
{
  char* p[3];
  char str[][5] = {"abc","def","ghi"};
  p[0] = &str[0][0];
  p[1] = str[1];
  p[2] = str[2];
  func(3,p);
}

方法三说明:在函数中使用传参过来的二维数组(指针)进行数组取值的时候不能使用(array[i][j] )这种形式来取值,应该将二维数组看成一个一维数组,使用array[i * j + j]这种形式来进行取值。这是因为在传参的时候,将array[][]数组当成二级指针来进行传递,所以数组的属性退化成了二级指针的属性,不能使用array[i][j]这种方式来进行数组取值。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM