计算机系统实验三---数据类型的转换

程序调试与实践:数据的存储与运算---数据类型的转换

整数之间的数据类型转换

C语言中,数据的赋值不是发生在真值上的复制,而是在机器数上的赋值。
赋值语句 b=a;

情况一:相同宽度的两个整型数据之间的赋值
将一个n位的整数a赋值给另一个n位的整数b,赋值发生在机器数上,所以此情况下a和b的机器数一样。
a和b的机器数相同,真值不一定相同,取决于a和b的数据类型。

情况二:将一个短的数据类型赋值给一个长的数据类型
将一个n位的整数a赋值给另一个m位的整数b(n<m).
把a的n位01序列复制在b的低n位,b的高m-n位由a的数据类型决定。
1.如果a的数据类型是无符号整数,不管b是什么数据类型都需要采用零扩展策略,即将b的高m-n位置为0。
2.如果a的数据类型是带符号整数,不管b是什么数据类型都采用符号扩展策略,即将b的高m-n位置为a的符号位。

情况三:将一个长的数据类型赋值给一个短的数据类型
将一个n位的整数a赋值给另一个m位的整数b(n>m)--------截断策略
将a的低m位的01序列赋值给b,丢弃a的高位部分。

1.在home目录下的test文件夹中新建intconversion.c文本文件
intconversion.c代码如下

#include "stdio.h" 
void main() 
{  
  short si=-100; 
  unsigned short usi=si; 
  int i=usi; 
  unsigned ui=usi; 
  int i1=si; 
  unsigned ui1=si; 
  int i2=0x12348765; 
  short si2=i2; 
  unsigned short usi2=i2; 
  int i3=si2;
  int i4=4294967296;
printf("si=%d,usi=%u,i=%d,ui=%u,i1=%d,ui1=%u\n",si,usi,i,ui,i1,ui1);
printf("i2=%d,si2=%d,usi2=%u,i3=%d,i4=%d \n", i2,si2,usi2,i3,i4);
}

2.编译运行

gcc -o0 -m32 -g intconversion.c -o intconversion

3.反汇编该文件

objdump -S intconversion>intconversion.txt

在这里插入图片描述
4.打开gdb调试运行到printf语句

gdb intconversion

在这里插入图片描述
说明之前的赋值语句都已经执行过
显示当前的esp ebp寄存器的内容

i r esp ebp

显示当前的栈帧内容

x/60xb $esp

在这里插入图片描述
由si的机器数,可知小端方式存储
在这里插入图片描述
5.分析一:unsigned short usi=si;
si、usi:16位整数,属于相同宽度的两个整数数据之间的赋值。
则为第一种情况,即usi和si的机器数相同,但是真值与数据类型相关。

./ intconversion

在这里插入图片描述
在这里插入图片描述
6.分析二:int i=usi; unsigned ui=usi;
usi是16位无符号整数,i是32位带符号整数,ui是32位无符号整数。
即零扩展:将一个短的无符号整数赋值给一个长的整数类型。
编译是根据源操作数的类型来确定使用的指令。
在这里插入图片描述
i、ui:机器数相同,低16位是usi的01序列,高16位都是0。

7.分析三:int i1=si; unsigned ui1=si;
把si赋值给带符号整数i1,把si赋值给无符号整数ui1,si是16位带符号整数,i1和 ui1都是32位整数。即为第二种情况中的符号扩展,两个语句编译转换后的指令是一样的,都是符号扩展传送指令,
在这里插入图片描述
i1和ui1的机器数相同,低16位都是si的01序列,高16位都是si的符号位。

8.分析四:int i2=0x12348765; short si2=i2; unsigned short usi2=i2;
i2赋值给无符号整数usi2,i2是32位带符号整数,si2是16位带符号整数,usi2是16位无符号整数。即为第三种截断操作,两个语句编译转换后的指令是一样的
在这里插入图片描述
si2、usi2:机器数相同,都是i2的低16位,i2的高12位都被丢弃

由于数据类型的宽度不同,所以机器数上的赋值过程中,可能出现扩展或截断的情况,所以计算机中的赋值操作不同于数学上的等于操作。
i2赋值给带符号整数si2,i2是32位带符号整数,si2是16位带符号整数。即为第三种截断操作,si2是i2的低16位,i2的高16位被舍弃。si2赋值给i3,为第二种情况中的符号扩展,扩展出来的高16位是si2的符号位0,所以i2和i3的高16位不一样,则i2和i3的机器数必不相同,所以,i2和i3的值不相等。

整数和浮点数之间的转换

整数与浮点数之间的转换,是在编码格式上的转换
带符号整数:补码的编码方法。 例:int是32位带符号整数,1位符号位,31位数值位。
浮点数:采用IEEE 754标准,有float、double两种基本格式。 例:float格式32位编码,1位符号位,8位阶码,23位尾数。

1.在home目录下的test文件夹中新建intconversion.c文本文件
intconversion.c代码如下

#include "stdio.h" 
int main() 
{
    int  i1=0x7fffffff, i2, itemp;
    float  f1=0x987654321, f2,ftemp;
    ftemp=i1; 
    i2=ftemp;   //i2=(int)(float)i1; 
    itemp=f1; 
    f2=itemp;   //f2=(float)(int)f1;
    printf("i1=%d,i2=%d,f1=%f,f2=%f\n", i1,i2,f1,f2); 
}

