由一道笔试题想到的:原码反码和补码

内核编译和嵌入式产品的设计与开发
xiaobo68688
帖子: 10
注册时间: 2010-02-22 12:05

由一道笔试题想到的:原码反码和补码

#1

帖子 xiaobo68688 » 2010-11-22 14:12

由一道笔试题想到的:原码反码和补码
这是我从一位CSDN网友的博客上边偶然发现的一道华为笔试题,博主给出了正确答案,但是我对博主的分析有点不太理解。

void main()
{
char *p;
*p=-130;
printf("%d",*p);
}

我们抛开程序的正确性不谈(没有给指针分配空间怎么能对指针进行解引用呢?),仅仅分析一下程序的输出结果。
如果您对原码反码补码的知识非常熟悉或者非常不熟悉,请都跳过下边这段文字直接看对程序的分析,因为我并不想把这篇文章写成对原码反码补码的教学文章。仅仅是想借程序巩固一下这方面的知识。
首先,我们需要分析一下-130在内存中的二进制存储方式。这就引出了有符号整数在内存中的存储方式:有符号整数在内存中是以补码形式存储的。
首先我们给出原码,反码和补码的定义:原码即有符号整数所对应的二进制数,正数的反码和本身一样,负数的反码是对应的原码除符号位以外,其它的数位依次取反,正数的补码和本身一样,负数的补码是对应的反码在末位加1。
计算机中存储方式为补码的原因是为了使减法可以当做加法来算。
其实,如果不采用补码的形式存储,内存中会出现 +0 和 -0,这一现象可以在下边的例子中看到。
举个简单的例子:
如果计算机以8位的方式存储有符号整数,则
原码 反码 补码
1 0000 0001 0000 0001 0000 0001
-1 1000 0001 1111 1110 1111 1111
0 0000 0000 0000 0000 0000 0000

如果是反码存储的话,-1 + 1 = 1111 1111, 1111 1111是 1000 0000 的反码,也就是 -0。

我们接着来分析这个程序:
-130在内存中以补码形式存储,在我的系统中,int类型占4个字节(到今天我也不知道到底是什么东西影响了int的大小,书上说和操作系统的位宽有关,但是我在32位和64位下都试了,结果都是4个字节,或许和编译器有关?如果哪位朋友知道请告知我!谢谢了!)。那么,-130的原码为: 1000 0000 0000 0000 0000 0000 1000 0010,反码为:1111 1111 1111 1111 1111 1111 0111 1101,补码为:1111 1111 1111 1111 1111 1111 0111 1110。
由于*p的数据类型是char,将-130赋值给*p会将数据从低位截断,结果是:0111 1110,那么,输出*p的时候,系统会将数据自动补上0,也就是说,会输出0000 0000 0000 0000 0000 0000 0111 1110。即126。
头像
linjiework
帖子: 240
注册时间: 2009-07-07 19:52

Re: 由一道笔试题想到的:原码反码和补码

#2

帖子 linjiework » 2010-11-22 14:22

学习了~~~


int 的字节数确实和编译器有关,和运行平台关系不大。int 的字节数在编译的时候就已经确定了。
阿呆 : 天下第一呆!
xiaobo68688
帖子: 10
注册时间: 2010-02-22 12:05

Re: 由一道笔试题想到的:原码反码和补码

#3

帖子 xiaobo68688 » 2010-11-22 15:25

linjiework 写了:学习了~~~


int 的字节数确实和编译器有关,和运行平台关系不大。int 的字节数在编译的时候就已经确定了。
谢谢您!那么,编译器是根据什么来确定的int的字节数呢?自己喜欢怎么样就怎么样吗?
头像
linjiework
帖子: 240
注册时间: 2009-07-07 19:52

Re: 由一道笔试题想到的:原码反码和补码

#4

帖子 linjiework » 2010-11-22 21:13

xiaobo68688 写了:
linjiework 写了:学习了~~~


int 的字节数确实和编译器有关,和运行平台关系不大。int 的字节数在编译的时候就已经确定了。
谢谢您!那么,编译器是根据什么来确定的int的字节数呢?自己喜欢怎么样就怎么样吗?

编译器分普通版本(也就是32位)和64位版本。因为编译器和硬件关系(主要是 CPU)比较密切,所以不同平台下的编译器是不同的。因此在内核里,为了保证在不同平台下,变量的字节数一致,自己定义了一整套数据类型,比如 u32, u8 。
阿呆 : 天下第一呆!
头像
友情提醒
帖子: 6
注册时间: 2010-10-07 20:50

