信息的表示和处理(整数部分)

这里经过分析一个练习题来总结:
考虑下列代码,这段代码试图计算数组a中全部元素的和,其中元素的数量由参数length给出。数组

/* WARNING: This is buggy code */
float sum_elements(float a[], unsigned length) {
    int i;
    float result = 0;
    
    for (i = 0; i <= length - 1; i++)
        result += a[j];
    return result;
}

当参数length等于0时,运行这段代码应该返回0.0。但实际上,运行时会遇到一个内存错误。请解释为何会发生这样的状况,而且说明如何修改代码。学习

解:code

首先咱们发现参数length的形式参数的类型为unsigned,是一个无符号数,而无符号数是非负的,好比一个字节能够表示的无符号数范围是0~255,在代码中若是参数length等于0,则在循环中会首先对length减1来判断,而这个结果并非一个负数,而是一个很大的正数。举个例子,对于一个字节,咱们这里用十六进制来表示方便一点,0的十六进制为0x00,而0 - 1会获得0xFF,这个数表示为无符号数的大小为255,也就是一个字节所能表示的最大无符号数,这就产生了错误,可是若是咱们用有符号数来解读0xFF,此时这个数的大小为-1,就正确了,因此咱们的改正方法是将length声明为int类型。内存

在C语言中,咱们经过unsigned来声明一个变量为无符号数,使用int来声明有符号数,在计算机中使用补码来表示有符号数。
在计算的时候,无符号数很容易,好比对于0101,它的无符号数大小为:0 * 2^3 + 1 * 2^2 + 0 * 2^1 + 1 * 2^0 = 5,很符合直觉,按照2的幂次计算而后都加起来便可;element

而对于有符号数,使用补码来表示,对于一个w位数的补码,它的最高有效位称为符号位,1表明负数,0表明正数,并且计算方法也不是将2的幂次都加起来,举个例子,对于0101,它的补码大小为:-0 * 2^3 + 1 * 2^2 + 0 * 2^1 + 1 * 2^0 = 5,很明显这是一个正数,由于最高位是0,而对于1011,它的补码大小为:-1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 1 * 2^0 = -5,咱们能够发如今计算补码的时候,要算上符号位,若是是0,那么结果和无符号是同样的,若是是1,则还要加上一个负的2的幂次,但愿上面两个例子能很清楚说明有符号和无符号数之间的区别和类似之处,不要混淆二者,它们只是对一个二进制01序列的两种不一样的解读方法,好比上面的1011,在无符号中,它的大小就是:1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 1 * 2^0 = 11。数学

但就是由于两种不一样的解读方法,有时候咱们在使用的时候就会出现一些反直觉的错误,这就是因为咱们对计算机中的表示不了解的后果,特别是一些隐式转换,好比一个有符号数和一个无符号数运算,则有符号数会自动转换为无符号数,那么若是这个有符号数恰好是一个负数,当它转换为无符号数时,是否是就变成了一个较大的无符号正数,好比:-1 < 0U,这里U表示0是一个无符号数,这个表达式的结果是0,这很违反咱们平时学习的数学,为何-1小于0是假的呢?这就是由于-1自动转换为无符号数了,咱们这里仍是取一个字节,-1的补码表示为1111,0的无符号数表示为0000,可是当-1转换为无符号数后,1111就表示15,是一个较大的正数,因此15 < 0是假的。这里涉及到补码和无符号数之间的转换,有便捷的计算公式,我这里就不说了。class

相关文章
相关标签/搜索