在这里插入图片描述
2.编译运行,反汇编该文件,输出相应的结果

gcc -o0 -m32 -g intfloat.c -o intfloat
objdump -S intfloat>intfloat.txt
./intfloat

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3.打开gdb调试运行到printf语句

gdb intfloat

在这里插入图片描述
说明之前的赋值语句都已经执行过
显示当前的esp ebp寄存器的内容

i r esp ebp

显示当前的栈帧内容

x/15xw $esp

在这里插入图片描述

4.分析一:i1和i2
在这里插入图片描述
i1和i2的机器数不一样。
在整数与浮点数之间的转换时,要进行数据编码格式上的转换,而不是机器数上的直接复制。补码->float编码->补码。

i1: 0x7fff ffff 补码
=0111 1111 1111 1111 1111 1111 1111 1111B
=1.11 1111 1111 1111 1111 1111 1111 1111*2^30 真值

要将i1写成尾数和阶码的格式,尾数的有效数字有31位,float格式浮点数的有效数字只有24位,则需要对尾数进行舍入操作,根据IEEE 754的舍入原则,执行入的操作,则在有效数字的最低位+1。因为10.0B不符合规格化的尾数,所以把尾数调整为1.0B
在这里插入图片描述
符号位位0,阶码为31+127,尾数为23个0
在这里插入图片描述

i2的机器数编码比i1的机器数编码大1,i2的真值为负数,与i1的真值差异较大。

5.分析二:f1和f2
在这里插入图片描述
在这里插入图片描述
用二进制值编码有36个二进制位,超过float浮点的24位精度,所以进行舍操作。
f1的浮点数编码是符号位为0,阶码为35+127,尾数为23位二进制,尾数部分如下:
在这里插入图片描述
0x51187654是f1的机器数。
f1->itemp,当f1转换为int时需要做编码格式的转换,f1的机器数对初始数据做过社操作,对于初始数据f1的二进制值依旧有36位,但是其低12位全为0,int型只有32位二进制,f1有36位二进制,所以超过了int型的表示范围。
itemp赋值给f2时,又要进行编码格式上的转换,itemp的补码转换为真值,则f2的编码的符号位为1,阶码为31+127,尾数为23个0,则f2的机器数为十六进制的cf000000H,f1和f2的机器数不一样。
根本原因在于f1超出了int型数据的表示范围,转换为int型时,系统赋值为最高位为1,其余位为0的机器数。

总结:
1. 整数与浮点数转换时,是在编码格式上的转换。
2. 在int float转换中,可能会有精度损失、溢出、小数丢弃等问题导 致的数据不一致。
3. 不同编译系统采用的编译优化有差异,同一程序在不同系统上运行,得 到的结果可能不一样。

C语言中的自动类型转换

1.在home目录下的test文件夹中新建fn.c文本文件
fn.c代码如下

#include "stdio.h"
int f1( unsigned int n ) 
{ 
   int sum = 1, power = 1; 
   int i;
   for ( i = 0; i <= n - 1; i ++ ) 
   { 
     power *= 2; 
     sum += power; 
   }
   return sum; 
} 
int main() 
{
   int  sum;
   sum=f1(0); 
   printf("sum=%d\n",sum); 
   return 0;
}

在这里插入图片描述
2.编译运行,反汇编该文件
gcc -o0 -m32 -g fn.c -o fn
objdump –S fn >fn.txt
在这里插入图片描述
i是带符号整数,n是无符号整数,带符号整数和无符号整数比较时,系统采用的是无符号整数的比较指令,i会被自动转换为无符号整数,然后与n-1进行比较
在这里插入图片描述
3.打开gdb调试运行到55a这条指令处
gdb fn
在这里插入图片描述
在这里插入图片描述
显示当前的edx eax寄存器的内容
i r edx eax
4.分析一:
在这里插入图片描述
edx中存储着n-1的内容,是16进制的8个f,是32位无符号整数的最大值
eax中存储着i的内容,i的内容在程序的执行过程中,从0开始会逐渐加1,但是在无符号数比较时,无论i的值为多少n-1大于等于i永远成立,i<0xffffff永远成立,因此这条转移指令永远转移到544的指令处执行,不会退出循环体,程序陷入死循环。
不能让系统采用无符号整数的比较转移指令,而要采用带符号整数比较的转移指令,所以n必须设置为带符号整数。
5.分析二:
修改n的定义,定义n为带符号整数,再编译该程序,反汇编可执行目标文件,查看反汇编后的内容
在这里插入图片描述
打开gdb调试运行到557这条指令处
gdb fn
在这里插入图片描述
显示当前eax寄存器的内容,当n为0时n-1的内容依旧是16进制的8个f,对于带符号整数来说这是负数-1,i=0时,因为0<=-1不成立,所以退出循环体,得到sum的值为1
在这里插入图片描述
程序中少数无符号整数在带符号整数和无符号整数一起运算时,系统会自动类型转换为无符号整数处理,而且无符号整数0-1得到了能表示的无符号整数的最大值,则会带来程序的一些错误问题。

  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值