Re: 由一道笔试题想到的:原码反码和补码

#5

帖子 友情提醒 » 2010-11-24 20:38

xiaobo68688 写了:
linjiework 写了:学习了~~~


int 的字节数确实和编译器有关,和运行平台关系不大。int 的字节数在编译的时候就已经确定了。
谢谢您!那么,编译器是根据什么来确定的int的字节数呢?自己喜欢怎么样就怎么样吗?
从80386开始 cpu地址线就变为32位。int 4字节,就是与32位对齐,这个貌似是ANSI C规定的。编译器不会对标准C基本类型做更改。
如果楼主研究过标准C的结构体就会明白。一个结构体的大小是最后一个成员类型决定的。比如

代码: 全选

struct foo1{ char a; int b};

代码: 全选

struct foo {int a; char b};
是不一样大小的。在foo1里 char a和int之间 编译器会自动补3个字节。如果不这样。cpu取址会出现异常甚至崩溃。
我想这也是int叫整形原因。。 :em06
xiaobo68688
帖子: 10
注册时间: 2010-02-22 12:05

Re: 由一道笔试题想到的:原码反码和补码

#6

帖子 xiaobo68688 » 2010-11-26 12:51

友情提醒 写了:
xiaobo68688 写了:
linjiework 写了:学习了~~~


int 的字节数确实和编译器有关,和运行平台关系不大。int 的字节数在编译的时候就已经确定了。
谢谢您!那么,编译器是根据什么来确定的int的字节数呢?自己喜欢怎么样就怎么样吗?
从80386开始 cpu地址线就变为32位。int 4字节,就是与32位对齐,这个貌似是ANSI C规定的。编译器不会对标准C基本类型做更改。
如果楼主研究过标准C的结构体就会明白。一个结构体的大小是最后一个成员类型决定的。比如

代码: 全选

struct foo1{ char a; int b};

代码: 全选

struct foo {int a; char b};
是不一样大小的。在foo1里 char a和int之间 编译器会自动补3个字节。如果不这样。cpu取址会出现异常甚至崩溃。
我想这也是int叫整形原因。。 :em06
恩,谢谢您,我明白了,但是下边您说的我不太了解,高手呀。
我还得查查资料再回来看看
头像
eexpress
帖子: 58428
注册时间: 2005-08-14 21:55
来自: 长沙

Re: 由一道笔试题想到的:原码反码和补码

#7

帖子 eexpress » 2010-11-26 13:24

原码反码和补码
就不是C该做的事情哦。C的编译器不同,缺省的字节长都不同的。
出这样题目的,就是脑袋给踢了的人。
那是asm,或者说指令集的事情。
● 鸣学
头像
BigSnake.NET
帖子: 12522
注册时间: 2006-07-02 11:16
来自: 廣州
联系:

Re: 由一道笔试题想到的:原码反码和补码

#8

帖子 BigSnake.NET » 2010-11-26 13:44

友情提醒 写了:
xiaobo68688 写了:
linjiework 写了:学习了~~~


int 的字节数确实和编译器有关,和运行平台关系不大。int 的字节数在编译的时候就已经确定了。
谢谢您!那么,编译器是根据什么来确定的int的字节数呢?自己喜欢怎么样就怎么样吗?
从80386开始 cpu地址线就变为32位。int 4字节,就是与32位对齐,这个貌似是ANSI C规定的。编译器不会对标准C基本类型做更改。
如果楼主研究过标准C的结构体就会明白。一个结构体的大小是最后一个成员类型决定的。比如

代码: 全选

struct foo1{ char a; int b};

代码: 全选

struct foo {int a; char b};
是不一样大小的。在foo1里 char a和int之间 编译器会自动补3个字节。如果不这样。cpu取址会出现异常甚至崩溃。
我想这也是int叫整形原因。。 :em06
一样大小的
^_^ ~~~
要理解递归,首先要理解递归。

地球人都知道,理论上,理论跟实际是没有差别的,但实际上,理论跟实际的差别是相当大滴。
头像
BigSnake.NET
帖子: 12522
注册时间: 2006-07-02 11:16
来自: 廣州
联系:

Re: 由一道笔试题想到的:原码反码和补码

#9

帖子 BigSnake.NET » 2010-11-26 13:57

