在说这个问题之前,先说两个需要知道的背景知识:

(1)语言的类型的强制转换不会修改原来的数据,会另外的开辟一个临时的或者程序中指定的空间来存储强制转换后的值。

(2)C++引用的实现是在符号表中动了手脚,把自己的变量符号对应的内存地址写成了它所引用的那个变量的内存地址了。

(3)C++的cout函数的执行,是根据变量的名称找到对应的内存地址,然后根据变量的类型从内存中抓取相应的数据。


有了上面的两个知识点,看下面的程序:

#include <iostream>
using namespace std; int main()
{
float a = 1.0f;
cout << (int)a << endl;
cout << (int &)a << endl; cout << endl; float b = 0.0f;
cout << (int)b << endl;
cout << (int &)b << endl; return 0;
}

  

程序执行结果:

首先先来看(int&)a是什么意思:

这句话的意思就是给a声明了一个匿名的引用,并且这个引用是临时的,int类型的。会再符号表中增加这么一个条目

(int&)a这个表达式就返回这个临时的变量temp,它是a的引用,只不过类型是int的。

所以在执行

cout << (int &)a << endl;

这句话的时候,cout就根据temp的地址和类型抓取数据。

看完了(int&)a,那么(int)a呢?似乎很熟悉,但是真的知道具体的过程吗。也许吧,看下面:

前面说到,对数据的类型强制转换,不会修改原来的数据的内容。所以(int)a这个表达式会再符号表中产生这样一个条目:

看到了,这里的临时变量的内存地址不是原来的地址,是操作系统又重新分配了一块临时的内存地址。这块内存地址的值就是变量类型强制转换后的值。

这样可以看出来这两个语句一个是在原来的内存基础上,把float类型的数据以int输出,一个是强制转转数据类型,从新开辟了一块新的存储空间放转换后的值,然后再输出。

上面分析的是程序的原理,应该是这么实现的。但是编译器为了减少访问内存的次数(符号表也在内存中的哦~),经常用寄存器来处理这些临时的变量,看这个程序的汇编代码:

--- C:\Program Files\Microsoft Visual Studio\MyProjects\TestThread\testThread.cpp  -----------------------------------------------------------------
: #include <iostream>
: using namespace std;
:
: int main()
: {
push ebp
mov ebp,esp
sub esp,48h
push ebx
push esi
push edi
lea edi,[ebp-48h]
0040178C mov ecx,12h
mov eax,0CCCCCCCCh
rep stos dword ptr [edi]
: float a = 1.0f;
mov dword ptr [ebp-],3F800000h//这里看到,1.0在内存中的存储方式,是单精度浮点数的存储方式
: cout << (int)a << endl;
0040179F push offset @ILT+(std::endl) (004010c8)
004017A4 fld dword ptr [ebp-]//把这个内存单元中的数据以浮点数方式加载到浮点寄存器中
004017A7 call __ftol (0042133c)//这个函数就把浮点数寄存器中的数据转换成了int类型,并把结果放在了eax寄存器中。转换完之后的结果00000001h
004017AC push eax//输出eax中的数据
004017AD mov ecx,offset std::cout (0047ff88)
004017B2 call @ILT+(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004010fa)//ILT是Debug模式下函数的入口表,Release下就直接调用函数了,245是int类型数据的输出函数序号
004017B7 mov ecx,eax
004017B9 call @ILT+(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011db)
: cout << (int &)a << endl;
004017BE push offset @ILT+(std::endl) (004010c8)
004017C3 mov eax,dword ptr [ebp-]//直接把a的值原封不动的copy到eax寄存去中
004017C6 push eax//输出eax中的数据
004017C7 mov ecx,offset std::cout (0047ff88)
004017CC call @ILT+(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004010fa)
004017D1 mov ecx,eax
004017D3 call @ILT+(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011db)
: cout << a << endl;
004017D8 push offset @ILT+(std::endl) (004010c8)
004017DD mov ecx,dword ptr [ebp-]
004017E0 push ecx
004017E1 mov ecx,offset std::cout (0047ff88)
004017E6 call @ILT+(std::basic_ostream<char,std::char_traits<char> >::operator<<) ()//285是float类型数据的输出函数序号
004017EB mov ecx,eax
004017ED call @ILT+(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011db)
:
: cout << endl << endl;
004017F2 push offset @ILT+(std::endl) (004010c8)
004017F7 push offset @ILT+(std::endl) (004010c8)
004017FC mov ecx,offset std::cout (0047ff88)
call @ILT+(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011db)
mov ecx,eax
call @ILT+(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011db)
:
: float b = 0.0f;
0040180D mov dword ptr [ebp-],
: cout << (int)b << endl;
push offset @ILT+(std::endl) (004010c8)
fld dword ptr [ebp-]
0040181C call __ftol (0042133c)
push eax
mov ecx,offset std::cout (0047ff88)
call @ILT+(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004010fa)
0040182C mov ecx,eax
0040182E call @ILT+(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011db)
: cout << (int &)b << endl;
push offset @ILT+(std::endl) (004010c8)
mov edx,dword ptr [ebp-]
0040183B push edx
0040183C mov ecx,offset std::cout (0047ff88)
call @ILT+(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004010fa)
mov ecx,eax
call @ILT+(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011db)
:
: return ;
0040184D xor eax,eax
: }

