int a和(int &)a的區別(轉)
(1)語言的類型的強制轉換不會修改原來的數據,會另外的開辟一個臨時的或者程序中指定的空間來存儲強制轉換後的值。
(2)C++引用的實現是在符號表中動了手腳,把自己的變量符號對應的內存地址寫成了它所引用的那個變量的內存地址了。
(3)C++的cout函數的執行,是根據變量的名稱找到對應的內存地址,然後根據變量的類型從內存中抓取相應的數據。
有了上面的兩個知識點,看下面的程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#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 -----------------------------------------------------------------
1: #include <iostream>
2: using namespace std;
3:
4: int main()
5: {
00401780 push ebp
00401781 mov ebp,esp
00401783 sub esp,48h
00401786 push ebx
00401787 push esi
00401788 push edi
00401789 lea edi,[ebp-48h]
0040178C mov ecx,12h
00401791 mov eax,0CCCCCCCCh
00401796 rep stos dword ptr [edi]
6: float a = 1.0f;
00401798 mov dword ptr [ebp-4],3F800000h//這裏看到,1.0在內存中的存儲方式,是單精度浮點數的存儲方式
7: cout << (int)a << endl;
0040179F push offset @ILT+195(std::endl) (004010c8)
004017A4 fld dword ptr [ebp-4]//把這個內存單元中的數據以浮點數方式加載到浮點寄存器中
004017A7 call __ftol (0042133c)//這個函數就把浮點數寄存器中的數據轉換成了int類型,並把結果放在了eax寄存器中。轉換完之後的結果00000001h
004017AC push eax//輸出eax中的數據
004017AD mov ecx,offset std::cout (0047ff88)
004017B2 call @ILT+245(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004010fa)//ILT是Debug模式下函數的入口表,Release下就直接調用函數了,245是int類型數據的輸出函數序號
004017B7 mov ecx,eax
004017B9 call @ILT+470(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011db)
8: cout << (int &)a << endl;
004017BE push offset @ILT+195(std::endl) (004010c8)
004017C3 mov eax,dword ptr [ebp-4]//直接把a的值原封不動的copy到eax寄存去中
004017C6 push eax//輸出eax中的數據
004017C7 mov ecx,offset std::cout (0047ff88)
004017CC call @ILT+245(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004010fa)
004017D1 mov ecx,eax
004017D3 call @ILT+470(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011db)
9: cout << a << endl;
004017D8 push offset @ILT+195(std::endl) (004010c8)
004017DD mov ecx,dword ptr [ebp-4]
004017E0 push ecx
004017E1 mov ecx,offset std::cout (0047ff88)
004017E6 call @ILT+285(std::basic_ostream<char,std::char_traits<char> >::operator<<) (00401122)//285是float類型數據的輸出函數序號
004017EB mov ecx,eax
004017ED call @ILT+470(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011db)
10:
11: cout << endl << endl;
004017F2 push offset @ILT+195(std::endl) (004010c8)
004017F7 push offset @ILT+195(std::endl) (004010c8)
004017FC mov ecx,offset std::cout (0047ff88)
00401801 call @ILT+470(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011db)
00401806 mov ecx,eax
00401808 call @ILT+470(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011db)
12:
13: float b = 0.0f;
0040180D mov dword ptr [ebp-8],0
14: cout << (int)b << endl;
00401814 push offset @ILT+195(std::endl) (004010c8)
00401819 fld dword ptr [ebp-8]
0040181C call __ftol (0042133c)
00401821 push eax
00401822 mov ecx,offset std::cout (0047ff88)
00401827 call @ILT+245(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004010fa)
0040182C mov ecx,eax
0040182E call @ILT+470(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011db)
15: cout << (int &)b << endl;
00401833 push offset @ILT+195(std::endl) (004010c8)
00401838 mov edx,dword ptr [ebp-8]
0040183B push edx
0040183C mov ecx,offset std::cout (0047ff88)
00401841 call @ILT+245(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004010fa)
00401846 mov ecx,eax
00401848 call @ILT+470(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011db)
16:
17: return 0;
0040184D xor eax,eax
18: }
從匯編語言中看到,(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。
from:http://www.cnblogs.com/stemon/p/4421178.html
int a和(int &)a的區別(轉)