John Rodens
John Rodens

Reputation: 43

Freeing specific element from dynamically allocated array of structs

I have a simple struct s_person and constructor(int number) that takes the number of s_person we want to construct and returns a pointer to it.

In this example I have constructed 2 s_person, and assigned values to their attributes, name. And it works.

#include<stdio.h>
#include<stdlib.h>
#define MAX_NAME 50
typedef struct s_person {
    char *name;
    double grade;
}s_person;

s_person* construct(int number){
    int each;
    s_person *person = (s_person*)malloc(sizeof(s_person) * number);
    if(person){
        for(each=0; each<number; each++){
            person[each].name = (char*)malloc(sizeof(char));
        }
    }
    return person;
}

main()
{
    s_person *person = construct(2);
    person[0].name = "steven";
    person[1].name = "michael";

    printf("%s %s\n", person[0].name, person[1].name);

    // This works
    free(&person[0]);
    printf("%s %s\n", person[0].name, person[1].name);

    //This doesn't work
    free(&person[1]); // Crashes here
    printf("%s %s\n", person[0].name, person[1].name);
}

When I try to free the element 0 and print values again it works.

But if I try to free the element 1 and print values it crashes.

So, how can I free memory for the specific element in this array of structs?

And is it a valid way to make dynamically allocated memory for array of structs with constructor(int number) function or is there a better practice?

Upvotes: 0

Views: 1325

Answers (3)

Vlad from Moscow
Vlad from Moscow

Reputation: 310940

Your program has several errors most of which are related to memory allocation and memory leaks.

For example in function construct you allocte memory for data member name

    for(each=0; each<number; each++){
        person[each].name = (char*)malloc(sizeof(char));

and then in main you overwrite this values

person[0].name = "steven";
person[1].name = "michael";

Thus you have no access any more to the allocated memory and it can not be freed.

As for this statement

free(&person[1]); 

then you did not allocate person[1] separatly. You allocated one extent for two instances of the structure. So you may free only this allocated extent.

You could in function construct allocate each instance of the structure separatly by allocating an array of pointers to structures. In this case the function would look the following way

s_person ** construct( int number )
{
    int each;

    s_person **persons =  ( s_person ** ) malloc( number * sizeof( s_person * ) );

    if ( persons != NULL )
    {
        for ( each = 0; each < number; each++ )
        {
            persons[each] = ( s_person * )malloc( sizeof( s_person ) );
            if ( persons[each] != NULL )
            {
                persons[each]->name = ( char* )malloc( MAX_NAME );
            }
        }
    }

    return persons;
}

And in main you have to use standard C function strcpy to initialize data member name of allocated structures with string literals.

#include <string.h>

//...

int main( void )
//^^^^^^^^^^^^^^
{
    s_person **persons = construct( 2 );
    if ( persons[0] ) strcpy( persons[0]->name, "steven" );
    if ( persons[1] ) strcpy( persons[1]->name, "michael" );

    //...

In this case you can delete each separate element. For example

free( persons[0]->name );
free( persons[0] );
persons[0] = NULL;

free( persons[1]->name );
free( persons[1] );
persons[1] = NULL;

// and at last

free( persons );

Upvotes: 1

&#212;rel
&#212;rel

Reputation: 7622

Why malloc personn you can use the stack:

#include<stdio.h>                                                              
#include<stdlib.h>                                                             
#define MAX_NAME 50                                                            
typedef struct s_person {                                                      
    char *name;                                                                
    double grade;                                                              
}s_person;                                                                     


main()                                                                         
{                                                                              
    s_person person[2];                                                        
    person[0].name = "steven";                                                 
    person[1].name = "michael";                                                

    printf("%s %s\n", person[0].name, person[1].name);                         
} 

Upvotes: 0

Lukas Thomsen
Lukas Thomsen

Reputation: 3207

You cannot free a part of a memory block.

Within your function construct you allocate a memory area with the size of sizeof(s_person) * number bytes. If you try to free the first element you free the entire memory (notice that &person[0] is equal to the value returned by construct(2);.

If you try to free &person[1] you try to free an unknown memory area and free(&person[1]) will fail. Freeing an invalid/unknown memory area does not lead to a crash. But in the code you've posted you free the entire memory area of person which leads to an access violation when trying to get the address of person[1] which is the input for your free call.

If you want to free single persons you may want to use linked lists instead of one huge array... There are numerous examples on the web.

I've seen another issue within your code. Looking at your function construct I've noticed that you allocate a single byte for the name of the persone (but I guess you want to have a maximum of MAX_NAME characters for the name). So either modify person[each].name = (char*)malloc(sizeof(char)); to person[each].name = (char*)malloc(sizeof(char) * MAX_NAME); or change your code into the following:

#define MAX_NAME 50
typedef struct s_person {
    char name[MAX_NAME];
    double grade;
}s_person;

s_person* construct(int number){
    s_person *person = (s_person*)malloc(sizeof(s_person) * number);
    return person;
}

I recommend the latter since you cannot forget to free the memory allocated for the name of each person which makes maintaning your code easier.

Upvotes: 0

Related Questions