简体   繁体   English

C:在结构中定义一个动态数组。 动态数组将保存不同结构的实例

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

Im working on a auto tweet generation project for school.我正在为学校做一个自动推文生成项目。

I have the 2 structs我有 2 个结构

  1. WordProb单词概率
  2. WordStruct - which must contain a dynamic array of WordProb objects. WordStruct - 必须包含 WordProb 对象的动态数组。

How the structs look [1]: https://i.stack.imgur.com/LGNQg.png结构的外观[1]: https://i.stack.imgur.com/LGNQg.png

The dynamic array should be initialized with size of 1 WordProb object. For each new WordProb object i must realloc() the dynamic array by size of 1 WordProb and insert it.动态数组应初始化为 1 WordProb object 的大小。对于每个新的 WordProb object,我必须按 1 WordProb 的大小重新分配()动态数组并将其插入。

I tried to use我试着用

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;

I have 3 questions:我有 3 个问题:

  1. How can i allocate the dynamic_arr_of_WordProb_objects[] (see picture above) correctlly?我怎样才能正确分配 dynamic_arr_of_WordProb_objects[](见上图)?
  2. How can I realloc it?我怎样才能重新分配它?
  3. How do I "assign" it with the new WordProb object?如何使用新的 WordProb object“分配”它?

Why don't i think my method is correct:为什么我不认为我的方法是正确的:

  1. I did我做了

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

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

This printed 28.... How can this be sizeof(28) if it holds an array that holds other structures that are at least 24 byte big each?这打印了 28.... 如果它包含一个数组,而这个数组包含每个至少 24 字节大的其他结构,那么它怎么可能是 sizeof(28) 呢? and how can i access index 55 if i never created it?如果我从未创建过索引 55,我该如何访问它?

I'm very new to C, so every guidance will be helpful.我是 C 的新手,所以每条指导都会有所帮助。 Thanks!谢谢!


After the GOAT @jsiller answer, I think i might have got it.在 GOAT @jsiller 回答之后,我想我可能已经明白了。 Is this the correct form to define a dynamic array in a struct, and realloc it later?这是在结构中定义动态数组并稍后重新分配的正确形式吗?

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))
}

Last question regarding the最后一个问题关于

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

issue.问题。 When i free(ws1->next_words) it will free all the memory from ws1->next_words[0] to [55]?当我释放(ws1->next_words)时,它会释放所有 memory 从 ws1->next_words[0] 到 [55]? Or only the first X amount of indexes i specified with realloc?或者只有我用 realloc 指定的前 X 数量的索引?

Thanks谢谢


