I wrote this program and I want to free a structure myStruct but it doesn't compile it says:
free():double free detected in tcache2
Can you help me please ?
#include <stdio.h>
#include <stdlib.h>
typedef struct myStruct {
char* chaine;
struct myStruct* next;
} mystruct;
void supprimer(myStruct* D) {
free(D->chaine);
free(D);
}
int main()
{
myStruct* D = malloc(sizeof(myStruct));
D->next = NULL;
char* chaine = malloc(sizeof(char)*10);
chaine = "ouii";
D->chaine = chaine;
supprimer(D);
printf("Hello World");
return 0;
}
I tried to run the main with creating a new structure and deleting it but it doesn't work.
chaine = "ouii"; is wrong. That does not copy “ouii” into the memory pointed to by chaine, it sets the pointer chaine to point to the array created for the string literal "ouii". That address should not be passed to free.
Change chaine = "ouii"; to strcpy(chaine, "ouii");.
char* chaine = malloc(sizeof(char)*10);
chaine = "ouii";
The second statement changes what chaine was originally pointing to (i.e. a pointer to a region of heap memory returned by malloc), such that it now points to the string literal ouii. Now you've lost access to the original memory returned by malloc (have leaked memory), and are trying to free memory not returned by one of the memory allocation functions. Hence the warning.
Possible fix:
Use the standard strcpy to copy the strings.
strcpy (chaine, "ouii");
And add #include <string.h> to the list of headers.
Aside: malloc returns NULL on failure. You should check if the pointer is valid before accessing it. It also sets errno to ENOMEM on POSIX-compliant systems. Here's one way I prefer to handle it in main ():
#include <errno.h>
errno = 0;
char *chaine = malloc (10); /* sizeof (char) is defined to be 1 */
if (!chaine) {
errno = ENOMEM;
perror ("malloc");
return EXIT_FAILURE;
}
See also: How do I modify a char** in another function
This code snippet
char* chaine = malloc(sizeof(char)*10);
chaine = "ouii";
results in a memory leak.
At first a memory was allocated dynamically and its address was assigned to the pointer chaine
char* chaine = malloc(sizeof(char)*10);
and then the pointer was reassigned with the address of the first character of the string literal "ouii"
chaine = "ouii";
So the address of the dynamically allocated memory was lost.
Instead of this assignment
chaine = "ouii";
you need to copy characters of the string literal into the dynamically allocated memory using standard string function strcpy declared in the header <string.h>. For example
#include <string.h>
//...
char* chaine = malloc(sizeof(char)*10);
strcpy( chaine, "ouii" );
Pay attention to that this function
void supprimer(myStruct* D) {
free(D->chaine);
free(D);
}
makes the pointer D declared in main
myStruct* D = malloc(sizeof(myStruct));
invalid.
It is better to pass the pointer to the function by reference.
In C passing by reference means passing an object (including pointers) indirectly through a pointer to it. Thus dereferencing the pointer you will have a direct access to the object pointed to by the pointer and can change it.
Also as it seems you want to define a singly linked list then the function can look the following way
void supprimer( myStruct **D )
{
while ( *D != NULL )
{
myStruct *current = *D;
*D = ( *D )->next;
free( current->chaine );
free( current );
}
}
And the function is called like
supprimer( &D );
In this case after calling the function the pointer D defined in main will be equal to NULL.
Pay attention to that you should check whether memory was allocated successfully to avoid undefined behavior.
Here is a demonstration program that shows how your list could be implemented.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct myStruct
{
char *chaine;
struct myStruct *next;
} mystruct;
int push ( myStruct **D, const char *chaine )
{
myStruct *current = malloc( sizeof( *current ) );
int success = current != NULL;
if (success)
{
current->chaine = malloc( strlen( chaine ) + 1 );
success = current->chaine != NULL;
if (success)
{
strcpy( current->chaine, chaine );
current->next = *D;
*D = current;
}
else
{
free( current );
}
}
return success;
}
void supprimer( myStruct **D )
{
while (*D != NULL)
{
myStruct *current = *D;
*D = ( *D )->next;
free( current->chaine );
free( current );
}
}
void output( const myStruct *D )
{
for ( const myStruct *current = D; current != NULL; current = current->next )
{
printf( "\"%s\" -> ", current->chaine );
}
puts( "null" );
}
int main( void )
{
myStruct *D = NULL;
push( &D, "World" );
push( &D, "Hello" );
output( D );
supprimer( &D );
}
The program output is
"Hello" -> "Word" -> null
Related
i am trying to write a code in C but i am having some problems with realloc. I had to write a code that will create a stack, and will add to it (dodaj_do_stosu), reamove from it (usun_ze_stosu) and will look at the top thing that is on this stack. I have problem with compiling(it does work for first two words but then it returns (0xC0000374)).
I think i am usining the realloc wrong and the sizeof my structure. If someone could look at my code (especially at the function (dodaj_do_stosu) and tell me what am i doing wrong thx. My code look like this:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef struct {
int n;
char *nazwa;
}element_t;
typedef struct {
int rozmiar;
element_t **tablica;
}stos_t;
void top_of_stack(stos_t *s){
printf("ostatni element stosu:rozmiar = %d nazwa=%s, n=%d\n", s->rozmiar, s->tablica[s->rozmiar]->nazwa, s->tablica[s->rozmiar]->n);
}
void init(stos_t *s)
{
s->rozmiar=0;
s->tablica=malloc(0);
}
void dodaj_do_stosu(stos_t *s, int n, char *name)
{
s->tablica = realloc(s->tablica, (s->rozmiar + 1) * sizeof(s->tablica));
s->tablica[s->rozmiar]->nazwa = name;
s->tablica[s->rozmiar]->n = n;
printf("rozmiar=%d, n=%d , nazwa=%s\n",s->rozmiar, s->tablica[s->rozmiar]->n, s->tablica[s->rozmiar]->nazwa);
s->rozmiar++;
}
void usun_ze_stosu(stos_t *s)
{
s->tablica = realloc(s->tablica, (s->rozmiar - 1) * sizeof(s->tablica[0]));
s->rozmiar--;
}
void rm(stos_t s)
{
free(s.tablica);
}
int main(int argc, char **argv)
{
stos_t s;
init(&s);
int i;
srand(time(0));
if (argc>1)
for(i=1;i<argc;i++){
printf("%s\n", argv[i]);
dodaj_do_stosu(&s, rand() % 10, argv[i]);
}
for(i=0;i<argc-1;i++){
//printf("i=%d, n=%d, nazwa=%s\n",i, s.tablica[i].n, s.tablica[i].nazwa);
}
//top_of_stack(&s);
//usun_ze_stosu(&s);
//top_of_stack(&s);
rm(s);
return 0;
}
A big part of your problem is that tablica is an array of pointers, but you never initialize the pointers themselves.
The dodaj_do_stosu function reallocates the array, but doesn't create the element_t objects. Therefore any dereference of e.g. s->tablica[s->rozmiar] will lead to undefined behavior.
There are two possible solutions:
Allocate a new element_t structure:
s->tablica[s->rozmiar] = malloc(sizeof(element_t));
before you initialize the element_t structure members.
Make tablica an array of structure objects instead of pointers:
element_t *tablica; // tablica is an array of objects, not an array of pointers
I recommend solution 2.
At least the function dodaj_do_stosu is wrong. The data member tablica is declared like
element_t **tablica;
So the expression s->tablica[s->rozmiar] has the type element_t * and an indeterminate value. Thus dereferencing the pointer expression for example like
s->tablica[s->rozmiar]->nazwa
invokes undefined behavior.
You have to allocate memory for objects of the structure type element_t not for pointers of the type element_t *.
So you need to declare the data member like
element_t *tablica;
and within the function to write
s->tablica = realloc(s->tablica, (s->rozmiar + 1) * sizeof( *s->tablica));
Also it is safer to use an intermediate pointer for calls of realloc.
The function can look the following way
int dodaj_do_stosu( stos_t *s, int n, char *name )
{
element_t *tmp = realloc( s->tablica, ( s->rozmiar + 1 ) * sizeof( *s->tablica ) );
int success = tmp != NULL;
if ( success )
{
s->tablica = tmp;
s->tablica[s->rozmiar]->nazwa = name;
s->tablica[s->rozmiar]->n = n;
printf("rozmiar=%d, n=%d , nazwa=%s\n", s->rozmiar, s->tablica[s->rozmiar]->n, s->tablica[s->rozmiar]->nazwa );
++s->rozmiar;
}
return success;
}
Consequently the function should be redefined at least the following way. As is it can for example invoke undefined behavior when s->rozmiar is equal to 0.
int usun_ze_stosu( stos_t *s )
{
int success = s->rozmiar != 0;
if ( success )
{
element_t *tmp = realloc( s->tablica, ( s->rozmiar - 1 ) * sizeof( *s->tablica ) );
success = tmp != NULL;
if ( success )
{
s->tablica = tmp;
--s->rozmiar;
}
}
return success;
}
Also within the function init it will be much better ro write
void init(stos_t *s)
{
s->rozmiar=0;
s->tablica = NULL;
}
Another problem is the function rm
void rm(stos_t s)
{
free(s.tablica);
}
You should pass the original object through a pointer to it and within the function to write
void rm(stos_t *s)
{
free( s->tablica );
s->tablica = NULL;
s->rozmiar = 0;
}
I want to free a pointer in another function to save lines in my actual function, but when i use this self-written function:
void free_func(char *ptr1, char *ptr2)
{
if (ptr1)
{
free(ptr1);
ptr1 = NULL;
}
if (ptr2)
{
free(ptr2);
ptr2 = NULL;
}
}
it doesn't free my pointer.
This is my main function where i call my free_function:
int main(void)
{
char *test;
test = (char *)calloc(5, sizeof(char));
test[0] = 'h';
test[1] = 'e';
test[2] = 'y';
test[3] = '\0';
printf("test before: %s\n", test);
//free_func(test, 0, 0);
free(test);
test = NULL;
printf("test after: %s\n", test);
return(0);
}
This will give me the expected output:
test before: hey
test after: (null)
But if i comment the line where i free and use NULL, and uncomment my free_func, i will get this output:
test before: hey
test after: hey
My question now: why does my self written free function does something different, then the actual same lines in my main function?
it doesn't free my pointer.
You are mistaken. It does free the memory to which your pointer points (i.e. the memory returned be calloc).
There's no way to check this. Well, short of using something like -fsanitize=address or valgrind to check for memory leaks.
why does my self written free function does something different, then the actual same lines in my main function?
Actually, they do exactly the same thing. They free the memory, then set the variable to NULL.
The only difference is that the variable you print (test) isn't the variable you set to NULL (ptr1) when using the function. If you want the function to change the variable in the caller, you will need to give it the address the variable.
void free_and_set_null( void **pp ) {
free( *pp );
*pp = NULL;
}
int main( void ) {
char *p = malloc( 1 );
// ...
free_and_set_null( &p );
}
At least, that's the normal solution. It doesn't quite work here since we want to accept any kind of pointer. On a x86/x86-64, all pointers are the same size, and NULL is the same for every type of pointer. But that's not the case everywhere. As such, the above code violates the standard and warns.
Here, a macro could be used.
#ifdef _DEBUG
#define SAFE_FREE( p ) do{ free( p ); p = NULL; } while 0
#else
#define SAFE_FREE( p ) free( p )
#endif
int main( void ) {
char *p = malloc( 1 );
// ...
SAFE_FREE( p );
}
I have a stack implementation that stores variable: char *items on the stack. But for some reason when I use stack->items[position], it treats it as a regular char (not a pointer) and I am unable to store the full char (it is a URL) on the stack.
I want to give the push function a char * (that is a URL) and I want to take that and put in on my stack, that is either:
p->items[p->pos] = item;
or
strcpy(p->items[p->pos], item);
Here is the part of the code that gives the error:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "shm_stack.h"
typedef struct int_stack{
int size; /* the max capacity of the stack */
int pos; /* position of last item pushed onto the stack */
char *items; /* stack of stored chars */
} ISTACK;
int is_full(ISTACK *p){
if ( p == NULL ) {
return 0;
}
return ( p->pos == (p->size -1) );
}
int sizeof_shm_stack(int size){
return (sizeof(ISTACK) + sizeof(char) * size);
}
int init_shm_stack(ISTACK *p, int stack_size){
if ( p == NULL || stack_size == 0 ) {
return 1;
}
p->size = stack_size;
p->pos = -1;
p->items = (char *) (p + sizeof(ISTACK));
return 0;
}
ISTACK *create_stack(int size){
int mem_size = 0;
ISTACK *pstack = NULL;
if ( size == 0 ) {
return NULL;
}
mem_size = sizeof_shm_stack(size);
pstack = malloc(mem_size);
if ( pstack == NULL ) {
perror("malloc");
} else {
char *p = (char *)pstack;
pstack->items = (char *) (p + sizeof(ISTACK));
pstack->size = size;
pstack->pos = -1;
}
return pstack;
}
void destroy_stack(ISTACK *p){
if ( p != NULL ) {
free(p);
}
}
int push(ISTACK *p, char *item){
if ( p == NULL ) {
return -1;
}
if ( !is_full(p) ) {
++(p->pos);
//p->items[p->pos] = item;
strcpy(p->items[p->pos], item);
//printf("push method: %d\n", p->items[p->pos]);
return 0;
} else {
return -1;
}
}
The issue is in my push method where I can neither use strcpy() or just assign the char to p->items[p-pos] without it saying something like "assigning char from incompatible type char *", but dereferencing "item" will only get me the first character, and I want the entire "string".
Why is this happening and how can I fix it?
p->items is a char*, so p->items[...] is a char. strcpy expects a char*, so there's a mismatch between what you provide and what's needed.
Not only that, it expects the pointer to point to the first of enough characters to contain the string being copied in. You did not even attempt to get the length of the string pointed by item, much less allocate enough memory for it.
I presume you want a stack of strings. If so, we need a array of pointers (char *items[]) or a pointer to a block of memory for pointers (char **items). The latter is simpler here. As such,
char *items;
should be
char **items;
It would be allocated using
malloc(sizeof(char*) * size)
There are two approaches to adding a string to the stack.
The stack could take ownership of the string provided.
p->items[p->pos] = item;
The stack could make a copy of the string provided.
p->items[p->pos] = strdup(item);
The difference is in who is responsible for freeing the string.
I am filling up a string of characters and I double its size from time to time.
When I finish, I would like to free unused memory.
void fun (char **str, size_t *len) {
size_t lsi; //last_significant_index
//filling up the str and reallocating from time to time.
//*len is storing the total size of allocated memory at this point
// idea #1
free((*str)[lsi + 1]);
// idea #2
for(size_t i = lsi + 1; i < *len; i++) {
free(&(*str)[i]);
}
}
None of these ideas seem to work however
Is it even possible to do it? If so, how?
Details:
I am using this function to reallocate my strings:
static void increase_list_size(char **list, size_t *list_len)
{
size_t new_list_size = (*list_len + 1) * 2; // I am not allocating my list at the declaration, so *list_len initially equals 0.
char *new_list = malloc(sizeof(char) * new_list_size);
for (size_t i = 0; i < *list_len; i++)
{
new_list[i] = (*list)[i];
}
if (list != NULL) // I don't want to free an empty list (it wasn't allocated at the declaration!
{
free(*list);
}
(*list) = new_list;
*list_len = new_list_size;
}
As you can see, I am allocating two-times more memory every time - that's why I wanted to free unused memory at the end.
I thought there was some kind of a tricky way to do it, since I felt that you can only use free() to free whole memory block.
No, you can only free() pointers that have been returned by malloc().
You want to use realloc() to change the allocated memory size to a smaller (as well as larger) size. The contents of the array will be preserved.
Example:
#include <stdlib.h>
int main() {
char *str = malloc(100);
...
str = realloc(str, 50);
...
free(str);
}
Remember to check the return value of realloc() (as well as the one of malloc()) to ensure that the (re)allocation has not failed.
You can only free a pointer that is the result of malloc or realloc. You can't reduce the size of an allocation by freeing at an arbitrary offset from it. But you can realloc it to a smaller size: realloc(*str, lsi).
one way is to create a new string and use only space required and copy the content to this one. now you can free the previous one.
I will use this is realloc() is not allowed (sometimes in homework)
the other way is realloc() as others suggested.
You can use standard C function realloc declared in header <stdlib.h>
For example
char *s = malloc( 100 );
strcpy( s, "Hello world" );
char *p = realloc( s, strlen( s ) + 1 );
if ( p != NULL ) s = p;
Here is a demonstrative program
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main( void )
{
char *s = malloc( 100 );
strcpy( s, "Hello world" );
char *p = realloc( s, strlen( s ) + 1 );
if ( p != NULL ) s = p;
puts( s );
free( s );
return 0;
}
The program output is
Hello world
Or if you want to write a separate function then it can look the following way
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void resize( char **s, size_t n )
{
char *p = realloc( *s, n );
if ( p != NULL ) *s = p;
}
int main( void )
{
char *s = malloc( 100 );
strcpy( s, "Hello world" );
resize( &s, strlen( s ) + 1 );
puts( s );
free( s );
return 0;
}
Also you can use POSIX function strdup
I am trying to make a dynamic array of structs, and I can successfully add one struct to it. But any more structs I add cause a segmentation fault. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PEOPLE_BLOCK 4
struct Person {
char *first_name;
char *last_name;
unsigned int age;
};
int add_person(struct Person **people, size_t *people_size, size_t *population, struct Person p) {
if ((sizeof(struct Person) * *population) > *people_size) {
return -1;
}
if ((sizeof(struct Person) * (*population + 1)) >= *people_size) {
*people_size = *people_size + sizeof(struct Person) * PEOPLE_BLOCK;
*people = realloc(*people, *people_size);
if (!*people) {
return -1;
}
}
*people[*population] = p;
++*population;
return 0;
}
int main(int argc, char const *argv[]) {
size_t population;
size_t people_size;
struct Person *people, timn, batman;
population = 0;
people_size = sizeof(struct Person) * PEOPLE_BLOCK;
people = malloc(people_size);
timn.first_name = "Timn";
timn.last_name = "Timothy";
timn.age = 38;
add_person(&people, &people_size, &population, timn);
printf("Person 0's first name: %s\n", people[0].first_name);
batman.first_name = "Bat";
batman.last_name = "Man";
batman.age = 42;
add_person(&people, &people_size, &population, batman);
printf("Person 1's first name: %s\n", people[1].first_name);
free(people);
return 0;
}
I'd appreciate any help on why this is happening, thanks!
The problem resides with this line :
*people[*population] = p;
Change it to:
(*people)[*population] = p;
Why are the parenthesis requried?
The compiler has rules of operator precedence. When applying them, it sees your code as this:
*(people[*population]) = p;
which is not what you intended. Given a pointer-to-pointer Type **pp,
*pp[n] = value;
means "take the n'th pointer starting at pp, and assign value at the location dereferenced from the address that pointer holds. In other words, it means essentially this:
Type *p = pp[n];
*p = value;
What you really want is something that does this:
Type *p = *pp;
p[n] = value;
and that is what (*pp)[n], distinguishing the dereference of the pointer to pointer, gives you. Without that, you're using an invalid pointer, leading to your fault.
Not sure whether this answer will help, but anyway.
I don't understand your code, what you are trying to do.
You directly use the number of elements, a pointer to the first person, and the maximum number of elements. You'll probably have a lot of problems passing that all around.
You're storing literal strings directly in your structs, which means that in a real case (using no literals) that would result in memory leaks.
Here is my take. I've made PEOPLE_BLOCK smaller for testing reasons.
Hope this helps.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PEOPLE_BLOCK 2
typedef struct _Person {
char *first_name;
char *last_name;
unsigned int age;
} Person;
typedef struct _VectorPeople {
Person * people;
size_t num;
size_t max;
} VectorPeople;
void init(VectorPeople *v)
{
v->max = PEOPLE_BLOCK;
v->num = 0;
v->people = (Person *) malloc( sizeof(Person) * v->max );
}
void clear(VectorPeople *v)
{
// Clear persons
Person * it = v->people;
while( ( it - v->people ) < v->num ) {
free( it->first_name );
free( it->last_name );
++it;
}
// Clear vector
v->max = v->num = 0;
free( v->people );
v->people = NULL;
}
void add(VectorPeople *v, Person *p)
{
// Reserve
if ( v->num >= v->max ) {
v->max += PEOPLE_BLOCK;
// Realloc
v->people = realloc( v->people, v->max * sizeof(Person) );
if ( v->people == NULL ) {
exit( -1 );
}
}
// Copy strings
p->first_name = strdup( p->first_name );
p->last_name = strdup( p->last_name );
// Insert
v->people[ ( v->num )++ ] = *p;
}
int main(int argc, char const *argv[]) {
VectorPeople vp;
Person timn;
Person batman;
Person bond;
Person superman;
init( &vp );
timn.first_name = "Timn";
timn.last_name = "Timothy";
timn.age = 38;
add( &vp, &timn );
batman.first_name = "Batn";
batman.last_name = "Man";
batman.age = 42;
add( &vp, &batman );
bond.first_name = "James";
bond.last_name = "Bond";
bond.age = 45;
add( &vp, &bond );
superman.first_name = "Super";
superman.last_name = "Man";
superman.age = 45;
add( &vp, &superman );
int i = 0;
for(; i < vp.num; ++i ) {
printf( "Person: %s, %s.\n", vp.people[ i ].last_name, vp.people[ i ].first_name );
}
clear( &vp );
return 0;
}
There were a number of errors in your code. One thing to keep in mind, when you dynamically allocate memory, you are responsible for keeping track of it and freeing it when you no longer need it (otherwise, you will leak memory like a sieve).
In your code, you attempt to create an array of structs holding pointer to an array of characters. The char * pointers are NOT allocated and cannot simply be assigned in the manner you attempt. strdup can help, but you have just allocated memory, so free it when you are done with it.
Attempting to allocate an array of structs with varying (unknown) lengths of first_name and last_name requires that you keep track of every allocation. In some sense, you are better off declaring people as pointer to pointer to Person This allows iteration over your people without having to store the population somewhere allowing you to iterate until the first NULL pointer is encountered.
Likewise, creating a typedef to your struct can greatly cut down on the number of times you write sizeof (struct Person). It keeps the code clean and helps you think though the pointer haze.
Here is an example using a pointer-to-pointer-to-struct of what I think you intended to do. It is followed below by an implementation using only a pointer to struct. Evaluate both and decide which implementation you prefer:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXPOP 128
typedef struct {
char *first_name;
char *last_name;
unsigned char age;
} Person;
Person *add_person (Person ***ppl, Person p, size_t *pop, size_t *max);
Person **realloc_person (Person **ppl, size_t *n);
void free_person (Person *p);
void free_person_names (Person *p);
int main (void) {
size_t population = 0;
size_t maxp = MAXPOP;
size_t i = 0;
Person timn, batman;
Person **people = calloc (MAXPOP, sizeof *people);
if (!people) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
timn.first_name = strdup ("Timn");
timn.last_name = strdup ("Timothy");
timn.age = 38;
add_person (&people, timn, &population, &maxp);
free_person_names (&timn);
printf("\nPerson 0\n first name: %s\n last name : %s\n age : %hhu\n",
people[0]->first_name, people[0]->last_name, people[0]->age);
batman.first_name = strdup ("Bat");
batman.last_name = strdup ("Man");
batman.age = 42;
add_person (&people, batman, &population, &maxp);
free_person_names (&batman);
printf("\nPerson 1\n first name: %s\n last name : %s\n age : %hhu\n",
people[1]->first_name, people[1]->last_name, people[1]->age);
for (i = 0; i < population; i++)
free_person (people[i]);
free (people);
return 0;
}
/* add a person to an array of pointers to Person */
Person *add_person (Person ***ppl, Person p, size_t *pop, size_t *max)
{
if (*pop == *max)
*ppl = realloc_person (*ppl, max);
if (!((*ppl)[*pop] = malloc (sizeof ***ppl)))
return NULL;
size_t i = (*pop)++;
(*ppl)[i]-> first_name = strdup (p.first_name);
(*ppl)[i]-> last_name = strdup (p.last_name);
(*ppl)[i]-> age = p.age;
return (*ppl)[i];
}
/* realloc an array of pointers to Person setting memory to 0. */
Person **realloc_person (Person **ppl, size_t *n)
{
Person **tmp = realloc (ppl, 2 * *n * sizeof *ppl);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
// return NULL;
exit (EXIT_FAILURE);
}
ppl = tmp;
memset (ppl + *n, 0, *n * sizeof *ppl); /* memset new ptrs 0 */
*n *= 2;
return ppl;
}
/* free memory for a Person */
void free_person (Person *p)
{
if (!p) return;
if (p->first_name) free (p->first_name);
if (p->last_name) free (p->last_name);
free (p);
}
/* free only names of Person (for temp structs) */
void free_person_names (Person *p)
{
if (!p) return;
if (p->first_name) free (p->first_name);
if (p->last_name) free (p->last_name);
}
Note: updated to correct ppl start address on reallocation.
Using only Array of Person
While not inherently different than using a pointer to pointer to Person using a simple pointer to Person eliminates the ability to iterate over your array until a NULL or (empty) pointer is encountered. The following is an implementation of the same code using only an array of Person:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXPOP 128
typedef struct {
char *first_name;
char *last_name;
unsigned char age;
} Person;
Person *add_person (Person **ppl, Person p, size_t *pop, size_t *max);
Person *realloc_person (Person *ppl, size_t *n);
void free_person_names (Person p);
int main (void) {
size_t population = 0;
size_t maxp = MAXPOP;
size_t i = 0;
Person timn, batman;
Person *people = calloc (MAXPOP, sizeof *people);
if (!people) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
timn.first_name = strdup ("Timn");
timn.last_name = strdup ("Timothy");
timn.age = 38;
add_person (&people, timn, &population, &maxp);
free_person_names (timn);
printf("\nPerson 0\n first name: %s\n last name : %s\n age : %hhu\n",
people[0].first_name, people[0].last_name, people[0].age);
batman.first_name = strdup ("Bat");
batman.last_name = strdup ("Man");
batman.age = 42;
add_person (&people, batman, &population, &maxp);
free_person_names (batman);
printf("\nPerson 1\n first name: %s\n last name : %s\n age : %hhu\n",
people[1].first_name, people[1].last_name, people[1].age);
for (i = 0; i < population; i++)
free_person_names (people[i]);
free (people);
return 0;
}
/* add a person to an array of pointers to Person */
Person *add_person (Person **ppl, Person p, size_t *pop, size_t *max)
{
if (*pop == *max)
*ppl = realloc_person (*ppl, max);
size_t i = (*pop)++;
(*ppl)[i].first_name = strdup (p.first_name);
(*ppl)[i].last_name = strdup (p.last_name);
(*ppl)[i].age = p.age;
return ppl[i];
}
/* realloc an array Person setting memory to 0. */
Person *realloc_person (Person *ppl, size_t *n)
{
Person *tmp = realloc (ppl, 2 * *n * sizeof *ppl);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
// return NULL;
exit (EXIT_FAILURE);
}
ppl = tmp;
memset (ppl + *n, 0, *n * sizeof *ppl); /* memset new ptrs 0 */
*n *= 2;
return ppl;
}
/* free only names of Person (for temp structs) */
void free_person_names (Person p)
{
if (p.first_name) free (p.first_name);
if (p.last_name) free (p.last_name);
}
Output
$ ./bin/struct_add_person
Person 0
first name: Timn
last name : Timothy
age : 38
Person 1
first name: Bat
last name : Man
age : 42
One problem is the last argument of add_person() to be specific, the argument '(struct Person) p'. When 'timn' and 'batman' are passed into the add_person() function, they are passed as a copy of the original structure. In the add_person() structure, that data is actually on the stack and is volatile outside the scope of the function. Try changing the last argument to a pointer.