簡體   English   中英

C:在結構中定義一個動態數組。 動態數組將保存不同結構的實例

[英]C: Define a dynamic array inside a struct. The dynamic array will hold instances of a different struct

我正在為學校做一個自動推文生成項目。

我有 2 個結構

  1. 單詞概率
  2. WordStruct - 必須包含 WordProb 對象的動態數組。

結構的外觀[1]: https://i.stack.imgur.com/LGNQg.png

動態數組應初始化為 1 WordProb object 的大小。對於每個新的 WordProb object,我必須按 1 WordProb 的大小重新分配()動態數組並將其插入。

我試着用

WordStruct * ws1 = (WordStruct *)malloc(sizeof(*ws1) + sizeof(WrodProbability));
ws1->dynamic_arr_of_WordProb_objects[0].occurances = 4;
ws1 = (WordStruct *)realloc(ws1, 2 * sizeof(WrodProbability));
ws1->dynamic_arr_of_WordProb_objects[1].occurances = 4;

我有 3 個問題:

  1. 我怎樣才能正確分配 dynamic_arr_of_WordProb_objects[](見上圖)?
  2. 我怎樣才能重新分配它?
  3. 如何使用新的 WordProb object“分配”它?

為什么我不認為我的方法是正確的:

  1. 我做了

ws1->dynamic_arr_of_WordProb_objects[55].occurances = 44;

printf("Sizeof ws1: %d", sizeof(*ws1->next_words));

這打印了 28.... 如果它包含一個數組,而這個數組包含每個至少 24 字節大的其他結構,那么它怎么可能是 sizeof(28) 呢? 如果我從未創建過索引 55,我該如何訪問它?

我是 C 的新手,所以每條指導都會有所幫助。 謝謝!


在 GOAT @jsiller 回答之后,我想我可能已經明白了。 這是在結構中定義動態數組並稍后重新分配的正確形式嗎?

typedef struct{
    int occurances;
}WrodProbability; 

typedef struct {
    WrodProbability next_words[];
}WordStruct;

int main()
{
    WordStruct * ws1 = malloc(sizeof(*ws1) + sizeof(WrodProbability));
    ws1->next_words[0].occurances = 1;
    ws1 = realloc(ws1, sizeof(*ws1) + 2 * sizeof(WrodProbability))
}

最后一個問題關於

ws1->next_words[55].occurances = 44;

問題。 當我釋放(ws1->next_words)時,它會釋放所有 memory 從 ws1->next_words[0] 到 [55]? 或者只有我用 realloc 指定的前 X 數量的索引?

謝謝


免費(sw1)不能正常工作

    WordStruct * ws1 = malloc(sizeof(*ws1) + sizeof(WrodProbability));
    ws1->next_words[0].occurances = 1;
    ws1 = realloc(ws1, sizeof(*ws1) + 2 * sizeof(WrodProbability));
    ws1->next_words[1].occurances = 2;
    printf("%d %p\n", ws1->next_words[0].occurances, ws1->next_words); // prints - 1 00000250023914a8
    free(ws1);
    printf("%d %p", ws1->next_words[0].occurances, ws1->next_words); // prints - 1 00000250023914a8

如您所見,在我釋放 (ws1) 后仍然存在 memory 泄漏。 如何正確釋放動態數組?

您正在使用的是(或曾經)稱為struct hack

我將用一個不同的例子來解釋這一點:

struct foo {
   int size;
   int arr[];
}

結構的 arr 屬性是一個大小為 0 的數組。結構的大小現在為 4,因為:(sizeof(size) = 4 + sizeof(arr) = 0) = 0。

要使用 arr,您現在可以分配 foo 結構的大小加上錯誤類型的大小乘以您想要擁有的元素數量。 讓我們將這行代碼轉換為示例:

WordStruct * ws1 = (WordStruct *)malloc(sizeof(*ws1) + sizeof(WrodProbability));
struct foo *f = (struct foo *)malloc(sizeof(*f) + sizeof(int));

這相當於:

struct foo *f = malloc(sizeof(*f) + 1 * sizeof(int));

在這種情況下,我們將分配一個 struct foo 並分配一個 size = 1 的數組。要分配例如 50 個元素,您可以將其調整為:

struct foo *f = malloc(sizeof(*f) + 50 * sizeof(int));

關於你對 realloc 的問題,realloc 會采用你想要調整大小的完整大小,而不僅僅是你想要添加的大小。 因此,如果我們想向具有 50 個元素的 foo 結構添加 25 個以上的元素,我們需要像這樣重新分配它:

f = realloc(f, sizeof(*f) + 75 * sizeof(int));
//75 because 50 + 25 = 75

要分配數組中的第一個元素,您可以這樣做:

f->arr[0] = 1;

您的代碼是正確的,但會分配第二個元素:

ws1->dynamic_arr_of_WordProb_objects[1].occurances = 4;

為什么可以訪問數組外的元素? C 不保護您訪問未分配的 memory,這是未定義的行為,可能導致您的程序崩潰。 這就是為什么您應該始終存儲數組的大小並在嘗試訪問數組之前檢查它。

最后為什么這條線打印出“錯誤”的尺寸

printf("Sizeof ws1: %d", sizeof(*ws1->next_words));

這可能是由於您對 realloc 的錯誤使用造成的,但我不能肯定地說,因為您沒有共享結構的代碼,並且在您共享的圖像中我找不到任何名為“next_words”的屬性。

大多數人所說的結構中的“動態數組”是指向動態分配數組的指針,如下所示:

struct WordStruct {
    size_t numWords;
    struct WordProbability *words;
};