Free(sw1) not working correct免费(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

As you see there is still memory leak after i free(ws1).如您所见,在我释放 (ws1) 后仍然存在 memory 泄漏。 How can i free the Dynamic array correctly?如何正确释放动态数组?

What you are using is (or used to be) called the struct hack .您正在使用的是(或曾经)称为struct hack

I will use a different example to explain this:我将用一个不同的例子来解释这一点:

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

The arr attribute of the struct is an array of size 0. The size of the struct now would be 4 because: (sizeof(size) = 4 + sizeof(arr) = 0) = 0.结构的 arr 属性是一个大小为 0 的数组。结构的大小现在为 4,因为:(sizeof(size) = 4 + sizeof(arr) = 0) = 0。

To use arr you can now allocated the size of the foo struct plus the size of the err-type times the amount of elements you want to have.要使用 arr,您现在可以分配 foo 结构的大小加上错误类型的大小乘以您想要拥有的元素数量。 Lets convert this line of your code to the example:让我们将这行代码转换为示例:

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

which is equivalent to:这相当于:

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

In this case we would have a struct foo allocated with an array of the size = 1. To allocate eg 50 elements you would adjust it to this:在这种情况下,我们将分配一个 struct foo 并分配一个 size = 1 的数组。要分配例如 50 个元素,您可以将其调整为:

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

Regarding you question to realloc, realloc takes the full size you want to size it to, not just the size you want to add.关于你对 realloc 的问题,realloc 会采用你想要调整大小的完整大小,而不仅仅是你想要添加的大小。 So if we want to add 25 more elements to the foo struct with 50 elements we would need to reallocate it like this:因此,如果我们想向具有 50 个元素的 foo 结构添加 25 个以上的元素,我们需要像这样重新分配它:

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

to assign the first element in the array you can do it like this:要分配数组中的第一个元素,您可以这样做:

f->arr[0] = 1;

your code is correct but would assign the second element:您的代码是正确的,但会分配第二个元素:

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

why can you access an element outside of the array?为什么可以访问数组外的元素? C does not protect you from accessing not allocated memory, it is undefined behavior and can cause your program to crash. C 不保护您访问未分配的 memory,这是未定义的行为,可能导致您的程序崩溃。 That is why you should always store the size of your array and check it before trying to access your array.这就是为什么您应该始终存储数组的大小并在尝试访问数组之前检查它。

Lastly why does this line print the "wrong" size最后为什么这条线打印出“错误”的尺寸

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

This is probably caused by your wrong usage of realloc, but I can not say this for sure, because you didn't share the code of your structs and in the images you shared I can not find any attribute called "next_words".这可能是由于您对 realloc 的错误使用造成的,但我不能肯定地说,因为您没有共享结构的代码,并且在您共享的图像中我找不到任何名为“next_words”的属性。

What most people mean by a "dynamic array" in a struct is a pointer to a dynamically-allocated array, something like this:大多数人所说的结构中的“动态数组”是指向动态分配数组的指针,如下所示:

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

On the other hand, what you appear to be using, and what your other answer discusses, is a struct with a flexible array member .另一方面,您似乎正在使用的以及您的其他答案所讨论的是一个 struct with a flexible array member The latter has a few advantages over the former, but also several disadvantages.后者比前者有一些优点,但也有一些缺点。 Proper use of these is tricky, to the extent that I would consider them an expert feature.正确使用这些是棘手的,在某种程度上我认为它们是专家功能。 Moreover, flexible array members are better characterized as "variable" than as "dynamic", for although they may vary in size from one instance of the struct to another, the size of any particular one does not change during its lifetime.此外,灵活的数组成员被更好地描述为“可变”而不是“动态”,因为尽管它们的大小可能因结构的一个实例而异,但任何特定成员的大小在其生命周期内都不会改变。 * *

Thus, when you say,因此,当你说,

The dynamic array should be initialized with size of 1 WordProb object. For each new WordProb object i must realloc() the dynamic array by size of 1 WordProb and insert it.动态数组应初始化为 1 WordProb object 的大小。对于每个新的 WordProb object,我必须按 1 WordProb 的大小重新分配()动态数组并将其插入。

, it suggests that you want a form along the lines of the one above, not a struct with a flexible array member. ,它表明您需要一个类似于上面的形式的形式,而不是具有灵活数组成员的结构。 From that perspective,从这个角度来看,

  1. How can i allocate the dynamic_arr_of_WordProb_objects[] (see picture above) correctlly?我怎样才能正确分配 dynamic_arr_of_WordProb_objects[](见上图)?

There is nothing special about the pointer involved being a structure member.作为结构成员的指针没有什么特别之处。 Given a struct WordStruct that already exists (and which does not itself need to have been dynamically allocated for this purpose), you use malloc() .给定一个已经存在的struct WordStruct (并且它本身不需要为此目的动态分配),您可以使用malloc() For example,例如,

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

Again, there is nothing special about the pointer involved being a structure member.同样,作为结构成员的指针没有什么特别之处。 You use realloc() in the usual way:您以通常的方式使用realloc()

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

Supposing that the allocation / reallocation succeeds, you have a viable structure object in the array, but its value is indeterminate.假设分配/重新分配成功,数组中有一个可行的结构 object,但它的值是不确定的。 You can simply assign to the members of that object (therefore not needing any separate structure)...您可以简单地分配给 object 的成员(因此不需要任何单独的结构)...

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

... or if you want to copy in the value of a whole structure then you do that, still with the assignment operator ( = ): ...或者如果你想复制整个结构的值,那么你这样做,仍然使用赋值运算符( = ):

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

You go on to say你go就说

I did我做了

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

This printed 28.... How can this be sizeof(28) if it holds an array that holds other structures that are at least 24 byte big each?这打印了 28.... 如果它包含一个数组,而这个数组包含每个至少 24 字节大的其他结构,那么它怎么可能是 sizeof(28) 呢?

Because your sizeof expression does not mean what you think it means.因为你的sizeof表达式并不代表你认为的意思。 ws1->next_words has an array type, so when it appears as an operand in most expressions, including *ws1->next_words , it is automatically converted to a pointer to the first element. ws1->next_words具有数组类型,因此当它作为操作数出现在大多数表达式中时,包括*ws1->next_words ,它会自动转换为指向第一个元素的指针。 This is sometimes called "decaying".这有时被称为“衰减”。 When you dereference the resulting pointer, you get an object of type struct WordProbability .当您取消引用结果指针时,您会得到一个 object 类型的struct WordProbability The sizeof operator does not actually evaluate the expression, but it does return the size of that expression's type. sizeof运算符实际上并不计算表达式,但它确实返回该表达式类型的大小。 That is, your sizeof(*ws1->next_words) is 100% equivalent to sizeof(WordProbability) .也就是说,您的sizeof(*ws1->next_words) 100% 等同于sizeof(WordProbability) There is no way to use sizeof to determine the size of a FAM.无法使用sizeof来确定 FAM 的大小。 You need to keep track of that explicitly.你需要明确地跟踪它。

and how can i access index 55 if i never created it?如果我从未创建过索引 55,我该如何访问它?

It is unclear from what you presented whether the array is long enough to have an element at index 55. If it is then the question is moot.从您提供的内容中不清楚数组是否足够长以在索引 55 处包含一个元素。如果是,那么这个问题就没有意义了。 If it isn't then the behavior is undefined.如果不是,则行为未定义。 "Undefined" means what it says on the tin: you cannot rely on any particular behavior, and in particular, it is not safe to assume that the program will crash or even visibly misbehave. “未定义”意味着它在锡罐上所说的:你不能依赖任何特定的行为,特别是,假设程序会崩溃甚至明显的行为不端是不安全的。 Of course, it also is not safe to assume that the program will behave as intended, or even behave the same way from one run to another.当然,假设程序会按预期运行,或者甚至从一次运行到另一次运行都以相同的方式运行也是不安全的。

When i free(ws1->next_words) it will free all the memory from ws1->next_words[0] to [55]?当我释放(ws1->next_words)时,它会释放所有 memory 从 ws1->next_words[0] 到 [55]? Or only the first X amount of indexes i specified with realloc?或者只有我用 realloc 指定的前 X 数量的索引?

Note first that if ws1->next_words is a flexible array member then you cannot free (just) it in the first place.首先请注意,如果ws1->next_words是一个灵活的数组成员,那么您不能首先释放(只是)它。 You can free only the specific, whole objects you have previously allocated and not since freed.您只能释放您之前分配的特定的、完整的对象,并且之后还没有释放。 You cannot allocate memory for a FAM alone, so you cannot free it alone, either.您不能单独为 FAM 分配 memory,因此您也不能单独释放它。

You can certainly free a conventional dynamic array, however.但是,您当然可以释放传统的动态数组。 In that case, the whole allocated object is freed.在这种情况下,整个分配的 object 被释放。 This has nothing to do with which elements have been accessed, only the size of the allocated block, as chosen by malloc / realloc in light of the size requested.这与访问了哪些元素无关,仅与分配块的大小有关,由malloc / realloc根据请求的大小选择。


Finally, you ask about an example of (apparently) FAM usage:最后,您询问了一个(显然)FAM 用法的示例:

Free(sw1) not working correct免费(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

As you see there is still memory leak after i free(ws1).如您所见,在我释放 (ws1) 后仍然存在 memory 泄漏。 How can i free the Dynamic array correctly?如何正确释放动态数组?

I see no evidence of a memory leak, only undefined behavior arising from attempting to access an object after the end of its lifetime.我没有看到 memory 泄漏的证据,只有在其生命周期结束后尝试访问 object 时出现的未定义行为。 The output of the second printf() in that example is undefined and therefore not meaningful.该示例中第二个printf()的 output 未定义,因此没有意义。 It does not tell you anything about whether the object to which ws1 originally pointed was successfully freed.它不会告诉您有关ws1最初指向的 object 是否已成功释放的任何信息。

free() has no testable failure modes. free()没有可测试的故障模式。 If the program passes a pointer that does not satisfy free() 's requirements then undefined behavior results.如果程序传递的指针不满足free()的要求,则会导致未定义的行为。 If the program passes a pointer that does satisfy free() 's requirements then it must assume success.如果程序传递的指针确实满足free()的要求,则它必须假定成功。


* Someone might object, saying "you can realloc() the struct to change the size of a FAM", but reallocation is best viewed as replacing one object with a different one. *有人可能会说 object,说“你可以realloc()结构来改变 FAM 的大小”,但最好将重新分配视为用另一个 object 替换一个。 Although it copies data from the original object to the new one, the new one very often has a different address, especially when it is larger than the old one, and an object's address is the most fundamental aspect of its identity.虽然它将数据从原来的 object 复制到新的,但是新的经常有不同的地址,特别是当它比旧的大时,一个对象的地址是其身份的最基本方面。 This has practical impacts.这具有实际影响。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM