徹底粉碎“指標與數組的困惑”(上)

來源:互聯網
上載者:User

   摘要:指標是C語言的精髓,也算是C++的精髓吧,要掌握它不是很容易,特別是與數組經常弄混,希望通過本文,你能夠有一個清楚的認識。
   (1)一維數組與指標
    首先我們看一下下面的例子:
  char a[10], *p;
  p=a;
    p是一個char類型的指標,它裡面放的是數組a的首地址,即a[0];a這裡有雙重含義:地址,也可以看作是常量指標;結構,裡麵包含了10個char元素。
    在這種情況下,指標可以完成數組的操作,例如,對於數組的訪問,可以用指標進行,也可以想數組一樣對數組單元賦值和取址。但是數組不能看做普通指標,應該理解成一個固定的地址,她是在編譯期間確定的,也可以理解成常量指標。
    但是,由於數組名的雙重含義,決定了他自己的特性,不信請看下面的程式:
 #include <iostream>
 #include <stdlib.h>
 using namespace std;

 int main(int argc, char *argv[])
 {
    int a[10]={1,2,3,4,5,6,7,8,9,10}, *p;
    p=a;

    cout<<a<<endl;
    cout<<&a<<endl;
    cout<<&a[0]<<endl;

    cout<<p<<endl;
    cout<<&p<<endl;
    cout<<&p[0]<<endl;

            system("PAUSE"); 
            return 0;
 }
    我們看到a, &a , &a[0]輸出的結果都是一致的,他們都是數組的首地址,這裡a體現的是指標的特性,&a則體現了結構的特性。但是對於p來講就不一樣了,因為指標p在開始的時候開闢了一塊自己的記憶體空間,經過p=a後,p裡面放的是數組a的首地址,所以p和&p[0]結果都是數組a的首地址,但是&p則是指標p本身的記憶體位址。
    那麼一維數組究竟是什麼含義呢?
    實際上,一維數組應該理解成一個線性表,他在記憶體中開闢了一塊連續的記憶體空間。在對數組訪問的時候,實際上,編譯器做了一定的轉換工作:數組名(數組的起始地址)加上相對於起始地址的相對量(由下標變數給出),得到要訪問的數組元素的單元地址,然後再對計算出的單元地址的內容進行訪問。例如a[3]被轉換成*(a+3)。
    由於數組是在編譯期間確定,代表的是一塊固定的記憶體空間,所以數組是不能夠改變的,即a++,++a,a--,--a,a+=3,a-=3等都是不允許的,但是指標p做這些動作是毫無問題的。
    在記憶體中,指標的分配只是佔用了4個位元組的空間(不要考慮16位等其他情況,畢竟最常見的是32位的,嗯,好吧,我們隨後就談談這方面的問題),但是對於數組就不同了,它除了要分配自己的記憶體單元,還要儲存一些資訊去儲存它的元素的個數。從理論上來講,編譯器可以採用任何技術,但是在實際的編譯器上,一般採用2種比較流行的手法:關聯手法、Cookie策略。
    關聯手法,就是分配一個static數組,有編譯器去控制,裡面放上一個map,關鍵字可以採用數組名+有效區間,值則可以是元素個數,釋放數組記憶體空間的時候,就去搜尋相應的map,看起來這是一個比較安全的策略,但是也比較的慢。
    Cookie策略,也稱過渡分配技術,沒進行一次數組空間分配的時候,就在數組前面的某個位置加上一個sizeof(size_t)位元組的空間,來存放數組的元素個數。這個策略毫無疑問很快,但是不夠安全,有的時候,我們可以取到Cookie的值,並進行修改,這樣一來就可以破壞堆棧空間。
    不過,這裡有一個例外,在數組被當作參數傳遞的時候,數組會被自動將為普通指標,這樣做的好處也很明顯:提高效率。你可以想象,如果不當作指標傳遞,那麼元素的傳遞將是一筆大的開銷。
    指標還有一個很要命的問題,那就是指標的對齊問題。
    當你在80x86處理器上執行你的程式時,這個問題不是致命的,但對其他的絕大多數晶片來說,這就是致命的了。它還會對你的應用程式移植到某個其他環境的能力產生影響。此外,甚至對於Intel 處理器來說,這個問題也許不是致命的,但是她會佔用CPU更多的時間進行轉換,毫無疑問的導致效能的下降。
    當你的指標從一種類型轉換到另一種類型的時候,就有可能產生一個非對準指標(misaligned pointer)。處理器一般要求記憶體塊的地址要與一個和這個記憶體塊的尺寸匹配的邊界對齊。例如,字只能在字邊界上被訪問(地址是二的倍數),雙字只能在雙字邊界上被訪問(地址是四的倍數),依次類推。
    編譯器通常確保監視這個規則。但是當你的指標類型從一種類型轉換成較大類型時,你就可以很容易地違反這個規則:
      char cA;
      char* pC = &cA;
      int* pI;
      pI = (int*)pC;
      *pI = 0;
    因為字元僅僅是一個位元組長,所以地址&cA可能有任意值,包括奇數值。可是,pI應只包含四的倍數的地址。通過轉換,允許把pC賦給pI,但是如果地址不是四的倍數,則接著發生的賦值可能使程式崩潰。
   不過,這種情況只在你正在把你的指標從指向一種類型轉換成指向較大類型時才會出現。    

    未完(待續...)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.