从汇编语言中看到,(int)a是要经过类型强制转换的,并且把转换后的值放在寄存器中输出,(int&)a直接把原来的数据copy到一个寄存器中输出。


重要的说明一下:

符号表是在编译阶段产生的,上面说的temp和temp1这样的临时的变量也是在编译的时候都已经弄到了符号表中,只不过它 的作用域仅仅的就是那句话。不是在执行阶段在往符号表中增加的条目。

最后再说一个知识点:单精度浮点数、双精度浮点数的存储。

单精度和双精度浮点数的存储方式和int类型的存储方式是完全不同的,int的1在内存中的存储方式是00000001h,int的0是00000000h,但是浮点数要用符号位+阶码+尾数的方式存储。

以单精度的float为例:

它的形式是1.M * 2E-127,其中E是指数为的移码形式。所以对于float的1.0,尾数M=0,阶码E=127,符号位是0,所以对应的机器码是:3F800000h。

提示:为什么浮点数阶码部分要用移码?

(1)使用移码方便运算。2的指数部分有正有负,使用了移码之后,2的指数依然有正有负,但是数据的真正的存储位E就完全是正的值了,没有了负值,这样能加快运算。

(2)为了统一浮点数的0和整数的0。整数0的各个二进制位是全0(公认的了),但是如果不用移码,浮点数的全0是1,用了移码之后,这个是就是1.0 * 20-127,由于这个数太小了,这时会发生溢出,也就是0。

