【不是惡夢的開始,而是再度的精進。指標】


在談指標之前,我們先回顧一下變數。


我們都知道要使用某個變數之前都要先宣告變數,讓編譯程式來分配某個記憶體位置來存放變數。

當我們這麼宣告  int a = 7;  時,程式本身做了這樣的事情

記憶體位址      變數值    變數名稱

 0x70fe14          7          a    


程式會分配一塊記憶體 0x70fe1c 這個位址來存放變數值 7。
而所謂的一塊記憶體位址,並不是只有 0x70fe1c 這一個位址,而是根據作業系統與變數型態來決定需要多大的記憶體,以我的電腦來說,int 整數型態的變數佔4個位元組,所以變數a就佔0x70fe1c、0x70fe1d、0x70fe1e、0x70fe1f這四個位元組,而以0x70fe1c為標記位址。
所以用四個位元組來存放7這個數值。
但是變數名稱a又是存到哪兒呢?
其實記憶體內的這四個位元組的名字就是a,a是給我們看的,四個位元組是給機器(電腦)看的。


在談指標之前,先介紹一下這個符號 '&'。


& 是取指運算子,宣告格式如下:

&變數名稱;

 

&a 就是取出變數a在記憶體中的位址


什麼是指標呢?


因為這個"記憶體位址0x70fe1c"是指向變數值7,所以我們就稱"記憶體位址0x70fe1c"是變數 的指標


什麼是指標變數呢?


存放指標(記憶體位址)的變數就是指標變數,所以指標變數並不是存放數值、字元、字串...,而是存放記憶體位址。


指標變數的宣告也很類似各種變數的宣告,格式如下:

int a;           //宣告整數變數
int* pi = &a;   // 把指標變數設定出指向特定的地方

int* pi = NULL; // 空指標變數


我們來看宣告指標變數的宣告

int*  就是宣告一個整數型的指標變數
pi   就是這個整數型的指標變數名稱,我的習慣p代表指標,i代表整數,這不是硬性規定
= &a  就是取出變數a的記憶體位址設定給等號左邊的指標變數pi


千萬避免這樣宣告 int* pi; 
這是避免指標指向記憶體的其他資料區、指向程式區或系統區,而我們冒然的使用這樣的指標,就有可能改變到其他區域記憶體內的資料,後果難料。

在我的測試 int* pi; 居然編譯成功,完全沒有編譯錯誤的警告,所以真的要很小心使用指標。


我們再依上面的例子來說明:

當我們這麼宣告  int a = 7; int* pi =&a; 時,程式本身做了這樣的事情

記憶體位址      變數值         變數名稱

 0x70fe18      0x70fe14     pi (指標變數)

 0x70fe14           7            a  (數值變數)


當我們宣告 int* pi = &a; 就是把變數a的位址設定給(存入)指標變數
這時候指標變數 pi 內存的值是變數a的位址 0x70fe14 
而程式也同樣會分配一個位址 0x70fe18 來儲存指標變數 pi(整數變數 a 的位址)


各種基本資料型態的變數都可以設定各自類型的指標

例:

int* pi = &a;

char* pch = &b;

double* pd = &c;

float* pf = &d;


***  不 要 把 不 同 類 型 的 指 標 存 入 你 指 定 類 型 的 指 標 變 數 ,否 則 編 譯 時 會 產 生 無 法 轉 換 類 型 的 錯 誤  ***

指標中有了取指運算子&,還有一個叫依址取值運算子*
這個星號
"*" 到目前為止有三種可能

1.它是乘號運算子,例: a = b * 3;
2.它是宣告指標,例: int* pi = &a;
3.就是我們現在要說的依址取值運算子


* 是依址取指運算子,宣告格式如下:

*變數名稱;

例:

int a = 5;
int* pi = &a;
*pi = 100;  // 這樣就把變數 
的值設為100


我們就來段小程式碼


 #include <iostream>
 
 using namespace std;
 
 int main(int argc, char** argv)
 {
     int a = 5;
     int* pi = &a;
     *pi = 100;  // 這樣就把變數a的值設為100
     cout << "\na = " << *pi;
     
     return 0;
     
  } 

 
 

執行結果:

a = 100

我們可以透過&a取得變數a的位址(指標),再將這位址(指標)存到指標變數pi,最後將100存入依指標變數內的位址(指標)找到的變數a,這樣我們就可以透過指標來設定或操作變數a的值了(*pi = 100;)。

在指標的設定運算與設值中,C++允許我們把多個同資料型態的指標指向單一個變數

例:


#include <iostream>

using namespace std;