而且楼主的解释各种问题 。。
1. p 木有初始化,所以第二行就已经是未定义行为了 。。
2. 即使刚好木有段错误,如果 p 没有对齐,在某些机器上也会崩掉。
3. 溢出是在赋值那里发生的, -130 超出了 char 的范围
^_^ ~~~
要理解递归,首先要理解递归。

地球人都知道,理论上,理论跟实际是没有差别的,但实际上,理论跟实际的差别是相当大滴。
poet
帖子: 2841
注册时间: 2006-09-11 22:47

Re: 由一道笔试题想到的:原码反码和补码

#10

帖子 poet » 2010-11-26 15:11

xiaobo68688 写了:
linjiework 写了:学习了~~~
int 的字节数确实和编译器有关,和运行平台关系不大。int 的字节数在编译的时候就已经确定了。
谢谢您!那么,编译器是根据什么来确定的int的字节数呢?自己喜欢怎么样就怎么样吗?

编译器在不同的平台上是根据对应 CPU 技术参数的推荐来决定的。因为 int 应当是“这个CPU最方便处理的,处理效率最高的整数类型”。这个类型不一定跟CPU位宽相同。

x86_64 位 CPU,在诞生的时候,其指令集就推荐使用 32 位整数,因此 int 是 32 位。——所以说实际上 int 字长与 CPU 位宽没有直接关系。(x86_64 指令集诞生于 K8 时代,各位可以查询 K8 相关文档,其中明确描述了整数 int 应当是 32 位。)

但是,我知道的,有个 HP 的小型机专用64 位CPU,其指令集推荐使用 64 位整数,因此那个平台上的编译器就选择了 64 位。
头像
友情提醒
帖子: 6
注册时间: 2010-10-07 20:50

Re: 由一道笔试题想到的:原码反码和补码

#11

帖子 友情提醒 » 2010-11-26 23:45

BigSnake.NET 写了:
友情提醒 写了:
xiaobo68688 写了:
linjiework 写了:学习了~~~


int 的字节数确实和编译器有关,和运行平台关系不大。int 的字节数在编译的时候就已经确定了。
谢谢您!那么,编译器是根据什么来确定的int的字节数呢?自己喜欢怎么样就怎么样吗?
从80386开始 cpu地址线就变为32位。int 4字节,就是与32位对齐,这个貌似是ANSI C规定的。编译器不会对标准C基本类型做更改。
如果楼主研究过标准C的结构体就会明白。一个结构体的大小是最后一个成员类型决定的。比如

代码: 全选

struct foo1{ char a; int b};

代码: 全选

struct foo {int a; char b};
是不一样大小的。在foo1里 char a和int之间 编译器会自动补3个字节。如果不这样。cpu取址会出现异常甚至崩溃。
我想这也是int叫整形原因。。 :em06
一样大小的
:em06 :em06 sorry...很久没写生疏了。这个例子举的太烂了。。

代码: 全选

struct foo1 {char a; int b; char c};

代码: 全选

struct foo2 {int a; char b; char c};
这样就会不一样。。o(∩∩)o...哈哈 我的失误。对不起楼主。
头像
会fly的青蛙
帖子: 66
注册时间: 2010-10-13 13:15

Re: 由一道笔试题想到的:原码反码和补码

#12

帖子 会fly的青蛙 » 2010-12-07 19:43

咦,书上说结构体的大小是其所有元素总和呀,楼上的这两个不还是一样的吗?
没得玩了
frankvista
帖子: 177
注册时间: 2010-06-16 20:55

Re: 由一道笔试题想到的:原码反码和补码

#13

帖子 frankvista » 2010-12-08 15:57

eexpress 写了:原码反码和补码
就不是C该做的事情哦。C的编译器不同,缺省的字节长都不同的。
出这样题目的,就是脑袋给踢了的人。
那是asm,或者说指令集的事情。
不敢苟同.
比如x&-x这种代码,如果计算机用的不是补码就悲剧了.
xenogear
帖子: 1
注册时间: 2010-12-08 23:04

Re: 由一道笔试题想到的:原码反码和补码

#14

帖子 xenogear » 2010-12-08 23:08

请问楼主为什么说-130是一个int类型的?
头像
icmmed
帖子: 46
注册时间: 2010-12-03 11:08

Re: 由一道笔试题想到的:原码反码和补码

#15

帖子 icmmed » 2010-12-16 11:45

:em11 学习
我喜欢忙一点...
Lost Temple...
回复