C语言 有符号类型转换为无符号类型

C语言中变量的类型变换比较多,有些地方需要时刻注意,不然很可能写出带有bug的代码并深埋入系统,难以察觉。


例如 有符号类型和无符号类型运算,有符号类型转换为无符号类型。

需要注意的有两种情况:
1、有符号和无符号的算术运算
2、有符号和无符号的比较运算

一、验证有符号类型转换为无符号类型:
程序:
#include <stdio.h>

int main()
{
int a = -20;
unsigned int b = 6;

if((a+b) > 0)
{
printf("signed to unsigned\n");
}
else
{
printf("unsigned to signed\n");
}

return 0;
}

结果为:
signed to unsigned

这说明在c语言操作中,如果遇到无符号数与有符号数之间的操作,编译器会自动转化为无符号数来进行处理。
这个也验证了有符号和无符号算术运算操作时会将有符号类型转换为无符号类型来计算。


二、比较运算

#include <stdio.h>

int main()
{
int a = -2;
unsigned int b = 1;

printf("a = %d, b = %u\n\n", a, b);

if(a > b)
{
printf("a > b\n");
}
else
{
printf("a < b\n");
}

return 0;
}
结果:
a > b

这说明有符号数和无符号数进行比较运算时(==,<,>,<=,>=),有符号数隐式转换成了无符号数(即底层的补码不变,但是此数从有符号数变成了无符号数),此时的a的值远大于b。

=============

C中有符号数与无符号数转化之间的危险



无符号数与有符号数之间存在着很多细节问题,稍有不慎就可能导致程序出现不可预料的错误。
 

陷阱


在C语言中,如果一个运算包含一个有符号数和一个无符号数,那么C语言会隐式地将有符号数转换为无符号数,这对于标准的算术运算没什么问题,但是对于 < 和 > 这样的关系运算符来说,它会出现非直观的结果,这种非直观的特性经常会导致程序中难以察觉的错误

看下面的例子:
int strlonger(char *s, char *t)
{
return strlen(s) - strlen(t) > 0;
}



上面的函数看起来似乎没什么问题, 实际上当s比t短时,函数的返回值也是1, 为什么会出现这种情况呢?

原来strlen的返回值类型为size_t,C语言中将size_t定义为unsigned int,当s比t短时,strlen(s) - strlen(t)为负数,但无符号数的运算结果隐式转换为无符号数就变成了很大的无符号数.

为了让函数正确工作,代码应该修改如下 :
return strlen(s) > strlen(t);

2002年, 从事FreeBSD开源操作系统项目的程序员意识到,他们对getpeername函数的实现存在安全漏洞.代码的简化版本如下:
//void *memcpy(void *dest, void *src, size_t n);

#define KSIZE 1024
char kbuf[KSIZE];

int copy_from_kernel(void *user_dest, int maxlen)
{
int len = KSIZE < maxlen ? KSIZE : maxlen;
memcpy(user_dest, kbuf, len);
retn len;
}

你看出了问题所在吗?


=========

测试时一些细节地方会影响结果,需要注意


1、测试的时候需要注意printf的输出参数对结果的影响:
#include <stdio.h>

int main()
{
unsigned int a = 1;
signed int b = -2;

printf("%d\n", a + b);
printf("%u\n", a + b);

return 0;
}

printf("%d\n", a + b)中,由于%d的作用,使a+b以有符号十进制的形式输出,此时,最高位的1为符号位,减一取反得到其原码,得到1000 0001,即十进制的-1;

printf("%u\n", a + b)中,由于%u的作用,使a+b以无符号十进制的形式输出,此时,最高位的1并非符号位,其表示数值大小,不用减一取反,得到1111 1111,实际Linux系统中,该数用4个字节表示,也就是说实际上这里有16个"1",即十进制数的4294967295(=2^32-1)。

综上,该段程序的输出结果为:

-1

4294967295

2、不要使用char类型来验证这个规则,会使分析变得复杂

一方面是因为char类型在不同编译器环境下可能是无符号的,也可能是有符号的,取决于编译器的实现。
【这个可以搜索“char可以是负数吗”来了解。】

另一方面char类型在运算的时候会提升为int类型再运算,这会导致结果分析极其繁琐,结果不同不代表无符号和有符号这个运算规则有错。













 

猜你喜欢

转载自blog.csdn.net/elikang/article/details/85762104