有些基础知识点如果不经常温故可能就会忘记,难道是因为我已经老了吗?人要是老了,脑子是真的不好用了啊。今天看一个很简单的c代码:
#include <stdio.h>
#include <stdlib.h>
typedef int eint32;
typedef unsigned int euint32;
int main()
{
unsigned char a = 300;
euint32 *p = &a;
printf("a = %d, *p = %d\n", a, *p);
if(a > 0)
{
printf("a > 0\n");
}
else
{
printf("a < 0\n");
}
return 0;
}
这个输出会是多少呢?
这里涉及到了两个知识点:char 类型的取值范围及指针类型。我们知道unsigned char的取值范围为0~255, 即0000 0000 ~ 1111 1111。从上面的编译警告也可以得到一些信息,如第1个警告说是a可能是被赋予的是截断后的值;而第2个是类型不匹配进行了赋值。a = 44是怎么来的呢?其实就是被截断后的值,因为 300 的二进制为 0001 0010 1100(前面的0就不列了),而 char 只占 8 位,所以只会把低 8 位赋给了 a (即0010 1100,其十进制就是44);而*p的值就不确定了,因为 p 是 unsigned int 类型,所以 *p 则会取 4 个字节的值(sizeof(unsigned int) = 4)。而 p 指向了 a,所以取的就是以 a 地址为起点的 4 个字节的值。用gdb跟踪如下:
a 地址里的值的二进制就是 00101100, 而十进制就是44;那当我们用 *p 取值的时候,就是以地址 0x7fffffffe347 为起点的4个字节的内容,如:
代码里用的 %d 为有符号的十进制进行输出形式,但我们从windows 的计算器上看这个二进制 对应的十进制为:
这也对应不上啊,同样的一个二进制不同的两个值。这是为什么呢?这里又涉及到原码、反码、补码的知识点了,有兴趣的可以看这篇 char 的取值范围 。 上面这样的二进制是错的,因为我使用的是机器是小端的存储方式。
在计算器上的应该是这样的:
所以打印 *p 的值为 4293084972,但是程序中是以 %d 打印的,是有符号的,其结果为:
其结果为负数 -1882324。上面说过原码、反码、补码,那我们反过来,把上面的 4293084972 的二进制先减1为反码:
然后再将反码还原为原码,好符号位不变,其他所有位取反,得:
这里最高位为1表示负数,不包含最高位的值为:1882324
而加上符号位它就是 -1882324
总结:
补码:正数的补码等于它的原码;负数的补码等于反码+1
从上面的输出我们也可以得出,如果是以无符号方式输出则其二进制是多少其值就是多少,而以有符号输出,因其最高位是 1,表示负数,则其二进制是以补码方式存储的,要输出其十进制则需要还原为原码。