C++中(int&)和(int)的区别
在说这个问题之前,先说两个需要知道的背景知识:
(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)的区别的更多相关文章
- C#中(int)、int.Parse()、int.TryParse()和Convert.ToInt32()的区别
转自:http://www.cnblogs.com/leolis/p/3968943.html 在编程过程中,数据转换是经常要用到的,C#中数据转换的方法很多,拿将目标对象转换为 整型(int)来讲, ...
- C#中(int)、int.Parse()、int.TryParse()和Convert.ToInt32()的区别 <转>
作者:Statmoon 出处:http://leolis.cnblogs.com/ 在编程过程中,数据转换是经常要用到的,C#中数据转换的方法很多,拿将目标对象转换为整型(int)来讲,有四种方法 ...
- .net中三种数据类型转换区别((int),Int32.Parse() 和 Convert.toInt32() )
(typename)valuename,是通用方法: Convert类提供了灵活的类型转换封装: Parse方法,适用于向数字类型的转换. 例如,(int),Int32.Parse() 和 Conve ...
- java 中int与integer的区别
int与integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而integer是对象,用一个引用指向这个对象 1.Java 中的数据类型分为基本数据类型 ...
- C#中Convert.ToInt32、int.TryParse、(int)和int.Parse四者的区别
Convert.ToInt32.(int)和int.Parse三者的区别: 首先:Convert.ToInt32 适合将object类类型转换成int类型,如Convert.ToInt32(sessi ...
- java中Integer和int的区别(转)
int和Integer的区别 1.Integer是int的包装类,int则是java的一种基本数据类型 2.Integer变量必须实例化后才能使用,而int变量不需要 3.Integer实际是对象的引 ...
- mysql中int(3)与int(11)有什么区别吗?
注意:这里的M代表的并不是存储在数据库中的具体的长度,以前总是会误以为int(3)只能存储3个长度的数字,int(11)就会存储11个长度的数字,这是大错特错的. 其实当我们在选择使用int的类型的时 ...
- java中int和Integer的区别?为什么有了int还要有设计Integer?
参考https://blog.csdn.net/chenliguan/article/details/53888018 https://blog.csdn.net/myme95/article/det ...
- sql语句中 int(1)与int(10)有什么区别?资深开发竟然能理解错
过完春节该投入战斗了,上班第一天发现了一个挺有意思的知识点给大家分享一下:一直以来的的误区我们都认为了int后面的跟的数字为最大显示宽度会对后面插入的参数会有限制,其实倒不是这样的 # 困惑 最近遇到 ...
- C++中int *p[4]和 int (*q)[4]的区别
这俩兄弟长得实在太像,以至于经常让人混淆.然而细心领会和甄别就会发现它们大有不同. 前者是指针数组,后者是指向数组的指针.更详细地说. 前: 指针数组;是一个元素全为指针的数组.后: 数组指针;可以直 ...
随机推荐
- Windows 技巧
1.反选 快捷键: Alt+E+I 2. windows7 以上 cmd命令 切换目录 F:\>cd /d c:\windowsc:\Windows> 3.
- HDU 5741 Helter Skelter(构造法)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5741 [题目大意] 一个01相间的串,以0开头,给出的序列每个数字表示连续的0的个数或者1的个数, ...
- python 在 for i in range() 块中改变 i 的值的效果
先上一段代码: for i in range(3): i = 2 print(i) 实际结果是: 2 2 2 可以发现实际效果就是 在每次执行 for 语句块的内容后 i 会被重新赋值
- hdu 1394 zoj 1484 求旋转序列的逆序数(并归排序)
题意:给出一序列,你可以循环移动它(就是把后面的一段移动到前面),问可以移动的并产生的最小逆序数. 求逆序可以用并归排序,复杂度为O(nlogn),但是如果每移动一次就求一次的话肯定会超时,网上题解都 ...
- Extjs4 类的定义和扩展
一般定义方式,注意方法和函数的添加方式不同.(添加函数只能用override方式添加不知为什么,有知道的,请搞之.) 定义一个类,并给他一个方法 1: Ext.define('Simple.Class ...
- 超高性价比USB转CAN适配器,2500V工业级隔离,兼容ZLG软件
淘宝链接: http://item.taobao.com/item.htm?spm=a230r.1.14.16.QGsAZg&id=20134109594&initiative_new ...
- python闭包以及装饰器
通俗的定义:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure).它只不过是个“内层”的函数,由一个名字(变量)来指代,而这个名字(变 ...
- 使用coding.net来托管源码(可以免费存放私有项目的哦)(转载)
coding.net是国内新兴的一个项目管理平台,功能主要包括:代码托管.在线运行环境.监控代码质量,兼有一定的社交功能.在线运行环境支持Java.Ruby.Node.js.PHP.Python.Go ...
- NSArray 与 NSMutableArray 的排序
由于集合的使用过程中,经常需要对数组进行排序操作,此博客用于总结对在OC中对数组排序的几种方法 1.当数组中存放的是Foundation框架中提供的对象时,直接使用 compare:方法 如:NSSt ...
- activemq demo指南
queue与topic的技术特点对比 topic queue 概要 Publish Subscribe messaging 发布订阅消息 Point-to-Point 点对点 有无状态 topic ...