1. 程式人生 > >int a和(int &)a的區別(轉)

int a和(int &)a的區別(轉)

not strong c++引用 alt ios ostream light write highlight

(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的區別(轉)