2019年7月2日 星期二

C 動態陣列 範例與解說

C 動態陣列 範例與解說

其實是看到有人在社團問問題,順道寫了程式,不過當我弄完的時候才發現早就關閉回應了QQ
好吧自己發部落格XD

動態陣列

顧名思義,意思是說當你需要用到多大的陣列才向系統要求多大的記憶體,一般如果直接寫在宣告上的陣列,是程序一打開的瞬間就向系統要求記憶體,這種一開始就要求的記憶體是有限的不能塞太大的。
其次使用動態陣列也可以節省空間,如果初期並不知道空間大小,而是計算後才能知道總共要多大,直接寫在宣告上,只能多寫一點避免到時候不夠。動態陣列就沒關係了,程序執行後計算出來再要求記憶體即可。

宣告方法

介紹最基本的兩個函式
int* p=NULL;
int len=5;

// 要求空間
p = (int*)calloc(len, sizeof(int));     // 陣列內容會初始化為0
p = (int*)malloc(len* sizeof(int));   // 陣列內容不會初始化,可能是亂碼垃圾值
兩個簡單就差別是否初始化,根據使用情況調整使用就好,其中要注意的是 malloc 只有一個參數,calloc則是有兩個參數,所以可以看到其中一個是逗號跟乘號(這不是打錯)。

檢測

獲取之後就可以使用了,但是再使用前要先檢查是否有成功要到記憶體,電腦的記憶體是有限的,如果你要求一個超大的,大於當前記憶體容量,系統不會給你的,這種情況下函式會返回NULL,所以類似於底下的方法就可以檢測是否成功。
if(p){
    printf("成功");
} else {
    printf("失敗");
}
當然啦現在電腦記憶體都很大,寫作業基本上應該是不會遇到NULL的情況,但是即便如此,我還是建議你養成習慣寫一下,以後寫大專案的時候你會感謝你自己的。
另外記憶體的不足不只發生在記憶體空間不構的情況,如果程序長時間運行,不斷的解構重購,會造成記憶體破碎,就是說一段有使用,一段沒使用,記憶體中間是有間格的,上述的兩種要求方法,要求的情況是連續記憶體,在這種記憶體破碎嚴重的情況下,即便記憶體充足也可能要不到,系統找不到足夠長的連續空間。

使用

使用就很簡單了,要求到的記憶體要用第幾個,直接加上去就好,編譯器會自動幫你計算位置的,最後再使用星號取值即可。
for (int i; i<len; ++i){
    prinf("%d", *(p+i));
}

解構

用完之後要記得把記憶體回收,告訴系統這個我不用了,這樣下次你要求空間的時候,原本這一段也可以被列入使用,不回收的話,這個空間就一直佔著誰也不能用。
如果你再還沒回收之前就把那個記憶體位址給丟失了,也就是那個P你改成別的值,那就再也沒有人知道,那一段記憶體在哪裡,你就沒有辦法釋放那塊記憶體了,這種情況叫做 memroy leak。
倒是不用擔心練習程式忘記釋放記憶體,結果記憶體被偷吃滿,以目前的作業系統來說是會自動幫你把記憶體回收掉的,不會讓你做這種傻事,作業系統知道這隻程式從頭到尾到底要了那些記憶體,既然都把程式關了,當然全部都回收瞜。
解構的方法很簡單,只是一個函式
free(self->p);
self->p=NULL;
free即可解構,後面的設置成NULL是一種保險,也是一種聲明,既然都解構掉了代表這塊記憶體不能用了,還留著指標幹嘛,不知道知情的人傻傻的拿去用了就罪過了(這個叫未定義行為)。當設置成NULL,別人就用不了了,同時也可以用if來判斷已解構。
不要想說反正記憶體充足就不解構,這個習慣真的很重要,以後寫大專案的時候沒有養成習慣,真的會抓蟲抓的不要不要的。不解構甚至過分點leak,不只影響到記憶體用量,有時候還可能導致非預期的行為,導致你抓蟲的時候沒得下手,怎麼想都奇怪,這時候就來不及了,只能加油吧,祝好運XDD。
抓蟲第一件事情,先把釋放還有leak做好,真的是不是開玩笑的,沒有嚴謹的照規矩寫的程式,很多時候補上就莫名其妙好了,然後完全不知道發生什麼了,乾怎麼好了!

範例

最後附上簡單寫的範例,可以對照著參考
/*****************************************************************************
Name : 動態陣列
Date : 2019/07/02
By   : CharlotteHonG
Final: 2019/07/02
*****************************************************************************/
#include <iostream>
using namespace std;

struct Arr {
    int* p;
    int len;
};

void Arr_ctor(Arr* self, int len) {
    int*p = NULL;
    p = (int*)calloc(len, sizeof(int));

    if(p){
        self->p   = p;
        self->len = len;
    } else {
        printf("Error Arr_ctor.");
    }
}
void Arr_set(Arr* self, int idx, int num) {
    int* p = self->p;
    int len = self->len;

    if(idx>=0 && idx < len){
        *(p+idx) = num;
    } else {
        printf("Error Arr_set");
    }
}
void Arr_dtor(Arr* self) {
    free(self->p);
    self->p=NULL;
    self->len=0;
}
void Arr_print(Arr* self) {
    for(size_t i = 0; i < self->len; i++){
        int* p = self->p;
        printf("%d, ", *(p+i));
    } printf("\n");
}
//=============================================================================
int main(int argc, char const* argv[]) {
    // 陣列長度
    int len = 10;

    // 建立陣列 (初始值為0)
    Arr array;
    Arr_ctor(&array, len);
    // 打印
    Arr_print(&array);
    // 更改數值
    for(size_t i = 0; i < len; i++){
        Arr_set(&array, i, i+1);
    }
    // 打印
    Arr_print(&array);
    // 釋放資源
    Arr_dtor(&array);

    return 0;
}
//=============================================================================

沒有留言:

張貼留言