本来这部分内容考纲没有明确指出来的,但是往年的试题却是及其热门,可以出选择题,也可以出大题结合寄存器部分的知识考。所以这不仅仅可以看出一个人的语言功底,同时也可以看出对补码反码原码等的熟悉程度。
所以,在学习这个之前最好先回顾一下之前讲的内容,
挑战408——组成原理(2)——二进制数和十六进制数
挑战408——组成原理(3)——原码,补码,反码
好了言归正传,我们先来看看C语言中的基本数据类型(别问我为啥就强调C语言,你学这门课偏硬件):
基本数据类型
- 整型(int):即定点整数,在寄存器中一般用补码表示,其最高位代表符号位,一般是4个字节。具体的位数跟变异平台有关。
- 无符号整数(unsigned):无符号,即不考虑数据位,二进制码表示的数就是其值。一般用补码表示。
- 长整型和短整型(long short):用补码表示,这只是位数不同罢了(一个长一个短)。
- 单精度浮点数和双精度浮点数(float double):就是我们平时说的小数点会移动的小数,前面的32位,后面的是64位。
数据间的保留,当计算记过超出机器所能表示的范围的时候,就会发生“溢出现象”。此时面临一个问题,那么是丢掉前面的N位还是丢掉后面的N位呢?一般我们选择保留后面的N位,丢掉前面的N位,若丢掉后发现不能表示正确的结果,说明产生溢出,还有一种情况就是不受影响了。
十进制转二进制(代码)
在进入正题之前我们先介绍一下十进制跟二进制怎么转换(原谅我是个代码迷,就喜欢验证一下)。代码奉上:
#include <iostream>
#include <cstdlib> //引入这个头文件,使用itoa函数,因为这是C语言中的一个古老函数
#include <string>
using namespace std;
int main() {
char array[64];
int number = -4321;
/*
*itoa函数原型:char* itoa(int value,char *string,int radix)
*用法:将输入的数转换为相应进制的数,radix,对应的进制
*/
itoa(number,array,2);
printf("integer = %d\n string = %s\n", number, array);
return 0;
}
结果如下:
我用的是C++的环境编写的,有人可能会问,那为什么不用标准的COUT来呢?其实在精准度方面,C语言的printf函数确实是比C++做的好点。不信?那就试试下面这段代码。只是改变了一下输出的方式:
#include <iostream>
#include <cstdlib> //引入这个头文件,使用itoa函数,因为这是C语言中的一个古老函数
#include <string>
using namespace std;
int main() {
char array[64];
int number = -4321;
/*
*itoa函数,原型:char* itoa(int value,char *string,int radix)
*用法:将输入的数转换为相应进制的数,radix,对应的进制
*/
itoa(number,array,2);
cout << "number = " << number << endl;
for (int i = 0; i < 64; i++)
{
cout << array[i];
}
return 0;
}
那么会出现什么结果?自己去试试吧,话说到这里。
转换过程
同类型之间的转换
进入正题,先看下面的一段代码:
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
int main() {
short x = -4321;
unsigned short y = (unsigned short) x;
printf("x = %d, y = %u\n",x,y);
}
结果如下:
我们前面说过,它们都是在寄存器中以补码的形式存放的,所以,我们把这两个数写成补码的方式(截取后16位):
我们惊奇的发现,这里的二进制表示居然一模一样。但是结果却不同,原因是X的第一位解释成了符号,而后面的才解释成为真值。(这里不知道有没有较真的人发现,-4321的补码没有错,但是61215的补码总觉得怪怪的?哈,肯定是不记得了正数的反码 = 补码 =原码,负数的才是取反后加一)。
所以,强制类型转换实际上是位值不变,只是改变了解释这些位的方式。
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
int main() {
unsigned short x = 65535;
short y = (short) x;
printf("x = %d, y = %u\n",x,y);
}
同理分析这一段代码就一目了然了。
不同字节类型之间的转换
我们知道,不同类型的数据往往其占有的字节大小不一样,比如:
在结合上面的第一次的代码,-4321前面的16位都是1又是为何?不够的数都用1来凑?显然不对。
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
int main() {
int x = 165537, u = -34991;
short y = (short)x;
short v = (short)u;
printf("x = %d, y = %u\n",x,y);
printf("u = %d, v = %u\n",u,v);
}
当数据太大,用二进制不好表示的时候我们选择用16进制(在之前提到过),分别是0x000286a1,0x86a1,0xffff7751, 0x7751,可以看出大字节转向小字节的转换的规则是:低位直接赋值(赋几位就看你的数据占几位,比如short占2字节,16位,一个16进制数代表4位2进制数),高位直接截断。
那么显然,反过来,小字节转向大字节那就得扩展位数了。比如:
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
int main() {
short x = -4321;
int y = x;
unsigned short u = (unsigned short)x;
unsigned int v = u;
printf("x = %d, y = %u\n",x,y);
printf("u = %d, v = %u\n",u,v);
}
输出的数用16进制表示我就不说了,结论:从短字长到长字长的转换,相应的位值不变,向高位补充的数为符号位,所以-4321前面的16位都是1,因为它符号位是负号,如果是正号,那就变成0.
这部分内容考试必考,2分选择题左右。后面刷题的时候回再说。