2008-12-04 50 views
4

我試圖創建一個函數,它將一個數組作爲參數,向它添加值(如果需要,增加它的大小)並返回項的數量。 到目前爲止,我有:將動態數組傳遞給函數C

int main(int argc, char** argv) { 
    int mSize = 10; 
    ent a[mSize]; 
    int n; 
    n = addValues(a,mSize); 

    for(i=0;i<n;i++) { 
     //Print values from a 
    } 
} 

int addValues(ent *a, int mSize) { 
    int size = mSize; 

    i = 0; 

    while(....) { //Loop to add items to array 
     if(i>=size-1) { 
      size = size*2; 
      a = realloc(a, (size)*sizeof(ent)); 
     } 
     //Add to array 
     i++; 
    } 
    return i; 
} 

這個工作,如果MSIZE大到足以容納陣列的所有潛在因素,但如果需要調整,我得到一個分段錯誤。

我也曾嘗試:

int main(int argc, char** argv) { 
    ... 
    ent *a; 
    ... 
} 

int addValues(ent *a, int mSize) { 
    ... 
    a = calloc(1, sizeof(ent); 
    //usual loop 
    ... 
} 

無濟於事。

我假設這是因爲當我調用realloc時,'a'的副本被指向其他地方 - 如何修改它以便'a'總是指向相同的位置?

我是否正確地處理這個問題?有更好的方法來處理C中的動態結構嗎?我應該實施一個鏈表來處理這些嗎?

回答

10

這裏的主要問題是,你試圖使用realloc與堆棧分配數組。您有:

ent a[mSize]; 

這是堆棧上的自動分配。如果你想以後使用這個realloc的(),您將創建在使用堆的malloc(陣列),像這樣:

ent *a = (ent*)malloc(mSize * sizeof(ent)); 

從而使圖書館的malloc(因此realloc的()等)知道你的陣列。從外觀上看,你可能會混淆C99 variable-length arrays與真實dynamic arrays,所以請務必在嘗試解決此問題之前瞭解其中的差異。但是,真的,如果你正在用C編寫動態數組,你應該嘗試使用OOP-ish設計來封裝有關你的數組的信息並將它從用戶身上隱藏起來。您希望將關於數組的信息(例如指針和大小)合併到結構和操作中(例如,分配,添加元素,移除元素,釋放等)到與​​您的結構一起工作的特殊函數中。所以,你可能有:

typedef struct dynarray { 
    elt *data; 
    int size; 
} dynarray; 

,你可能會定義一些功能與dynarrays工作:

// malloc a dynarray and its data and returns a pointer to the dynarray  
dynarray *dynarray_create();  

// add an element to dynarray and adjust its size if necessary 
void dynarray_add_elt(dynarray *arr, elt value); 

// return a particular element in the dynarray 
elt dynarray_get_elt(dynarray *arr, int index); 

// free the dynarray and its data. 
void dynarray_free(dynarray *arr); 

這樣,用戶不必記得到底如何分配的事或什麼大小的數組目前。希望能讓你開始。

+0

感謝您的支持!非常有用的信息 - 我將會看你是否可以按照Javier的建議重做我的代碼。 如果我有多個不同類型的動態數組(如'ents'數組,'foos'數組等),是否可以創建一組方法來處理它們? – Tom 2008-12-04 17:11:41

+0

你可以用C++中的模板做到這一點,但是在C中沒有很好的方式來處理它。你可以通過一組精心設計的宏來實現,但是這並不適用於最漂亮或最易維護的代碼。 – tgamblin 2008-12-04 17:15:22

6

嘗試對其進行重構,以便傳入指向數組的指針,即ent **a。然後您將能夠更新陣列新位置上的調用者。

+0

您需要將其與tgamblin的答案相結合才能獲得完整的解決方案。 – 2008-12-04 17:16:40

1

您正在按值傳遞數組指針。這意味着:

int main(int argc, char** argv) { 
    ... 
    ent *a; // This... 
    ... 
} 

int addValues(ent *a, int mSize) { 
    ... 
    a = calloc(1, sizeof(ent); // ...is not the same as this 
    //usual loop 
    ... 
} 

因此改變在addValues函數的值在主不會改變的價值。要更改主內容的值,您需要將其引用傳遞給addValues。此刻,a的值被複制並傳遞給addValues。傳遞一個參照本發明的使用:

int addValues (int **a, int mSize) 

並調用它喜歡:

int main(int argc, char** argv) { 
    ... 
    ent *a; // This... 
    ... 
    addValues (&a, mSize); 
} 

addValues,訪問的一個元素是這樣的:

(*a)[element] 

和重新分配陣列像這樣:

(*a) = calloc (...); 
1

這是使用OOP的一個很好的理由。是的,你可以在C上做OOP,如果做得對,它甚至看起來不錯。

在你不需要繼承或多態性,只是封裝和方法的概念這個簡單的例子

  • 定義與長度和數據指針的結構。也許是一個元素大小。
  • 寫getter/setter函數對指向該結構的指針進行操作。
  • 'grow'函數修改結構中的數據指針,但任何結構指針都保持有效。
0

Xahtep解釋了您的調用者如何處理realloc()可能會將該數組移動到新位置的事實。只要你這樣做,你應該沒問題。

如果您開始使用大型數組,則realloc()可能會變得昂貴。那是時候開始考慮使用其他數據結構 - 鏈接列表,二叉樹等。

0

如前所述,您應該傳遞指針指針來更新指針值。
但我會建議重新設計,並避免這種技術,在大多數情況下,它可以也應該避免。不知道你到底想要達到什麼目的,很難建議其他設計,但我99%確定它可以通過其他方式實現。和Javier悲傷 - 認爲面向對象,你將永遠得到更好的代碼。

1

如果您在主要改變的變量聲明爲

ent *a = NULL; 

像你通過不釋放堆棧分配的數組所設想的代碼會工作了。設置爲NULL是有效的,因爲realloc將此視爲用戶稱爲malloc(size)。請記住,這種變化,這些原型的addValue需要改變到

int addValues(ent **a, int mSize) 

,並且代碼需要處理的realloc失敗的情況。例如

while(....) { //Loop to add items to array 
    tmp = realloc(*a, size*sizeof(ent)); 
    if (tmp) { 
     *a = tmp; 
    } else { 
     // allocation failed. either free *a or keep *a and 
     // return an error 
    } 
    //Add to array 
    i++; 
} 

我預計,如果當前緩衝區需要調整大小使得原有代碼的

size = size * 2; 

不必要的realloc的大多數實現內部分配兩倍的內存。

0

你真的需要使用C嗎?這將是C++的「std :: vector」的一個很好的應用,它正好是一個動態調整大小的數組(可以輕鬆調整大小,只需調用一次,不必編寫和調試自己)。