1.4,signed、unsigned 关键字
编译器缺省默认情况下数据为 signed 类型 的。
举例:
上面的解释很容易理解,下面就考虑一下这个问题:
include<stdio.h>
include<string.h>
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
//printf("a[%d] = 0x%x\n", i, a[i]);
//printf("a[%d] = %c\n", i, a[i]);
}
printf("%d", strlen(a));
return 0;
}
我们知道在计算机系统中,数值一律用补码来表示(存储)。int在真正存储在内存中的二进制数不是值的原码,而是其补码。主要原因是使用补码,可 以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补码表示的数 相加时,如果最高位(符号位)有进位,则进位被舍弃。正数的补码与其原码一致;负数的 补码:符号位为 1,其余位为该数绝对值的原码按位取反,然后整个数加 1。
按照负数补码的规则,可以知道-1 的补码为 0xff,-2 的补码为 0xfe
当 i 的值为 127 时,a[127]的值为-128,而 -128 是 char 类型数据能表示的最小的负数。当 i 继续增加,a[128] 的值肯定不能是-129。因为这时候发生了溢出,-129 需要 9 位才能存储下来,而 char 类型 数据只有 8 位,所以最高位被丢弃。剩下的 8 位是原来 9 位补码的低 8 位的值,即 0x7f。 当 i 继续增加到 255 的时候,-256 的补码的低 8 位为 0。然后当 i 增加到 256 时,-257 的补 码的低 8 位全为 1,即低八位的补码为 0xff,如此又开始一轮新的循环......
按照上面的分析,a[0]到 a[254]里面的值都不为 0,而 a[255]的值为 0。strlen 函数是计 算字符串长度的,并不包含字符串最后的‘\0’。而判断一个字符串是否结束的标志就是看 是否遇到‘\0’。如果遇到‘\0’,则认为本字符串结束。
\0 在 C 语言中表示空字符或空终止符(null terminator)。它的 ASCII 编码值是 0。在 ASCII 编码中,值为 0 的字符就是空字符。它没有任何可打印的字符形式,通常用于表示字符串的结尾。空字符 \0 是 ASCII 编码中 128 个字符中的一个。它的编码值是 0x00,对应二进制 00000000。空字符 \0 作为字符串的结束标志非常重要。它允许应用程序确定字符串的长度,而不需要保存字符串长度的额外信息。在 C 语言中,字符串常量的最后一个字符总是空字符 \0。这是 C 语言字符串处理的基础。
留三个问题:
1),按照我们上面的解释,那-0 和+0 在内存里面分别怎么存储?
2),int i = -20; unsigned j = 10; i+j 的值为多少?为什么?
3), 下面的代码有什么问题?
unsigned i ;
for (i=9;i>=0;i--)
{
printf("%u\n",i);
}
1)正零 (+0) 的存储:在计算机中,正零 (+0) 通常以 00000000 的二进制形式存储。也就是说,正零的位模式是全 0。这种表示方式是唯一的,没有其他的正零表示。负零 (-0) 的存储:负零 (-0) 的二进制位模式是 10000000。也就是说,负零的最高位是 1,表示负数,其余位都是 0。这种表示方式与正零的位模式是不同的。
2)当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。
标准转换的规则是:短的的向长的转;有符号的向无符号的转。
无符号十进制数 = 2^32 + 补码对应的有符号十进制数。
因为int i = -20,所以unsigned int i = 2^32 - 20(十进制)。
所以i+j = unsigned int i + unsigned int j = 2^32 - 20 + 10 = 2^32 - 10(十进制)= 4294967296 -10 = 4294967286。
int i = -20; unsigned int j = 10; i+j 的值为多少?
//-20(补码) = 0xFFFFFFEC;
//int i = 0xFFFFFFEC;
//i + j = unsigned int i + unsigned int j;
//int i -> unsigned int i;(signed -> unsigned)
//unsigned int i = 0xFFFFFFEC;
//i = 4294967276(十进制);
//(i+j) = 4294967276 + 10 = 4294967286
3)
//当i==-1时,unsigned i = 4294967295;
补充1:unsigned 和 signed 类型相互转换
1)unsigned → signed 的时候:
如果位数相同,直接复制;
如果位数不同:
① unsigned 位数 < signed 位数,unsigned 直接复制到 signed 低位,signed 高位填0;
② unsigned 位数 > signed 位数,直接装载 unsigned 低位到 signed 。
2)signed → unsigned 的时候:
如果位数相同,直接复制;
如果位数不同:
① signed 位数 < unsigned 位数,signed 直接复制到 unsigned 低位,unsigned 高位填符号位
(如果 signed 是负数,则 unsigned 高位填1,;如果 signed 是正数,则 unsigned 高位填0);
② signed 位数 > unsigned 位数,直接装载 signed 低位到 unsigned 。
举例1:
include <stdio.h>
//32位系统
int main(int argc, char* argv[])
{
unsigned char a = -1;
char b = a;
printf("%d %d", a, b);
return 0;
}
//-1(补码) = 0xFFFFFFFF;
//unsigned char a = 0xFF;(char 1 byte)
//char b = 0xFF;(unsigned -> signed)
//printf("%d %d", a, b)
//unsigned char a -> int a;(unsigned -> signed)
//int a = 0x00FF;(int 4 byte)
//char b -> int b
//int b = 0xFFFF;
//a = 255(十进制)
//b = -1(十进制)
举例2:
include <stdio.h>
//32位系统
int main(int argc, char* argv[])
{
unsigned short a = -1;
short b = a;
printf("%d %d", a, b);
return 0;
}
//-1(补码) = 0xFFFFFFFF;
//unsigned short a = 0xFFFF;(short 2 byte)
//short b = 0xFFFF;(unsigned -> signed)
//printf("%d %d", a, b);
//unsigned short a -> int a;(unsigned -> signed)
//int a = 0x0000FFFF;(int 4 byte)
//short b -> int b
//int b = 0xFFFFFFFF;
//a = 65535(十进制)
//b = -1(十进制)
举例3:
include <stdio.h>
//32位系统
int main(int argc, char *argv[])
{
unsigned int a = -1;
int b = a;
printf("%d %d", a, b);
printf("%u %u", a, b);
return 0;
}
//-1(补码) = 0xFFFFFFFF;
//unsigned int a = 0xFFFFFFFF;(int 4 byte)
//int b = 0xFFFFFFFF;(unsigned -> signed)
//printf("%d %d", a, b);
//unsigned int a -> int a;(unsigned -> signed)
//int a = 0xFFFFFFFF;(int 4 byte)
//int b = 0xFFFFFFFF;
//a = -1(十进制)
//b = -1(十进制)
//printf("%u %u", a, b);
//unsigned int a = 0xFFFFFFFF;
//int b -> unsigned int b;(signed -> unsigned)
//unsigned int b = 0xFFFFFFFF;
//a = 4294967295(十进制)
//b = 4294967295(十进制)
举例4:
int a = -1;
unsigned int b = -1;
printf("%d\n", a + b);
//-1(补码) = 0xFFFFFFFF;
//int a = 0xFFFFFFFF;(int 4 byte)
//unsigned int b = 0xFFFFFFFF;
//printf("%d\n", a + b);
//a + b = unsigned int a + unsigned int b;
//unsigned int a = 0xFFFFFFFF;(signed -> unsigned)
//unsigned int (a + b) = 0xFFFFFFFE;(溢出,保留后32位)
//int (a + b) = 0xFFFFFFFE;(unsigned -> signed)
//(a + b) = 4294967294(十进制);