简体   繁体   中英

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

  1. WordProb
  2. WordStruct - which must contain a dynamic array of WordProb objects.

How the structs look [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.

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:

  1. How can i allocate the dynamic_arr_of_WordProb_objects[] (see picture above) correctlly?
  2. How can I realloc it?
  3. How do I "assign" it with the new 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? and how can i access index 55 if i never created it?

I'm very new to C, so every guidance will be helpful. Thanks!


After the GOAT @jsiller answer, I think i might have got it. 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]? Or only the first X amount of indexes i specified with realloc?

Thanks


Free(sw1) not working correct

    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). How can i free the Dynamic array correctly?

What you are using is (or used to be) called the 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.

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. 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 *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. So if we want to add 25 more elements to the foo struct with 50 elements we would need to reallocate it like this:

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. 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".

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 . 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.

, 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?

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() . 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:

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?

Supposing that the allocation / reallocation succeeds, you have a viable structure object in the array, but its value is indeterminate. You can simply assign to the members of that object (therefore not needing any separate structure)...

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

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?

Because your sizeof expression does not mean what you think it means. 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. This is sometimes called "decaying". When you dereference the resulting pointer, you get an object of type struct WordProbability . The sizeof operator does not actually evaluate the expression, but it does return the size of that expression's type. That is, your sizeof(*ws1->next_words) is 100% equivalent to sizeof(WordProbability) . There is no way to use sizeof to determine the size of a FAM. You need to keep track of that explicitly.

and how can i access index 55 if i never created it?

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. 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]? Or only the first X amount of indexes i specified with realloc?

Note first that if ws1->next_words is a flexible array member then you cannot free (just) it in the first place. 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.

You can certainly free a conventional dynamic array, however. In that case, the whole allocated object is freed. 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.


Finally, you ask about an example of (apparently) FAM usage:

Free(sw1) not working correct

 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). 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. The output of the second printf() in that example is undefined and therefore not meaningful. It does not tell you anything about whether the object to which ws1 originally pointed was successfully freed.

free() has no testable failure modes. If the program passes a pointer that does not satisfy free() 's requirements then undefined behavior results. If the program passes a pointer that does satisfy free() 's requirements then it must assume success.


* 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. 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. This has practical impacts.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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