繁体   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