原码 反码 补码 负数

原码:将一个整数,转换成二进制,就是其原码。如单字节的5的原码为:0000 0101-5的原码为1000 0101

 

反码:正数的反码就是其原码;负数的反码是将原码中,除符号位以外,每一位取反。如单字节的5的反码为:0000 0101-5的反码为1111 1010                                         ((((((charshortint long 称为有序类型(integral types)。有序类型可以有符号,也可以

无符号。在有符号类型中,最左边的位是符号位,余下的位代表数值。在无符号类型中,所

有的位都表示数值。如果符号位被置为1,数值被解释成负数;如果是0,则为正数。一个

8 位有符号的char 可以代表从-128 127 的数值;一个无符号的char 则表示0 255 范围

内的数值。)))))

 

 

补码:正数的补码就是其原码;负数的反码+1就是补码。如单字节的5的补码为:0000 0101-5的原码为1111 1011

 

  在计算机中,正数是直接用原码表示的,如单字节5,在计算机中就表示为:0000 0101负数用补码表示,如单字节-5,在计算机中表示为1111 1011

 

 

这儿就有一个问题,为什么在计算机中,负数用补码表示呢?为什么不直接用原码表示?如单字节-51000 0101

 

  我想从软件上考虑,原因有两个:

1、表示范围

 

  拿单字节整数来说,无符号型,其表示范围是[0,255],总共表示了256个数据。有符号型,其表示范围是[-128,127]

 

  先看无符号,0表示为0000 0000255表示为1111 1111,刚好满足了要求,可以表示256个数据。

 

  再看有符号的,若是用原码表示,0表示为0000 000。因为咱们有符号,所以应该也有个负0(虽然它还是0):1000 0000

 

  那我们看看这样还能够满足我们的要求,表示256个数据么?

 

  正数,没问题,1270111 111110000 0001,当然其它的应该也没有问题。

 

  负数呢,-11000 0001,那么把负号去掉,最大的数是111 1111,也就是127,所以负数中最小能表示的数据是-127

 

  这样似乎不太对劲,该如何去表示-128貌似直接用原码无法表示,而我们却有两个0

 

  如果我们把其中的一个0指定为-128,不行么?这也是一个想法,不过有两个问题:一是它与-127的跨度过大;二是在用硬件进行运算时不方便。

 

  所以,计算机中,负数是采用补码表示。如单字节-1,原码为1000 0001,反码为1111 1110,补码为1111 1111,计算机中的单字节-1就表示为1111 1111

 

  单字节-127,原码是1111 1111,反码1000 0000,补码是1000 0001,计算机中单字节-127表示为1000 0001

 

  单字节-128,原码貌似表示不出来,除了符号为,最大的数只能是127了,其在计算机中的表示为1000 0000

 

  2、大小的习惯(个人观点)

 

  也可以从数据大小上来理解。还是以单字节数据为例。有符号数中,正数的范围是[1,127],最大的是127,不考虑符号为,其表示为111 1111;最小的是1,不考虑符号为,其表示为000 0001

 

  负数中,最大的是-1,我们就用111 1111表示其数值部分。后面的数据依次减1。减到000 0001的时候,我们用它标示了-127。再减去1,就变成000 0000了。还好我们有符号为,所以有两个0。把其中带符号的0拿过来,表示-128,刚好可以满足表示范围。

 

  以上只是从软件的角度进行了分析,当然,从硬件的角度出发,负数使用补码表示也是有其原因的,毕竟计算机中,最终实现运算的还是硬件。主要原因有三:

 

  1、负数的补码,与其对应正数的补码之间的转换可以用同一种方法—-求补运算完成,简化硬件。

 

  如:

 

  原码                反码                补码

 

  -127 –127    1000 0001  0111 1110  0111 1111

 

  127 –-127    0111 1111  1000 0000  1000 0001

 

  -128 –128    1000 0000  0111 1111  1000 0000

 

  128 –-128    1000 0000  0111 1111    1000 0000

 

  可以发现,负数和正数求补的方法是一样的。

 

  2、可以将减法变为加法,省去了减法器。

 

  在计算机中,我们可以看到,对其求补,得到的结果是其数值对应的负数。同样,负数也是如此。

 

  运算中,减去一个数,等于加上它的相反数,这个小学就学过了。既然其补码就是其相反数,我们加上其补码不就可以了。

 

  如:A – 127

 

  也就相当于:A + (-127)

 

  又因为负数是以补码的形式保存的,也就是负数的真值是补码,既然这样,当我们要减一个数时,直接把其补码拿过来,加一下,就OK了,我们也可以放心地跟减法说拜拜了!

 

  当然这也涉及到类型转换的问题,如单字节128,其原码是1000 0000,其补码也是1000 0000。这样我们+128,或者-128,都是拿1000 0000过来相加,这样不混乱掉了?还好,各个编程语言的编辑器对有类型转换相关的限制。

 

  如:(假设常量都是单字节)

 

  1 + 128 真值的运算是 0000 0001 + 1000 0000 ,如果你将结果赋值给一个单字节有符号正数,编辑器会提示你超出了表示范围。因为运算的两个数据是无符号的,其结果也是无符号的129,而有符号单字节变量最大可以表示的是127

 

  1 – 128,真知的运算是 0000 0001 + 1000 0000 ,因为-128是有符号,其运算结果也是有符号,1000 0001,刚好是-127在计算机中的真值。

 

  3、无符号及带符号的加法运算可以用同一电路完成。

 

  有符号和无符号的加减,其实都是把它们的真值拿过来相加。真值,也就是一个数值在计算机中的二进制表示。正数的真值就是其原码,负数的真值是其补码。所以,有符号和无符号由编译器控制,计算机要做的不过是把两个真值拿过来相加。

龙泉学车三日

   人总是在吃亏和跌跤中长大和成熟的。
   有规则的地方都有潜规则,遵守规则能解决的问题,通过潜规则也可以解决;遵守规则解决不了的问题,通过潜规则还可以解决。 
   学车之前,已经对驾校的潜规则有所耳闻;但是,没有体会,印象就不够具体,不够深刻。
   前天晚上,第一次上车。因为我知道教练的脾气都不太好,所以,我就装作一无所知,避免有些地方的认知和教练不一致,还得挨骂;于是,教练就比较仔细地给我讲了一下车里面的一些装置。
   教练一般有两个特点:
      1. 开始训练时,不告诉你这节课的训练内容;你只知道你现在是开着车往前走的,前面10米处的十字路口往这里拐或者是直走你都不知道,如果问了,你得到的基本是一个满带责怪的回答:“直着往前走啊?你想干嘛?”。于是,如果教练不说话,我就直走。
      2. 教练很少教你该怎么做,甚至不会提醒你的,直到你犯错了,他就该损你了。
   因为我知道,学车的过程中,不管因为车出现什么问题,教练都要负全部责任的,所以,你要完全按照教练说的去做,千万不要自作主张;我的每一次换挡都是教练的指令。

   虽然有些操作是很简单的,但是,我都还是不厌其烦地重复地做了;因为教练每天更是在做重复的事情,如果你不愿意做,他肯定会想法让你犯错,然后再骂你的。
   我不愿意和教练说话,因为说话就像是找没趣;即使他没有把问题解释明白,也不敢再问,因为就他们的教学的态度就没法多说话,如果能说10个来回,估计会出人命的。
   虽然我遇到的两个教练都很难对付,不过,由于我还比较谦虚,表现也还不错,所以基本没处什么岔子。

   还有两件事是不得不说的。
   1. 在网上约车时,每辆车都附带有教练的名字,我约的第二个教练是余xx,但是在我礼貌地称呼“余老师”时,教练却对我爱理不理的;后来通过另外一个教练得知,这个教练不是余xx,网上写的是该车的前一个主人,这个教练是李xx;于是,在他给我签字的时候,我便借这个机会给他改了名字,并做了道歉。
   2. 我的那个余xx教练告诉我说,不要总约一个教练;他们天天被困在学校里,每天重复这那些简单的事情,甚是烦躁,如果天天再看同一个面孔,就更加无聊了。话虽这么说,我想,他只是不喜欢你罢了,如果是一个大美女,他一定巴不得让她天天约自己车呢。后来,我找到了报名是认识的那个教练,他告诉我不要频繁换教练,那样不好,其实意思比较明显,你和一个教练混熟了之后,你请他吃个饭,然后送点儿东西什么的,后面的事情就好办多了,而且他至少会比较和气地教你。

   在只遵守规则的情况下,教练只是做自己必须做的事情,而且态度也很不好,因为他没有触犯学校的规定,所以学校也不会处分他的;如果遵守了潜规则,这个过程就不仅仅是教,而是好好地教,这时候,你才算是得到了自己应有的服务了。

    应该说,驾校在市场经济的背景下,也算是一个服务行业了,但是驾校的教练完全没有服务意识,我想,这和驾校的非市场特征有必然的联系。

PHP 中的引用

 
  1. <?php
  2. $a = 222;
  3. var_dump($a);
  4. $b = &$a;
  5. $c = &$b;
  6. $arr = array(&$a);
  7. var_dump($a);
  8. var_dump($b);
  9. var_dump($c);
  10. var_dump($arr);
  11. exit;

返回结果:
# php a.php
int(222)
int(222)
int(222)
int(222)
array(1) {
  [0]=>
  &int(222)
}
———————–
因为数组里面的那个元素是引用方式的,所以,var_dump出来的结果含有 ”&“ ;但是 $a $b $c 之间都是引用关系,为什么没有显示 ”&“ ; 尤其是使用gdb查看的时候,发现 $a $b $c 并没有指向相同的地址,而且 is_ref__gc = 0 ;

是何缘故呢?

有时间再看。。。

什么是OS/2

转自:http://zh.wikipedia.org/wiki/OS/2

概述

OS/2是由微软IBM公司共同创造,后来由IBM单独开发的一套操作系统。OS/2是"Operating System/2"的缩写,是因为该系统作为IBM第二代个人电脑PS/2系统产品线的理想操作系统引入的。

历史

在DOS于PC上的巨大成功后,以及GUI图形化界面的潮流影响下,IBM和Microsoft共同研制和推出了OS/2这一当时先进的个人电脑上 的新一代操作系统。最初它主要是由Microsoft开发的,由于在很多方面的差别,微软最终放弃了OS/2而转向开发Windows“视窗”系统。

OS/2则由IBM独自开发,在发行了若干个版本后, 最大规模的发行版本是于1994年发行的OS/2 Warp 3.0,是取名自星舰迷航记电影中的曲速引擎(Warp drive),来代表其稳定快速的特色。这个版本是第一个运行于X86体系的PC之上的32位 操作系统,早于微软的Windows 95上市。Warp改进了按照界面和加强了对外设的驱动支持,还随系统包含了一组名为“Bonus Pak”,里面有12种应用程序,如文字处理和传真软件等等。

随后的升级版本是OS/2 Warp 3 Connect——一个加强了网络支持的版本。而代号Merlin的OS/2 Warp 4版,是最后一个公开发行的OS/2版本。在与Windows的竞争中,OS/2最终失败了。随后IBM也发行了若干个版本的升级,但仅仅是小范围的使用。

OS/2的新希望是在1999年由Serenity Systems International公司取得IBM的OEM合约,重新打造出eComStation 1.0,并广受好评,许多旧的OS/2系统纷纷升级到eComStation。而目前最新的版本是eComStation 2.0,也已在测试阶段。新版的eComStation支持AMD 64位CPU及可开机的JFS档案系统。

2005年12月23日,IBM宣布 、不再销售和支持OS/2系统。 OS/2的支持者要求IBM将OS/2的原始码开放,但是遭到IBM拒绝。2007年11月,OS/2的支持者再次要求开放源码,但是IBM仍然拒绝[1]。另外则有VoyagerosFree试图以OS/2的架构重制一套新式的桌面环境。

OS/2官方网站: http://www-01.ibm.com/software/os/warp-withdrawal/

 

————————-

了解os/2后的最大的感受就是: 以后看到os2相关的代码不要看就是了,不是什么新东西

什么是oem版

    OEM是英文Original Equipment Manufacturer的缩写,按照字面意思,应翻译称原始设备制造商,指一家厂家根据另一家厂商的要求,为其生产产品和产品配件,亦称为定牌生产或授权贴牌生产。即可代表外委加工,也可代表转包合同加工。国内习惯称为协作生产、三来加工。OEM版就是软件生产商应硬件生产商要求,开发定制的软件版本,如XP的联想OEM版就是微软应联想公司的要求开发的XP版本。

汇编语言之helloworld.as

helloworld.as:

  1. .data                    # 数据段声明
  2.         msg : .string "Hello, world!\n" # 要输出的字符串
  3.         len = . – msg                   # 字串长度
  4. .text                    # 代码段声明
  5. .global _start           # 指定入口函数
  6.         
  7. _start:                  # 在屏幕上显示一个字符串
  8.         movl $len, %edx  # 参数三:字符串长度
  9.         movl $msg, %ecx  # 参数二:要显示的字符串
  10.         movl $1, %ebx    # 参数一:文件描述符(stdout)
  11.         movl $4, %eax    # 系统调用号(sys_write)
  12.         int  $0x80       # 调用内核功能
  13.         
  14.                          # 退出程序
  15.         movl $0,%ebx     # 参数一:退出代码
  16.         movl $1,%eax     # 系统调用号(sys_exit)
  17.         int  $0x80       # 调用内核功能

# as –gstabs -o helloworld.o helloworld.as
# ld -o helloworld helloworld.o
# ./helloworld                            
Hello, world!

其中:
as ld 都是gcc套件中的程序

常用的汇编开发环境:
masm : 微软开发的 ml.exe , 在visualstudio中都包含了
nasm : http://www.nasm.us/
masm32  : http://www.masm32.com/

参考资料:
强力推荐:http://www.ibm.com/developerworks/cn/linux/l-assembly/
http://www.masm32.com/
http://www.nasm.us/

com文件与exe文件的区别

COM文件是一种可执行程序的内存映象文件,它与只有16位地址线的8位机上的CP/M操作系统下的可执行程序结构相似。在COM程序执行过程中,除了调用DOS功能和 ROM BIOS 功能,以及用户特意安排外,段寄存器一般不发生变化。四个段寄存器具有同样的内容,都指向PSP,因此程序的大小仍限于64k以内。COM文件的入口必须是100H,而EXE文件可以有多个段。其中CS和SS以及IP和SP在程序装入时由DOS根据文件头中的信息初始化,ES和DS则指向PSP。EXE文件除了程序本身外,还要包括文件头(512字节的整数倍),因而所占磁盘空间大一些,装入也慢一些,但程序大小不受限制。EXE文件用debug修改后,无法直接写回磁盘上去。

一、相同点
二者都是DOS下的可执行文件。.COM文件是可执行的二进制代码文件,.EXE文件是可执行的浮动代码文件。
二、不同点
(1).COM文件一般比较小,其大小不能超过64KB,而.EXE文件一般比较大,可以超过64KB。
(2).COM文件装入内存较快,而.EXE文件较慢。
(3).COM文件一般比较简单,.EXE文件则比较复杂,编写的难度较大。
(4).COM文件在执行时段寄存器含有相同的值,即代码、数据等混合在一起,而.EXE文件在执行时段寄存器含有不同的值,即代码、数据等驻留在不同的段中。
(5)驻留在磁盘上的.COM文件既无文件头也没有任何其它的内部标识信息,而驻留在磁盘上的.EXE文件有一个文件头(也称作控制信息块)、一个重定位图以及DOS使用的其它信息。
xxxx(6)对.COM文件来说,DOS的装入程序不能为其执行段重定位,而对.EXE文件来说,其执行段可以被重定位。
(7).COM文件只能有一个段,而.EXE文件可以有一个以上的段。
(8).COM文件不能有段堆栈,.EXE文件则可以有。

C++经典问答——读书笔记

1、不要在C++中调用longjmp,因为他会打破析构函数将被调用的顺序。
http://blog.sina.com.cn/s/blog_4d8498800100bonm.html

2.inline函数是否可以访问静态成员变量
可以啊,内联函数就相当于把函数展开,所以就相当于一系列语句,并不是真正的函数。
可以,inline 只是提供给编译器的一个建议,具体怎么做要看实际情况而定,比如说如果函数内出现循环,那么编译器通常不会将其内联。而且是否内联可以通过一系列编译选项和宏来控制,即便如此,具体怎么做仍要看编译器,也可以使用 #pragma auto_inline( [{on | off}] ) 来告知编译器使之自动决定是否内联某些函数 。对于直接在类定义内部实现的函数就相当于前面加上 inline 关键字。

综上所述,内联函数在功能上与普通函数相同 。

3、C++函数中那些不可以被声明为虚函数
常见的不不能声明为虚函数的有:普通函数(非成员函数);静态成员函数;内联成员函数;构造函数;友元函数。

(1).为什么C++不支持普通函数为虚函数?

普通函数(非成员函数)只能被overload,不能被override,声明为虚函数也没有什么意思,因此编译器会在编译时邦定函数。

(2).为什么C++不支持构造函数为虚函数?

这个原因很简单,主要是从语义上考虑,所以不支持。因为构造函数本来就是为了明确初始化对象成员才产生的,然而virtual function主要是为了再不完全了解细节的情况下也能正确处理对象。另外,virtual函数是在不同类型的对象产生不同的动作,现在对象还没有产生,如何使用virtual函数来完成你想完成的动作。(这不就是典型的悖论)

(3).为什么C++不支持内联成员函数为虚函数?
其实很简单,那内联函数就是为了在代码中直接展开,减少函数调用花费的代价,虚函数是为了在继承后对象能够准确的执行自己的动作,这是不可能统一的。(再说了,inline函数在编译时被展开,虚函数在运行时才能动态的邦定函数)

4.为什么C++不支持静态成员函数为虚函数?
这也很简单,静态成员函数对于每个类来说只有一份代码,所有的对象都共享这一份代码,他也没有要动态邦定的必要性。

5.为什么C++不支持友元函数为虚函数?
因为C++不支持友元函数的继承,对于没有继承特性的函数没有虚函数的说法。
*********************************************************************

1.顶层函数:多态的运行期行为体现在虚函数上,虚函数通过继承方式来体现出多态作用,顶层函数不属于成员函数,是不能被继承的。

2.构造函数:
(1)构造函数不能被继承,因而不能声明为virtual函数

(2)构造函数一般是用来初始化对象,只有在一个对象生成之后,才能发挥多态作用,如果将构造函数声明为virtual函数,则表现为在对象还没有生成的情

5、什么时候调用拷贝构造函数?
通过数值传递、数值返回、明确地拷贝一个对象的时候。

6、当使用布局new语法时,程序员将单独承担释放对象的责任。通过明确地调用析构函数可以做到这一点,它是少有的几个需要明确调用析构函数的情况之一。

 
  1. using namespace std;
  2. //在构造函数中调用虚函数,不会产生多态的效果,只会调用自己的成员函数
  3. class Base
  4. {
  5. public:
  6.     Base();
  7.     virtual void f();
  8.     virtual ~Base(){}
  9. };
  10. class Derived : public Base
  11. {
  12. public:
  13.     Derived();
  14.     virtual void f();
  15. };
  16. Base::Base()
  17. {
  18.     cout<<"Base::Base() calling f()!"<<endl;
  19.     f(); //为了明了,此处应改为Base::f();
  20. }
  21. void Base::f()
  22. {
  23.     cout<<"Base::f()!"<<endl;
  24. }
  25. Derived::Derived():Base()
  26. {
  27.     cout<<"Derived::Derived() calling f()!"<<endl;
  28.     f(); //为了明了,此处应改为Derived::f();
  29. }
  30. void Derived::f()
  31. {
  32.     cout<<"Derived::f()!"<<endl;
  33. }
  34. int main()
  35. {
  36.     cout<<"Create a Derived!"<<endl;
  37.     Derived d;
  38.     return 0;
  39. }

private:和protected:赋值运算符返回的是什么?
返回对this对象的一个引用或者void

private:和protected:赋值运算符不必返回*this,这是因为该类的运算符只有非常少的用户,因此限制了返回*this的优点。

我们经常声明赋值运算符为private:以便阻止用户对该类的对象赋值。
为了防止偶然一个成员函数或者一个友元函数调用,我们经常不对其进行定义。

在抽象类中,我们经常把赋值运算符声明为:protected:以便确保当目标是一个抽象类的引用时,赋值操作不会发生。

如何在派生类内使用赋值运算符?
派生类中的赋值运算符首先调用其直接基类的赋值运算符,然后在调用它的成员对象的赋值运算符。

 
  1. /假如基类的赋值运算符是虚拟的,那么派生类应该做什么?
  2. //开发人员应该覆盖基类的赋值运算符,并且提供一个重载的赋值运算符。
  3. #include <iostream>
  4. using namespace std;
  5. class B
  6. {
  7. public:
  8.     virtual ~B();
  9.     virtual B& operator= (const B& b);
  10. };
  11. B::~B(){}
  12. B& B::operator= (const B& b)
  13. {
  14.     cout<<"B::operator= (const B& b)"<<endl;
  15.     return *this;
  16. }
  17. class D : public B
  18. {
  19. public:
  20.     virtual D& oeprator= (const B& b);  //覆盖父类的
  21.     D& operator= (const D& d);          //重载
  22. };
  23. D& D::operator= (const B& b) //覆盖
  24. {
  25.     cout<<"D::operator= (const B& b)"<<endl;
  26.     return *this;
  27. }
  28. D& D::operator= (const D& d)
  29. {
  30.     cout<<"D::operator= (const D& d)"<<endl;
  31.     return *this;
  32. }
  33. void Sample(D& d, B&b, D& d2, B& b2)
  34. {
  35.     cout<<"d = d2: "; d = d2;
  36.     cout<<"d = b2: "; d = b2;
  37.     cout<<"b = b2: "; b = b2;
  38.     cout<<"b = d2: "; b = d2;
  39. }
  40. int main()
  41. {
  42.     D d, b, d2, b2;
  43. //  Sample(d, b, d2, b2);
  44.     return 0;
  45. }

什么是最终类/
最终类(也称叶子类)是一个永久地禁止产生派生类的类。
仅当设计者决定永久地禁止任何将来的类从一个类派生时,才能声明该类为最终类。
C++不支持&ldquo;finnal&rdquo;关键词,因此我们一般只是对其加以注释。

使编译器强制某个类成为最终类并不困难:只需使构造函数private:并且提供public:static create()成员函数。这样阻止派生类的存在,因为派生类不能调用最终类(private:)构造函数。

最终类不应该有protected:数据成员——它所有的数据成员都应该是private:。类似地,最终类也不应该声明任何新的虚拟函数(尽管它经常覆盖继承的虚拟函数)。

转载: http://blog.csdn.net/wangyangkobe/article/details/6118748