另一方面,您似乎正在使用的以及您的其他答案所討論的是一個 struct with a flexible array member 后者比前者有一些優點,但也有一些缺點。 正確使用這些是棘手的,在某種程度上我認為它們是專家功能。 此外,靈活的數組成員被更好地描述為“可變”而不是“動態”,因為盡管它們的大小可能因結構的一個實例而異,但任何特定成員的大小在其生命周期內都不會改變。 *

因此,當你說,

動態數組應初始化為 1 WordProb object 的大小。對於每個新的 WordProb object,我必須按 1 WordProb 的大小重新分配()動態數組並將其插入。

,它表明您需要一個類似於上面的形式的形式,而不是具有靈活數組成員的結構。 從這個角度來看,

  1. 我怎樣才能正確分配 dynamic_arr_of_WordProb_objects[](見上圖)?

作為結構成員的指針沒有什么特別之處。 給定一個已經存在的struct WordStruct (並且它本身不需要為此目的動態分配),您可以使用malloc() 例如,

struct WordStruct ws = { 1, NULL };
ws.words = malloc(1 * sizeof(*ws.words));
// TODO: fail if ws.words == NULL
  1. 我怎樣才能重新分配它?

同樣,作為結構成員的指針沒有什么特別之處。 您以通常的方式使用realloc()

ws.numWords += 1;
struct WordProbability *newWords = realloc(ws.words, ws.numWords * sizeof(*ws.words));
// TODO: fail if newWords == NULL
ws.words = newWords;
  1. 如何使用新的 WordProb object“分配”它?

假設分配/重新分配成功,數組中有一個可行的結構 object,但它的值是不確定的。 您可以簡單地分配給 object 的成員(因此不需要任何單獨的結構)...

ws.words[ws.numWords - 1].occurrences = 42;

...或者如果你想復制整個結構的值,那么你這樣做,仍然使用賦值運算符( = ):

ws.words[ws.numWords - 1] = anotherWordProbStructure;

你go就說

我做了

ws1->dynamic_arr_of_WordProb_objects[55].occurances = 44; printf("Sizeof ws1: %d", sizeof(*ws1->next_words));

這打印了 28.... 如果它包含一個數組,而這個數組包含每個至少 24 字節大的其他結構,那么它怎么可能是 sizeof(28) 呢?

因為你的sizeof表達式並不代表你認為的意思。 ws1->next_words具有數組類型,因此當它作為操作數出現在大多數表達式中時,包括*ws1->next_words ,它會自動轉換為指向第一個元素的指針。 這有時被稱為“衰減”。 當您取消引用結果指針時,您會得到一個 object 類型的struct WordProbability sizeof運算符實際上並不計算表達式,但它確實返回該表達式類型的大小。 也就是說,您的sizeof(*ws1->next_words) 100% 等同於sizeof(WordProbability) 無法使用sizeof來確定 FAM 的大小。 你需要明確地跟蹤它。

如果我從未創建過索引 55,我該如何訪問它?

從您提供的內容中不清楚數組是否足夠長以在索引 55 處包含一個元素。如果是,那么這個問題就沒有意義了。 如果不是,則行為未定義。 “未定義”意味着它在錫罐上所說的:你不能依賴任何特定的行為,特別是,假設程序會崩潰甚至明顯的行為不端是不安全的。 當然,假設程序會按預期運行,或者甚至從一次運行到另一次運行都以相同的方式運行也是不安全的。

當我釋放(ws1->next_words)時,它會釋放所有 memory 從 ws1->next_words[0] 到 [55]? 或者只有我用 realloc 指定的前 X 數量的索引?

首先請注意,如果ws1->next_words是一個靈活的數組成員,那么您不能首先釋放(只是)它。 您只能釋放您之前分配的特定的、完整的對象,並且之后還沒有釋放。 您不能單獨為 FAM 分配 memory,因此您也不能單獨釋放它。

但是,您當然可以釋放傳統的動態數組。 在這種情況下,整個分配的 object 被釋放。 這與訪問了哪些元素無關,僅與分配塊的大小有關,由malloc / realloc根據請求的大小選擇。


最后,您詢問了一個(顯然)FAM 用法的示例:

免費(sw1)不能正常工作

 WordStruct * ws1 = malloc(sizeof(*ws1) + sizeof(WrodProbability)); ws1->next_words[0].occurances = 1; ws1 = realloc(ws1, sizeof(*ws1) + 2 * sizeof(WrodProbability)); ws1->next_words[1].occurances = 2; printf("%d %p\n", ws1->next_words[0].occurances, ws1->next_words); // prints - 1 00000250023914a8 free(ws1); printf("%d %p", ws1->next_words[0].occurances, ws1->next_words); // prints - 1 00000250023914a8

如您所見,在我釋放 (ws1) 后仍然存在 memory 泄漏。 如何正確釋放動態數組?

我沒有看到 memory 泄漏的證據,只有在其生命周期結束后嘗試訪問 object 時出現的未定義行為。 該示例中第二個printf()的 output 未定義,因此沒有意義。 它不會告訴您有關ws1最初指向的 object 是否已成功釋放的任何信息。

free()沒有可測試的故障模式。 如果程序傳遞的指針不滿足free()的要求,則會導致未定義的行為。 如果程序傳遞的指針確實滿足free()的要求,則它必須假定成功。


*有人可能會說 object,說“你可以realloc()結構來改變 FAM 的大小”,但最好將重新分配視為用另一個 object 替換一個。 雖然它將數據從原來的 object 復制到新的,但是新的經常有不同的地址,特別是當它比舊的大時,一個對象的地址是其身份的最基本方面。 這具有實際影響。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM