C语言中有符号数和无符号数的问题

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;
 }
  1. 请分析这段程序,当参数length等于0时, 运行这段代码应该返回的值是多少?
  2. 运行这段代码,当参数length等于0时,实际会产生什么结果?
  3. 请解释你的实际运行的结果产生的原因。
  4. 修改代码以得出正确的结果。

项目报告

问题1:分析这段程序,当参数length等于0时, 运行这段代码应该返回的值是多少?

我们可以看一下该程序代码的流程图:
C语言中有符号数和无符号数的问题_第1张图片
    很容易分析当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;
 }

结果如下:

  1. 使用远古编译器Turbo C运行此程序,Compile时没有报错,Run时黑屏无结果,并显示 cpu speed max 100% cycles,Frameskip 0,Program TC。CPU占用100%。
    C语言中有符号数和无符号数的问题_第2张图片
  2. 使用Dev c++ GCC 4.8.1编译运行此程序,没有产生警告,进程停止,命令行显示Process exited with return value 3221225477
  3. 使用Microsoft Visual Studio 2012/2017编译运行此程序,程序虽然运行成功,但并没有得到正确的运行结果,程序运行了几秒钟后在命令行上输出请按任意键继续的结果,并在控制台区报出警告:warning C4018: “<=”: 有符号/无符号不匹配。我们对float ret = sum_elements(a, 0);代码打断点进行程序调试,弹出提示框,显示0x011E14F3 处有未经处理的异常(位于 PBL.exe 中): 0xC0000005: 读取位置 0x0200000 时发生访问冲突
    C语言中有符号数和无符号数的问题_第3张图片
  4. 使用中国大学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的运行结果,经过查阅是不可表示的值。
C语言中有符号数和无符号数的问题_第4张图片

问题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
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;
}

    新人发文,欢迎大家批评指正!

你可能感兴趣的:(灯火阑珊,C语言,有/无符号数)