C++中(int&)和(int)的区别的更多相关文章

  1. C#中(int)、int.Parse()、int.TryParse()和Convert.ToInt32()的区别

    转自:http://www.cnblogs.com/leolis/p/3968943.html 在编程过程中,数据转换是经常要用到的,C#中数据转换的方法很多,拿将目标对象转换为 整型(int)来讲, ...

  2. C#中(int)、int.Parse()、int.TryParse()和Convert.ToInt32()的区别 <转>

    作者:Statmoon 出处:http://leolis.cnblogs.com/   在编程过程中,数据转换是经常要用到的,C#中数据转换的方法很多,拿将目标对象转换为整型(int)来讲,有四种方法 ...

  3. .net中三种数据类型转换区别((int),Int32.Parse() 和 Convert.toInt32() )

    (typename)valuename,是通用方法: Convert类提供了灵活的类型转换封装: Parse方法,适用于向数字类型的转换. 例如,(int),Int32.Parse() 和 Conve ...

  4. java 中int与integer的区别

    int与integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而integer是对象,用一个引用指向这个对象 1.Java 中的数据类型分为基本数据类型 ...

  5. C#中Convert.ToInt32、int.TryParse、(int)和int.Parse四者的区别

    Convert.ToInt32.(int)和int.Parse三者的区别: 首先:Convert.ToInt32 适合将object类类型转换成int类型,如Convert.ToInt32(sessi ...

  6. java中Integer和int的区别(转)

    int和Integer的区别 1.Integer是int的包装类,int则是java的一种基本数据类型 2.Integer变量必须实例化后才能使用,而int变量不需要 3.Integer实际是对象的引 ...

  7. mysql中int(3)与int(11)有什么区别吗?

    注意:这里的M代表的并不是存储在数据库中的具体的长度,以前总是会误以为int(3)只能存储3个长度的数字,int(11)就会存储11个长度的数字,这是大错特错的. 其实当我们在选择使用int的类型的时 ...

  8. java中int和Integer的区别?为什么有了int还要有设计Integer?

    参考https://blog.csdn.net/chenliguan/article/details/53888018 https://blog.csdn.net/myme95/article/det ...

  9. sql语句中 int(1)与int(10)有什么区别?资深开发竟然能理解错

    过完春节该投入战斗了,上班第一天发现了一个挺有意思的知识点给大家分享一下:一直以来的的误区我们都认为了int后面的跟的数字为最大显示宽度会对后面插入的参数会有限制,其实倒不是这样的 # 困惑 最近遇到 ...

  10. C++中int *p[4]和 int (*q)[4]的区别

    这俩兄弟长得实在太像,以至于经常让人混淆.然而细心领会和甄别就会发现它们大有不同. 前者是指针数组,后者是指向数组的指针.更详细地说. 前: 指针数组;是一个元素全为指针的数组.后: 数组指针;可以直 ...

随机推荐

  1. .net mvc笔记2_Essential C# Features

    Essential C# Features 1.Using Automatically Implemented Properties public class Product { private st ...

  2. Java的static详解

    static ['stætɪk] n. 静电:静电干扰 adj. 静态的:静电的:静力的 在计算机上我们译为:静态的.在Java种根据它修饰对象不同,我们可以划分为 1. static对象 2. st ...

  3. Uber司机手机终端问答篇

    手机客户端 Q:自带安卓手机可以使用吗? A:安卓终端已经推出,请在微信页面点左下菜单选取“下载司机端APP”查看! Q:对自带苹果手机的要求? A:4S型号及以上且未越狱:使用3G或4G网络 Q:客 ...

  4. Linux 电子书共享下载--大家一起学习

    文件名 大小 时间 到期时间 操作    鸟哥私房菜(全集).pdf 36.57 MB 2 小时前 免费永久       练成Linux高手.chm 3.76 MB 2 小时前 免费永久        ...

  5. java 在方法中新建线程,传参和加锁详解

    在实际开发中,往往在基本两三种创建线程的方法之外,还用到一个简单的创建线程调用方法的情况,代码如下: public void deleteRedisData(RedisKey redisKey){ n ...

  6. linux查看和设置系统时间 hwclock && date

    http://www.linuxso.com/command/hwclock.html查看时间{1. date查看系统时钟, hwclock查看硬件时钟hwclock && date ...

  7. 使用iscroll4可能会遇到的问题(转:记录)

    1.在iscroll4的滚动容器范围内,点击input框.select等表单元素时没有响应这个问题原因在于iscroll需要一直监听用户的touch操作,以便灵敏的做出对应效果,所以它把其余的默认事件 ...

  8. python函数any()与all()

    any(iterable) all(iterable) any()与all()函数的区别,any是任意,而all是全部. 版本:该函数适用于2.5以上版本,兼容python3版本. any(itera ...

  9. web 性能优化指南阅读笔记

    1.关于拥塞预防算法 PRR-比例降速,RFC6937 规定的一个新算法,其目标是改进丢包后的恢复速度,谷歌测量结果:该算法改进丢包造成的平均连接延迟减少了3%-10%.PRR是linux 3.2+内 ...

  10. 基于FPGA的cordic算法的verilog初步实现

    最近在看cordic算法,由于还不会使用matlab,真是痛苦,一系列的笔算才大概明白了这个算法是怎么回事.于是尝试用verilog来实现.用verilog实现之前先参考软件的程序,于是先看了此博文h ...