C语言中有符号数和无符号数的问题
项目说明
考虑下列代码, 这段代码试图计算数组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[i];
return result;
}
- 请分析这段程序,当参数length等于0时, 运行这段代码应该返回的值是多少?
- 运行这段代码,当参数length等于0时,实际会产生什么结果?
- 请解释你的实际运行的结果产生的原因。
- 修改代码以得出正确的结果。
项目报告
问题1:分析这段程序,当参数length等于0时, 运行这段代码应该返回的值是多少?
我们可以看一下该程序代码的流程图:
很容易分析当length等于0时,for循环中i<=length-1条件不成立,循环体内的语句不会执行,该函数应该返回的result的初始值0。
问题2:运行这段代码,当参数length等于0时,实际会产生什么结果?
我们在不同的编译器下运行了此段代码:
#include <stdio.h>
float sum_elements(float a[], unsigned length)
{
unsigned i;
float result = 0;
for (i = 0; i <=length-1; i++)
result += a[i];
return result;
}
int main()
{
float a[] = {1.7, 2.6, 4.1};
float ret = sum_elements(a, 0);
printf("%.lf", ret);
return 0;
}
结果如下:
- 使用远古编译器Turbo C运行此程序,Compile时没有报错,Run时黑屏无结果,并显示 cpu speed max 100% cycles,Frameskip 0,Program TC。CPU占用100%。
- 使用Dev c++ GCC 4.8.1编译运行此程序,没有产生警告,进程停止,命令行显示Process exited with return value 3221225477。
- 使用Microsoft Visual Studio 2012/2017编译运行此程序,程序虽然运行成功,但并没有得到正确的运行结果,程序运行了几秒钟后在命令行上输出请按任意键继续的结果,并在控制台区报出警告:warning C4018: “<=”: 有符号/无符号不匹配。我们对float ret = sum_elements(a, 0);代码打断点进行程序调试,弹出提示框,显示0x011E14F3 处有未经处理的异常(位于 PBL.exe 中): 0xC0000005: 读取位置 0x0200000 时发生访问冲突。
- 使用中国大学MOOC浙江大学的网页编译器运行此程序,编译器报错:
Error:Memory space at 0x100004(object ‘*(a+1)’) is not initialized
Result += a[i];
问题3:请解释你的实际运行的结果产生的原因。
经查阅资料知,C语言中在有符号类型和无符号类型数据之间测试相性时,编译器会隐性地将有符号的int类型升级为无符号的unsigned int类型。
回到本题中,在执行语句i<=length-1时,因为length是unsigned int类型,所以参与运算的参数都被隐式地被强制转换为unsigned int类型。在主函数中,通过执行float ret = sum_elements(a, 0);语句,使得形参length=0,运算length-1 = 0-1 = -1,-1转换成 unsigned int 的结果是一个非常巨大的正整数的,这就是实际运行结果产生的原因。
接着,我们来分析一下这个巨大的正整数,要知道整数在计算机中通常是以二进制补码的形式存在的,而-1的无符号补码(以32位为例)为0xFFFFFFFF,这是32位无符号整数能表示的最大值:232-1,即4294967296。因为232-1足够大,所以我们尝试将代码float ret = sum_elements(a, 0);中的0改为0xFFFFFFF在Microsoft Visual Studio 2012上进行程序调试运行,得到的结果和没有修改前的结果是一样的。
所以我们得出结论,运行该程序报错或者得不到运行结果的原因是执行从0到0xFFFFFFFF的循环时,数组下标越界,内存读写异常,在vs 2012/2017等较新的编译器中,并没有出现错误,而Turbo c中没有检测机制,会输出了-NAN的运行结果,经过查阅是不可表示的值。
问题4:修改代码以得出正确的结果。
通过分析代码错误的产生原因,经小组讨论,共得到4组代码:
- 代码1
// 把unsigned 修改成有符号数int
float sum_elements(float a[ ], int length)
{
int i;
float result = 0;
for (i=0;i<=length-1;i++)
result +=a[i];
return result;
}
- 代码2
// 提前判断length是否为0,如果是直接return result;
float sum_elements(float a[ ], unsigned length)
{
int i;
float result = 0;
if(length <= 0)
return result;
for (i=0;i<=length-1;i++)
result +=a[i];
return result;
}
- 代码3
//把 i<=length-1改成 i<length
float sum_elements(float a[ ], unsigned length)
{
int i;
float result = 0;
for (i=0;i<length;i++)
result +=a[i];
return result;
}
- 代码4
// 使用强制类型转换 i<=(int)length-1
float sum_elements(float a[ ], unsigned length)
{
int i;
float result = 0;
for (i=0;i<(int)length-1;i++)
result +=a[i];
return result;
}
新人发文,欢迎大家批评指正!