int main(int argc, char** argv)
{
    int a = 100, b = 200;
    int* pi1 = &a;
    int* pi2 = &b;
    
    cout << "原本的設值...\n";
    cout << "\na =  " << a << "     pi1 = " << pi1 << "    *pi1 = " << *pi1;
    cout << "\nb =  " << b << "     pi2 = " << pi2 << "    *pi2 = " << *pi2;
    
    pi2 = pi1;
    
    cout << "\n\n重新設值後....\n";
    cout << "\na =  " << a << "     pi1 = " << pi1 << "    *pi1 = " << *pi1;
    cout << "\nb =  " << b << "     pi2 = " << pi2 << "    *pi2 = " << *pi2;
    
    
    
    return 0;
 } 

 

 
執行結果:

原本的設值...

a =  100     pi1 = 0x70fe08    *pi1 = 100
b =  200     pi2 = 0x70fe0c    *pi2 = 200

重新設值後....

a =  100     pi1 = 0x70fe08    *pi1 = 100
b =  200     pi2 = 0x70fe08    *pi2 = 100


pi2 = pi1;

這是把 pi1 指標變數設定給 pi2,讓 pi2 指標變數內的指標同樣指向 pi1 指標變數內的指標所指的地方,所以口語上就會省略的說把指標pi1設給指標pi2,使兩個指標都指向變數a,就是這樣省略式的口語讓指標和指標變數就說不清了。

或許就當是滑稽ㄏㄨㄚˊ ㄐㄧ 一樣吧,忘了是幾年前誰說了滑稽ㄏㄨㄚˊ ㄐㄧ 該唸成 滑稽ㄍㄨˇ ㄐㄧ,可是在他說之前,明明大家都唸成 ㄏㄨㄚˊ ㄐㄧ 好幾十年了。
以此類推,pi1、pi2 就說成指標吧(反正也說了好幾十年了,只要心裡明白這是指標變數),免得在看其它的書籍或與人討論時,又牛頭對不上馬嘴。


反正都在談指標了,就多看一眼記憶體。

例:

 
#include <iostream>

using namespace std;

int main(int argc, char** argv)
{
    int  a = 7, b = 12;
    int* pia = &a;
    int* pib = &b;
    
    cout << "\npia  = " << pia << "     a   = " << a << "     sizeof(pia) = " << sizeof(pia);
    cout << "\n&pia = " << &pia << "     pia = " << pia;
    cout << "\n";
    cout << "\npib  = " << pib << "     b   = " << b;
    cout << "\n&pib = " << &pib << "     pib = " << pib;
    
    int c = 2;
    int* pic =&c;
    cout << "\n";
    cout << "\npic  = " << pic << "     c   = " << c;
    cout << "\n&pic = " << &pic << "     pic = " << pic;
        
    cout << "\n\n";
    cout << "\npic =   " << pic   << "   *pic = " << *pic;
    
    pic -= 1;
    
    cout << "\npic =   " << pic << "   *pic = " << *pic;
    
    return 0;
 } 

 


 執行結果:

pia  = 0x70fdec     a   = 7     sizeof(pia) = 8
&pia = 0x70fdf8     pia = 0x70fdec

pib  = 0x70fdf0     b   = 12
&pib = 0x70fe00     pib = 0x70fdf0

pic  = 0x70fdf4     c   = 2
&pic = 0x70fe08     pic = 0x70fdf4


pic =   0x70fdf4   *pic = 2
pic =   0x70fdf0   *pic = 12


把記憶體位置排序一下


&pic = 0x70fe08 
&pib = 0x70fe00
&pia = 0x70fdf8 

pic  = 0x70fdf4
pib  = 0x70fdf0
pia  = 0x70fdec 


在我的電腦裡,整數佔四個位元組,但整數指標佔八個位元組(好像和書說的不一樣(需要確認一下))。
    
    
我的程式碼故意先輸出變數a,b和a,b的指標,再宣告變數c和c的指標,但這並不影響編譯程式安排變數和指標的位置,依然變數放在一起,接著才一起放指標。    
     
   
   
指標除了前面所談的,可以設定運算 pi2 = pi1; 外,指標也可以作加法與減法運算,當然啦!這是指對於指標內的位址所作的運算。


當我們把指標減一的時候 pic -= 1;,pic指標從0x70fdf4變成0x70fdf0,相差四個位元組,正好就是整數變數所佔記憶體的空間大小,這也恰巧地指向變數b的位置,變成重疊變數b的指標了,也就取到變數b的值了。這個例子並不是要表達指標可以用加減法運算來取得另一個變數值而是要表達在指標的使用上必須小心謹慎,避免誤取他值而不自知。


回頭來看,如果我們真的想用指標pic來取變數b的值,我們可以
1. pic = &b;  // 直接把變數b的位址設定給指標pic

2. pic = pib; // 把指標pib 設定給pic;
然後依址取值...*pic;
這樣才是正確的指標操作....

 

 

 

 

.

arrow
arrow
    全站熱搜

    伊蒙‧普羅客 發表在 痞客邦 留言(0) 人氣()