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

(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. 在实体对象中访问导航属性里的属性值出现异常“There is already an open DataReader associated with this Command which must be closed first”

    在实体对象中访问导航属性里的属性值出现异常“There is already an open DataReader associated with this Command which must be ...

  2. Debian下自动备份文件并上传到远程FTP服务器且删除指定日期前的备份Shell脚本

    说明:  1.备份目录/home/osyunwei下面所有的文件到/home/osyunweibak里面,并且保存为osyunwei20120701.tar.gz的压缩文件格式(2012_07_01是 ...

  3. 转: JS自定义事件的定义和触发(createEvent, dispatchEvent)

    四.伪DOM自定义事件 这里的“伪DOM自定义事件”是自己定义的一个名词,用来区分DOM自定义事件的.例如jQuery库,其是基于包装器(一个包含DOM元素的中间层)扩展事件的,既与DOM相关,又不直 ...

  4. JSP与Servlet的中文乱码处理

    注:百度来的,改了改... jsp页面的的头要设置好 <%@ page language="java" contentType="text/html; charse ...

  5. NAND Flash中常用的纠错方式(ECC算法)

    Hanming,RS,BCH —— NAND Flash中常用的纠错方式 因为闪存中会有出错的可能,如果没有使用ECC模块,读出的数据和写入的数据会有不匹配的可能,也许一个文件中只有一两个bit不匹配 ...

  6. Juicy Couture_百度百科

    Juicy Couture_百度百科 Juicy Couture

  7. Struts学习之集成Ajax

    转自:http://blog.csdn.net/hanxuemin12345/article/details/38782213 一,引题 1,Json数据格式简介 JSON是脱离语言的理想的数据交换格 ...

  8. JSP——页面三大部分(指令、脚本、动作组件)

    一.JSP简介: JSP(Java Server Pages,Java服务器端页面开发技术) JSP可以实现的技术都可以通过Servlet实现,他们本质上是一样的.但JSP设计的目的在于简化表示层的表 ...

  9. nodejs入门demo

    demo的实例引用自:http://www.runoob.com/nodejs/nodejs-event.html, 官方文档:https://nodejs.org/dist/latest-v6.x/ ...

  10. 解决windows7搜索不了txt文本内容的问题

    windows7默认的搜索框是只搜索文件名,若是要文件内容的话,需要这样设置: 打开"我的电脑",左上角"组织"→"文件夹和搜索选项"→&q ...