关于有符号数、无符号数和数据类型的总结
一、
CPU只会根据输入信号进行逻辑运算,在硬件级别是没有有符号无符号的概念,运算结束会根据运算前的信号和输出信号来设置一些标志位,是不是有符号由写程 序的人决定,标志位要看你把操作数当有符号还是无符号来选择,就像内存中的数据,你可以按照需要来解析,原始数据在那里,你要按什么数据格式来解析在于自 己的选择,所以玩汇编的要做到心里有数,加减法只有一套指令,因为这一套指令同时适用于有符号和无符号。下面这些指令:mul div movzx … 是处理无符号数的,而这些:imul idiv movsx … 是处理有符号的。举例来说:
内存里有 一个字节x 为:0x EC ,一个字节 y 为:0x 02 。当把x,y当作有符号数来看时,x = -20 ,y = +2 。当作无符号数看时,x = 236 ,y = 2 。下面进行加运算,用 add 指令,得到的结果为:0x EE ,那么这个 0x EE 当作有符号数就是:-18 ,无符号数就是 238 。所以,add 一个指令可以适用有符号和无符号两种情况。(呵呵,其实为什么要补码啊,就是为了这个呗,:-))
乘法运算就不行了,必须用两套指令,有符号的情况下用imul 得到的结果是:0x FF D8 就是 -40 。无符号的情况下用 mul ,得到:0x 01 D8 就是 472 。
二、
C又是可怕的,因为它把机器层面的所有的东西都反应了出来,像这个有没有符号的问题就是一例(java就不存在这个问题,因为它被设计成所有的整数都是有符号的)。为了说明c的可怕特举一例:
#include <stdio.h>
#include <string.h>
int main()
{
int x = 2;
char * str = "abcd";
int y = (x - strlen(str) ) / 2;
printf("%d\n",y);
}
结果应该是 -1 但是却得到:2147483647 。为什么?因为strlen的返回值,类型是size_t,也就是unsigned int ,与 int 混合计算时类型被自动转换了,结果自然出乎意料。。。
观察编译后的代码,除法指令为 div ,意味无符号除法,即将-2看做无符号数了。
解决办法就是强制转换,变成 int y = (int)(x - strlen(str) ) / 2; 强制向有符号方向转换(编译器默认正好相反),这样一来,除法指令编译成 idiv 了。我们知道,就是同样状态的两个内存单位,用有符号处理指令 imul ,idiv 等得到的结果,与用无符号处理指令mul,div等得到的结果,是截然不同的!所以牵扯到有符号无符号计算的问题,特别是存在讨厌的自动转换时,要倍加小心!(这里自动转换时,无论gcc还是cl都不提示!!!)
为了避免这些错误,建议,凡是在运算的时候,确保你的变量都是 signed 的。
三、自动类型转化时的,短字节向长字节转化时,有符号数会符号扩展,无符号数会0扩展;长字节向短字节转化时,会自动截取高位,留下低位字节。特别在做算术运算时,比如乘除法,就可能涉及到有符号数自动转化为无符号数,从而采用不同的指令处理。
例如:下面的代码输出是什么,为什么?
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}
这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 ”>6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式 计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。
扩展一下,如果是这段代码呢?
{
unsigned int a = 6;
int b = -20;
printf("%d\n",a+b);
(a+b > 6) ? puts("> 6") : puts("<= 6");
}
结果是输出:-14 >6
为什么?我们要注意,在printf()中,a+b也是转换为unsigned int(0xF2)的,但是%d要求又是按照有符号数输出,所以0xf2当成有符号数输出了-14。
四。特别注意在有常数的算数表达式中,往往有隐含的数据类型转化,因为整数常量并没有明确的被指出其的数据类型,
整常数在不加特别说明时总是正值。如果需要的是负值,则负号“-”必须放置于常数表达式的前面。
每个常数依其值要给出一种类型。当整常数应用于一表达式时,或出现有负号时,常数类型自动执行相应的转换,十进制常数可等价于带符号的整型或长整型,这取决于所需的常数的尺寸。
八进制和十六进制常数可对应整型、无符号整型、长整型或无符号长整型,具体类型也取决于常数的大小。如果常数可用整型表示,则使用整型。如果常数值大于一 个整型所能表示的最大值,但又小于整型位数所能表示的最大数,则使用无符号整型。同理,如果一个常数比无符号整型所表示的值还大,则它为长整型。如果需 要,当然也可用无符号长整型。
但是,可以在一个常数后面加一个字母l或L强制其数据类型,则认为是长整型。如1 0 L、7 9 L、0 1 2 L、0 11 5 L、0 X A L、0 x 4 f L等。
L, U, LU,叫类型后缀,.
一般在程序中出现3种数据.我把它们叫,变量,常量,字面量.
变量,常量一般都已经规定了类型了的,所以后缀针对的是字面量.
由于语言默认,整数是int型.即字面量 12 是 int型的.
如果要表示 长整型的12 就得加后缀 12L,无符号的 12U,无符号长整型的12UL.
具体这些有什么用,你需要了解整数在内存中的存放形式...存放长度(位)...