變長數組 - 轉

來源:互聯網
上載者:User

標籤:with   cap   print   開啟   ext   function   lan   好的   函數實現   

http://ericwang.github.io/program/2010/02/10/c_Variable_length_arrays/

 

C中的Variable length arrays (變長數組)

Variable length arrays 是C99的特性,而不是 C++98 的,關於c99標準的變長數組, 在標準的6.7.5.2 Array declarators裡面有這樣的說明:
2.Only ordinary identifiers (as defined in 6.2.3) with both block scope or function prototype scope and no linkage shall have a variably modified type. If an identifier is declared to be an object with static storage duration, it shall not have a variable length array type.
換而言之, 變長數組不能是在靜態儲存區(包括全域變數和靜態變數)中的。
另外,VLA 需要支援 sizeof 運算, 動態sizeof 也是C99的一個特有特性。
目前很多C++編譯器尚不能支援動態數組特性(VC++2005不支援此特性, GCC3.2之後支援,之前的版本沒有調查,不知道是從哪個版本開始支援此特性的)。gcc的文檔裡面說:Variable-length automatic arrays are allowed in ISO C99, and as an extension GCC accepts them in C89 mode and in C++。所以,使用GCC,即使你開啟 -ansi (等價於 —std=c89)選項,也仍然可以使用動態數組。
其實C99的VLA也不是真正意義上的變長數組—它只是在運行時可以根據一個變數產生一個數組,此數組此後並不會變化,而真正意義的變長數組可以在實際使用時可以動態伸縮。
過去的C 和現在的C++中,自動變數的位移都是預定的,但是VLA不能這樣實現。
下面的例子中,由於b, c大小不確定,所以它們的地址必須是運行時確定的

void test(int tmp){int a;scanf("%d", &a);int b[tmp];int c[a];printf ("size(b) = %d, size(c) = %d\n", sizeof(b)/sizeof(int), sizeof(c)/sizeof(int));}

VLA在棧上分配空間,這個速度很快。 我們舉一個典型的32位編譯器下函數實現為例子。這個實現中,下面兩個寄存器關注函數堆棧:

ebp: 函數佔有內容起點即幀(frame)的位置, esp:當前棧頂

一般來說,一個函數中自動變數個數和大小都是確定的。比如共有4個int,一個大小為10的 float 數組。 編譯器可以簡單的 esp -= 56 就留出所有空間(以32位PC機為例)。 每個變數相對 ebp 的位移也是確定的,所以,若他訪問 int a,

$a = 4, 他會這樣訪問: $a [ ebp ]

對於 VLA 來說,事情複雜一些。 現在有一個 VLA c。 他的大小是不確定的 —— 所以它的位移也是不確定的:如果它之前有另外一個 VLA b, 它不可能知道自己排到哪裡。目前 GCC 中的做法是,在可變數組較少的情況下,直接用寄存器+位移的方式來存取;在較多的情況下,都是先從堆棧中得到數組的起始地址, 然後再用位移量的方式存取數組的元素。

這個操作是非常快的。不過有一個問題是,棧的大小是固定的,而且不大。你不能分配很大的東西。另外這個數組是臨時的 —— 一旦退出當前範圍,他就可以被任何其他函數刷掉。

變長數組的特性綜述(摘自2):

1. 變長數組是分配在堆棧上的, 其實從語義的角度也應該是這樣, 變長數組還是一個數組, 還是一個局部變數, 在c語言中, 局部變數是分配在堆棧上的, malloc才是分配在堆上面的. 這裡面沒有不存在什麼記憶體流失, 因為堆棧上的記憶體是不需要程式員管理的

2. 變長數組和其他變數共同存在於一個範圍之內時, 變長數組是在最後分配的, 也就是處在堆棧的最下面(地址最低). 這也是變長數組不同於普通變數的情況, 普通變數的記憶體配置是按照定義順序來的, 先定義的先分配, 但是變長數組不是這樣的, 在一個範圍內變長數組都是放到最後分配的, 原因很簡單, 變長數組的大小在編譯時間無 法確定, 所以如果在變長數組後面分配普通變數的空間, 那麼對後面普通變數的存取就不方便了,當然簡單的情況可以通過最佳化解決, 但是如果有很多的變數數組和普通變數混合在一起的話,最佳化很難做到很好, 所以把變長數組都放在最後面分配, 是一個比較合理的辦法;
當然, 不是說一定要放在最後, 按照普通的定義順序來分配空間也是可以的,只是在存取可變數組後面的變數的時候, 需要首先找到可變數組的起始地址, 然後再往下(低地址) 找到後面變數的地址, 存取相應的變數。目前gcc所使用的放到最後的做法, 也可以理解成一種最佳化. 。

3. 對變長數組的元素存取, 可以把首地址放在寄存器裡面, 也可以在把首地址放在堆棧裡面,然後存取元素的時候先取出首地址, 然後在通過位移量的方式存取裡面的元素.

4.變長數組不能在靜態儲存區中, 這個很顯然, 靜態儲存區在編譯時間就確定分配記憶體的大小,不像堆棧一樣是動態, 變長數組顯然不能定義在靜態儲存區中.

5. 變長數組的意義, 我覺得在c裡面還是有一些意義, 如果是在對效能,記憶體要求十分嚴格的地方, 允許聲明變長數組還是比較方便的, 因為原來需要一塊連續記憶體的地方要麼聲明的大一些,保證肯定夠用, 但這樣就浪費了, 在對記憶體要求嚴格的地方就不好了, 另外一種做法就是malloc動態分配, 但是這樣的缺點是需要手動管理, 要手動free, 程式如果大的話記憶體管理不好的話容易記憶體流失,所以在c裡面還是有一些意義. 但是在c++裡面已經有vector了, 而且c++也不太多應用在對效能,記憶體要求非常高的地方,

6.使用變長數組, 最重要的當然是要檢查數組的size的合法性了, 我前面舉例為了減少幹擾因素, 都沒有做檢查, 實際的程式肯定要做這個檢查, 則面試的時候這樣的程式肯定立刻被kick. 另外, 就是又多了一種堆疊溢位的方法,數組大小如果是負的話又可以跳到不該跳到的地方了

 

變長數組 - 轉

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.