c语⾔四字节数补码,C语⾔-数据类型(原码、反码、补码)1、原码、反码和补码的表⽰⽅法
(1) 原码:在数值前直接加⼀符号位的表⽰法。
例如: 符号位 数值位
[+7]原= 0 0000111 B
[-7]原= 1 0000111 B
注意:a. 数0的原码有两种形式:
[+0]原=00000000B [-0]原=10000000B
b. 8位⼆进制原码的表⽰范围:-128~+127
(2)反码:
正数:正数的反码与原码相同。
负数:负数的反码,符号位为“1”,数值部分按位取反。
例如: 符号位 数值位
[+7]反= 0 0000111 B
[-7]反= 1 1111000 B
注意:a. 数0的反码也有两种形式,即
[+0]反=00000000B
[- 0]反=11111111B
b. 8位⼆进制反码的表⽰范围:-128~+127
(3)补码的表⽰⽅法
12)补码的表⽰:
正数:正数的补码和原码相同。
负数:负数的补码则是符号位为“1”,数值部分按位取反后再在末位(最低位)加1。也就是“反码+1”。
例如: 符号位 数值位
[+7]补= 0 0000111 B
[-7]补= 1 1111001 B
补码在微型机中是⼀种重要的编码形式,请注意:
a. 采⽤补码后,可以⽅便地将减法运算转化成加法运算,运算过程得到简化。正数的补码即是它所表⽰的数的真值,⽽负数的补码的数值部份却不是它所表⽰的数的真值。采⽤补码进⾏运算,所得结果仍为补码。
b. 与原码、反码不同,数值0的补码只有⼀个,即 [0]补=00000000B。
c. 若字长为8位,则补码所表⽰的范围为-128~+127;进⾏补码运算时,应注意所得结果不应超过补码所能表⽰数的范围。
2.原码、反码和补码之间的转换
由于正数的原码、补码、反码表⽰⽅法均相同,不需转换。
在此,仅以负数情况分析。
(1) 已知原码,求补码。
例:已知某数X的原码为10110100B,试求X的补码和反码。
解:由[X]原=10110100B知,X为负数。求其反码时,符号位不变,数值部分按位求反;求其补码时,再在其反码的末位加1。
1 0 1 1 0 1 0 0 原码
1 1 0 0 1 0 1 1 反码,符号位不变,数值位取反
1 +1
1 1 0 0 1 1 0 0 补码
故:[X]补=11001100B,[X]反=11001011B。
(2) 已知补码,求原码。
分析:按照求负数补码的逆过程,数值部分应是最低位减1,然后取反。但是对⼆进制数来说,先减1后取反和先取反后加1得到的结果是⼀样的,故仍可采⽤取反加1 有⽅法。
例:已知某数X的补码11101110B,试求其原码。
解:由[X]补=11101110B知,X为负数。求其原码表⽰时,符号位不变,数值部分按位求反,再在末位加1。
1 1 1 0 1 1 1 0 补码
1 0 0 1 0 0 0 1 符号位不变,数值位取反
1 +1
1 0 0 1 0 0 1 0 原码
1.3.2 有符号数运算时的溢出问题
请⼤家来做两个题⽬:
两正数相加怎么变成了负数
1)(+72)+(+98)=?
0 1 0 0 1 0 0 0 B +72
+ 0 1 1 0 0 0 1 0 B +98
1 0 1 0 1 0 1 0 B -42
两负数相加怎么会得出正数
2)(-83)+(-80)=?
1 0 1 0 1 1 0 1 B -83
+ 1 0 1 1 0 0 0 0 B -80
0 1 0 1 1 1 0 1 B +93
思考:这两个题⽬,按照正常的法则来运算,但结果显然不正确,这是怎么回事呢?
答案:这是因为发⽣了溢出。
如果计算机的字长为n位,n位⼆进制数的最⾼位为符号位,其余n-1位为数值位,采⽤补码表⽰法时,可表⽰的数X的范围是 -2n-
1≤X≤2n-1-1
当n=8时,可表⽰的有符号数的范围为-128~+127。两个有符号数进⾏加法运算时,如果运算结果超出可表⽰的有符号数的范围时,就会发⽣溢出,使计算结果出错。很显然,溢出只能出现在两个同符号数相加或两个异符号数相减的情况下。
对于加法运算,如果次⾼位(数值部分最⾼位)形成进位加⼊最⾼位,⽽最⾼位(符号位)相加(包括次⾼位的进位)却没有进位输出时,或者反过来,次⾼位没有进位加⼊最⾼位,但最⾼位却有进位输出时,都将发⽣溢出。因为这两种情况是:两个正数相加,结果超出了范围,形式上变成了负数;两负数相加,结果超出了范围,形式上变成了正数。
⽽对于减法运算,当次⾼位不需从最⾼位借位,但最⾼位却需借位(正数减负数,差超出范围),或者反过来,次⾼位需从最⾼位借位,但最⾼位不需借位(负数减正数,差超出范围),也会出现溢出。
-----------------------------------------------------
-----------------------------------------------------
#include
int main(void)
{
unsigned int un = 3000000000; /* 我使⽤的编译器 int 是 32 位的 */
short end = 200; /* ⽽ short 是 16 位的 */ long big = 65537;
printf("un = %u and not %d\n", un, un);
printf("end = %hd and %d\n", end, end);
printf("big = %ld and not %hd\n", big, big);
printf("Press ENTER ");
getchar();
return 0;
}
程序输出结果如下:
un = 3000000000 and not -1294967296
end = 200 and 200
big = 65537 and not 1
Press ENTER
这个程序表明,错误使⽤格式限定符会导致意想不到的输出。⾸先,错误使⽤ %d 来做⽆符号整型变量 un 的格式限定符,导致输出的是负数。这是因为我的计算机使⽤相同的⼆进制形式来表⽰ 3000000000 和 -129496296 ,⽽计算机只认识⼆进制。所以,如果我们使⽤%u 告诉 printf 输出⽆符号整数,输出的就是 3000000000;如果我们误⽤了 %d,那么输出的就是⼀个负数。不过,如果我们把代码中的 3000000000 改成 96 的话,输出就不会出现异常。因为 96 没有超出 int 的表⽰范围。
然后,对于第⼆个 printf,⽆论我们使⽤ %hd 还是 %d,输出的结果都是⼀样的。这是因为 C 语⾔标准规定,当 short 类型值传递给函数时,要⾃动转化成 int 类型值。之所以转化成 int,是因为 int 被设计为
计算机处理效率最⾼的整数类型。所以,对于 short 和 int ⼤⼩不同的计算机来说,把变量 end 转化成 int 类型再传递给函数,速度更快。如此说来,h 好像没有存在意义。其实不然。我们可以⽤ %hd 来看看较⼤的整数类型被截断成 short 类型的时候会是什么样的。
⽽第三个 printf,由于误⽤ %hd,导致输出是 1。这是因为,如果 long 是 32 位的话,65537 的⼆进制形式便是0000 0000 0000 0001 0000 0000 0000 0001,⽽ %hd 命令 printf 输出 short 类型的值,从⽽导致 printf 只处理 16 位数据(假设 short 是 16 位的),最终导致输出 1。
整数溢出
⾸先请看以下程序:
#include
{
/* 32 位 int 表⽰范围的上限和下限 */ int i = 2147483647, j = -2147483648;
unsigned int k = 4294967295, l = 0;
printf("%d %d %d %d\n", i, i+1, j, j-1);
printf("%u %u %u %u %u\n", k, k+1, k+2, l, l-1);
printf("Press ENTER ");
getchar();
return 0;
}
两个负数的补码相加程序输出结果如下:
2147483647 -2147483648 -2147483648 2147483647
4294967295 0 1 0 4294967295
Press ENTER
本例中,i+1 是负数,j-1 是正数,k+1 是 0,l-1 是 4294967295 。这是因为加减运算过后,它们的值超出了它们对应的那种整数类型的表⽰范围,我们把这种现象称为溢出。
unsigned int 型变量的值如果超过了上限,就会返回 0,然后从 0 开始增⼤。如果低于下限,那么就会到达 unsigned 型的上限,然后从上限开始减⼩。就好像⼀个⼈绕着跑道跑步⼀样,绕了⼀圈,⼜返回出发点。⼀般,int 型变量溢出的话,会变成负数,或者正数。
C语⾔为编程者提供了三种不同长度的整数:short int、int和long int,但不管是哪种类型表⽰的整数总有⼀定的范围,越出该范围时称为整数的溢出。例如现有算法要求如下:求满⾜条件1+2+3+…+n≤32767的最⼤整数n,请考察如下程序段:
int n=1,sum=0;
while(sum<=32767) {sum+=n; n++;}
printf(“n=%d\n”,n-1);
乍看该程序时⽆错误,但事实上,上列程序中的while循环是⼀个⽆限循环,原因在于int型数的表⽰范围为-32768到+32767,当累加和sum超过32767时,便向⾼位进位,⽽对int型数⽽⾔,最⾼位表⽰符号,故sum超过32767后便得到⼀个负数,while条件当然满⾜,从⽽形成⽆限循环。此时,最好的解决办法是将sum定义为long int型。
另外google的⼀道笔试题中也需要意识到溢出的存在
short cal(short x)
{
if(x==0)
return 0;
else
return x+cal(x-1);
}
答案
x==0时,0
x<0时,x+(x-1)+…+(-32768)【溢出】+32767+……+1,中途栈溢出
假如是short类型的负数来说,-32768减去1之后,编程32767,就是说对于有符号整数来说,最⼩的负数-1=最⼤的整数,最⼤的整数+1=最⼩的负数。
假如栈不溢出,那么将递归32768-x+32767次,最后的值按照上⾯是可以计算出来的
但是栈的空间有限,当栈溢出的时候,错误,强制退出。
在gcc下,测试,假如上述数据类型是char,最后是能计算出值的⼤⼩,栈的⼤⼩还够⽤
上⾯提到的是有符号数的溢出,下⾯是⽆符号数的溢出
在c语⾔的程序开发调试中,经常碰到⾮法操作导致程序强⾏终⽌。这种情况的发⽣多是因为程序指针的指向错误,数据溢出就是其中的⼀种,下⾯我将介绍⼀下常见的⼏种溢出情况。
1、⽆符号整数上溢
⽰例代码:
bool funcB(char *s1,unsigned short int len1,char *s2,unsigned short int len2)
{
if (1 + len1 + len2 > 64)
return false;
char *buf = (char*)malloc(len1+len2+1);
if (buf) {
memcpy(buf,s1;len1);
memcpy(buf+len1,s2,len2);
}
if (buf) free(buf);
return true;
}
这段代码存在整数上溢问题,当len1等于64,len2是0XFFFF,这段代码就会发⽣溢出。因为在定义为unsigned short char 类型下1+0xFFFF=0,这样就绕过了1 + len1 + len2 > 64的条件判断。直接导致后⾯的代码中错误地分配64字节内存,在内存拷贝时将产⽣溢出错误。
2、⽆符号整数下溢
⽰例代码:
bool funcA(unsigned int cbSize)
{
if (cbSize < 1024)
{
char *buf = new char[cbSize-1];
memset(buf,0,cbSize-1);
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论