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.
Related
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.
Basically, I made a structure which includes informations of a person. When I create them, I then hop into my other function which should print their information but I am stuck at that point.
This is my main where I call my functions:
int main()
{
Person* p1 = person_constructor("Steven", 1970, "male");
display_person(p1);
return 0;
}
This is where I construct my human, I am required to use dynamic memory allocation:
Person* person_constructor(char *name, int year_of_birth, char *sex)
{
Person p = {};
Person* pptr = &p;
strcpy(p.name, name);
p.year_of_birth = year_of_birth;
strcpy(p.sex, sex);
pptr = malloc(strlen(p.name) * sizeof(char) + strlen(p.sex) * sizeof(char) + sizeof(int));
return pptr;
}
And this is the print function which can't print out the name:
void display_person(Person* p)
{
printf("%s",p->name);
}
Your person_constructor() is seriously confused. You set pptr to point to p, only to overwrite it with the pointer to an uninitialised dynamic memory block (with incorrectly determined size).
// Allocate the structure memory
Person* pptr = malloc( sizeof(Person) ) ;
// Assign the structure members
strcpy( pptr->name, name ) ;
pptr->year_of_birth = year_of_birth;
strcpy( pptr->sex, sex ) ;
// Return a pointer to the allocation
return pptr ;
Your constructor should allocate memory for the person object, then initialize the allocated memory, in that order:
Person* person_constructor(char *name, int year_of_birth, char *sex)
{
Person *p = malloc(sizeof(*p));
if (p) {
snprintf(p->name, sizeof(p->name), "%s", name);
p->year_of_birth = year_of_birth;
snprintf(p->sex, sizeof(p->sex), "%s", name);
}
return p;
}
The code that calls the constructor must also free the memory after using it:
int main(void)
{
Person* p1 = person_constructor("Steven", 1970, "male");
if (p1) {
display_person(p1);
free(p1);
}
return 0;
}
Remarks:
Allocate according to the size of your object. You don't show the definition of the person struct, but the two string fields seem to be arrays of a fixed size, so that sizeof(struct Person) already includes them.
I've used snprintf instead of strcpy, becuse it ensures a null-terminated string that does not overflow the memory.
Amended #Observer code to make it more safe, removing not needed code
#define some_value1 64
#define some_value2 64
typedef struct
{
char name[some_value1];
char sex[some_value2];
int year_of_birth;
} Person;
char *safe_strncpy(char *dest, const char *src, size_t length)
{
strncpy(dest, src, length -1);
dest[length - 1] = 0;
return dest;
}
Person *person_constructor(const char *name, const int year_of_birth, const char *sex)
{
Person *pptr;
pptr=malloc(sizeof(*pptr));
if(pptr)
{
safe_strncpy(pptr->name,name, sizeof(pptr->name));
safe_strncpy(pptr->sex,sex, sizeof(pptr->sex));
pptr->year_of_birth=year_of_birth;
}
return pptr;
}
Simplify it to:
//ASSUMING PERSON IS LIKE THIS:
typedef struct person
{
char name[some_value1];
char sex[some_value2];
int year_of_birth;
} Person;
Person* person_constructor(char *name, int year_of_birth, char *sex)
{
Person *pptr;
pptr=malloc(sizeof(*pptr));
memset(pptr->name,'\0',sizeof(pptr->name));
memset(pptr->sex,'\0',sizeof(pptr->sex));
strcpy(pptr->name,name);
strcpy(pptr->sex,sex);
pptr->year_of_birth=year_of_birth;
return pptr;
}
Plus don't forget to check if space has been really allocated dynamically or not
This question already has an answer here:
Dynamic memory access only works inside function
(1 answer)
Closed 4 years ago.
I have created a structure which includes an array of strings and have populated that with words. When I try and fill the array more than half full I want to create a larger structure, copy the current data to that larger structure and then have that larger structure 'replace' the old one that is called from main. Although I have successfully created and copied the data to the new structure; which I can prove by printing the data out from within the function; I am not able to replace the old structure in main. The next book_insert I try inserts to the old, smaller structure not the new, larger one.
I am operating within a constraint whereby I cannot do the resizing / copying / replacing within main; it has to be called from the book_insert function called from main. Additionally I cannot edit void book_insert(dic* s, char* v) (i.e. add double pointers), it has to remain in this format.
#include <stdio.h>
#include <stdlib.h>
struct book {
int size;
int count;
char** words;
};
typedef struct book book;
/* Create empty book, specifying lenght of strings and how many of them */
book* book_init(int wordlen, int maxwords);
/* Add one element into the book */
void book_insert(book* s, char* v);
/* Creates and returns new, bigger book */
book* resize(book* s, book* new);
/* Prints book */
void prints(book* a);
int main(void)
{
book* test;
test = book_init(60, 10);
book_insert(test, "dog");
book_insert(test, "cat");
book_insert(test, "mouse");
book_insert(test, "elephant");
book_insert(test, "snake");
/*The next insert will cause the resize function to trigger*/
book_insert(test, "fish");
/*The resize funtion should cause 'test' to be replaced by a bigger book*/
/*But doesn't as this next print shows*/
printf("But printing from main function means I'm back to %d\n", test->size);
prints(test);
}
void book_insert(book* s, char* v)
{
int i = 0;
while (s->words[i] != NULL ) {
i++;
}
s->words[i] = v;
s->count++;
/*If the book is half full resize is triggered, and should pass back new, bigger book*/
if((s->count * 100 / s->size) > 50) {
book *new_book;
new_book = book_init(60, 20);
s = resize(s, new_book);
printf("Printing from resize function gives me new length of %d\n", s->size);
prints(s);
}
}
book* resize(book* s, book* new)
{
int i;
for (i = 0; i < s->size; i++) {
if (s->words[i] != NULL ) {
new->words[i] = s->words[i];
}
}
return new;
}
book* book_init(int wordlen, int maxwords)
{
int i;
book* new = malloc(sizeof(book));
new->size = maxwords;
new->count = 0;
new->words = (char**) calloc((size_t)new->size, sizeof(char*));
for (i=0; i<new->size; i++) {
new->words[i] = (char*) calloc(wordlen, sizeof(char));
new->words[i] = NULL;
}
return new;
}
void prints(book* a)
{
int i;
for (i = 0; i < a->size; i++) {
printf("Index: %d, word: %s\n", i, a->words[i]);
}
}
I have also attempted this with a pointer swap in a separate function, but this does not seem to work either. In this version I have made book_resize void and instead from dic_insert called the below function, after the resize, with dictionary_swap(&new_book, &s):
void dictionary_swap(book **new, book **old)
{
book *temp = *old;
*old = *new;
*new = temp;
}
This again lets me print out the new larger, structure within the book_insert function, but has no affect on what happens in main.
EDIT ANSWER
This question has been marked as a duplicate, which means I can't answer it myself, however I have since found the answer; I changed the above duplicate swap so that I called dictionary_swap(new_book, s); (no ampersands) on the following code:
void dictionary_swap(book *new, book *old)
{
book temp;
temp = *old;
*old = *new;
*new = temp;
}
In order to modify a pointer inside a function you have to pass the address of the pointer to the function, eg:
void changePtr(char* test) {
test = "Hello";
}
The above will not work because test cannot be returned to the caller, however:
void changePtr(char** test) {
if ( test != NULL ) {
*test = "Hello";
}
}
The above will work because the address of the pointer is passed and it can be de-referenced to change the contents.
Example of calling:
char* ptr;
changePtr(&ptr);
Here is a rewrite of your code implementing the above technique:
#include <stdio.h>
#include <stdlib.h>
typedef struct _book {
int size;
int count;
char** words; //Must allocate space for each pointer before copying to.
} book;
//No need for below, see above:
//typedef struct book book;
/* Create empty book, specifying lenght of strings and how many of them */
book* book_init(int wordlen, int maxwords);
/* Add one element into the book */
void book_insert(book** s, char* v);
/* Creates and returns new, bigger book */
book* resize(book* s, book* new);
/* Prints book */
void prints(book* a);
int main(void) {
book* test = book_init(60, 10);
book_insert(&test, "dog");
book_insert(&test, "cat");
book_insert(&test, "mouse");
book_insert(&test, "elephant");
book_insert(&test, "snake");
/*The next insert will cause the resize function to trigger*/
book_insert(&test, "fish");
/*The resize funtion should cause 'test' to be replaced by a bigger book*/
/*But doesn't as this next print shows*/
printf("But printing from main function means I'm back to %d\n", test->size);
prints(test);
}
void book_insert(book** s, char* v) {
if ( s == NULL || v == NULL ) {
return;
}
(*s)->words = realloc((*s)->words, sizeof(char*) * (++(*s)->count));
(*s)->words[(*s)->count - 1] = v;
/*If the book is half full resize is triggered, and should pass back new, bigger book*/
if((((*s)->count * 100) / s->size) > 50) {
book *new_book;
new_book = book_init(60, 20);
*s = resize(*s, new_book);
}
}
book* resize(book* s, book* new) {
int i;
for (i = 0; i < s->size; i++) {
if (s->words[i] != NULL ) {
new->words[i] = s->words[i];
}
}
printf("Printing from resize function gives me new length of %d\n", new->size);
prints(new);
return new;
}
book* book_init(int wordlen, int maxwords) {
int i;
book* new = calloc(1, sizeof(book));
new->size = maxwords;
return new;
}
void prints(book* a) {
int i;
for (i = 0; i < a->size; i++) {
printf("Index: %d, word: %s\n", i, a->words[i]);
}
}
I'm fiddling around with Object oriented programming in C (note! Not C++ or C# - just plain ol' C). Right now, I'm trying to dynamically resize a struct (I'm playing with writing a simple String class). The code builds okay:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct TestClass
{
char *s;
size_t size;
size_t b_size;
void (*CreateString) (struct TestClass*,char*);
};
void TestClassCreateString(struct TestClass *m, char* str)
{
char *buf;
m->size = strlen(str);
if (!m->size)
{
free(m->s);
m->s = malloc(16);
}
else
{
buf = realloc(m->s, m->size);
if (buf) m->s = buf;
}
}
struct TestClass* TestClassCreate()
{
struct TestClass* m = malloc((sizeof(struct TestClass)));
m->CreateString = TestClassCreateString;
return m;
}
int main()
{
struct TestClass* fizz = TestClassCreate();
fizz->CreateString(fizz,"Hello World");
free(fizz);
return 0;
}
…But on running it I get the following error:
malloc: *** error for object 0x5000000000000000: pointer being realloc'd was not allocated
*** set a breakpoint in malloc_error_break to debug
Is anyone able to identify where I've gone wrong? Thanks in advance!
malloc does not zero its memory; it returns garbage, so you get an invalid pointer inside this struct:
struct TestClass* m = malloc((sizeof(struct TestClass)));
When creating a struct TestClass in TestClassCreate() the code misses to properly initialise the freshly allocated struct.
So calling
free(m->s);
tries to free memory at a random address, which invokes undefined behaviour and typically crashes the program.
To fix this modify the code as follows
struct TestClass* TestClassCreate()
{
struct TestClass* m = ...
...
m->s = NULL;
m->size = 0;
m->b_size = 0;
return m;
}
To make things better also add some error checking:
struct TestClass* TestClassCreate()
{
struct TestClass * m = malloc((sizeof(struct TestClass)));
if (NULL != m)
{
m->CreateString = TestClassCreateString;
m->s = NULL;
m->size = 0;
m->b_size = 0;
}
return m;
}
To make the code even more fail-safe apply these last changes:
struct TestClass* TestClassCreate(void)
{
struct TestClass * m = malloc(sizeof *m);
...
Further more the code misses to allocate memory for the C-"string"'s 0-terminator here:
void TestClassCreateString(struct TestClass *m, char* str)
{
...
else
{
buf = realloc(m->s, m->size + 1); /* allocate 1 byte more for the trailing
`0` marking the end of a C-"string". */
...
You are short by 1-byte. You need to add 1 to m->size for the null-terminator if you intend to copy str to m->s. E.g.:
void TestClassCreateString(struct TestClass *m, char* str)
{
char *buf;
m->size = strlen(str);
if (!m->size)
{
free(m->s);
m->s = malloc(16);
}
else
{
buf = realloc(m->s, m->size + 1);
if (buf) m->s = buf;
strncpy (m->s, str, m->size + 1);
}
}
Then you can do something like:
int main()
{
struct TestClass* fizz = TestClassCreate();
fizz->CreateString(fizz,"Hello World");
printf ("\n fizz->s : %s\n\n", fizz->s);
free(fizz);
return 0;
}
and get:
$ ./bin/oo_struct
fizz->s : Hello World
I have structs:
typedef struct accont
{
char **tel;//list of tel
char **email;//list of emails
}acc;
and
typedef struct _strcol
{
int count; //total of accounts
acc **list;
} strcol ;
I access the structure with a pointer:
strcol index;
contato *p;
p = (index.list + index.count);
the question, how i use malloc() in this function?
i try:
(*p)->tel = (char **) malloc(i * sizeof (char*))
p.tel = (char **) malloc(i * sizeof (char*))
&(*p)->tel = (char **) malloc(i * sizeof (char*))
and then as I do the second malloc to save data email or tel
my first post, excuse anything
So this:
(*p)->tel = (char **) malloc(i * sizeof (char*))
allocates space to store i pointers to char - so you can have i telephone number strings. But you don't actually have any space allocated to store those telephone number strings themselves yet. To do that, you need (for the first telephone number):
(*p)->tel[0] = malloc(j);
If this call to malloc() succeeds, you can now store nul-terminated string of length j-1 in the space pointed to by (*p)->tel[0]. You can then do the same for the other pointers in (*p)->tel up to (*p)->tel[i-1].
Using malloc() is simple if code follows:
some_type *p;
p = malloc(number_of_elements * sizeof *p);
if (p == NULL) Handle_OutOfMemory();
So with p.tel,
// p.tel = (char **) malloc(i * sizeof (char*));
p.tel = malloc(i * sizeof *(p.tel));
if (p.tel == NULL) exit(EXIT_FAILURE);
I'm going to assume 'p' is acc *p; (i have no idea what 'contato' is).
Anyway ... the point is to show how memory can be allocated & tel/email data stored/accessed ... Also copied tel #/email id simply to demonstrate ...
Regarding casting void pointer returns from malloc, I've seen arguments for/against ... i cast (malloc's about the only case where i cast).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct accont
{
char **tel; //list of tel
char **email; //list of emails
}acc;
typedef struct _strcol
{
int count; //total of accounts
acc **list;
}strcol;
int main()
{
int iNumAccounts = 5; // Assume there are 5 Accounts.
int iNumTels = 2; // Each Account has 2 Tel #s.
int iNumEmails = 3; // Each Account has 3 Email ids.
strcol index;
acc *p = NULL;
index.list = (acc **)malloc(5 * sizeof(acc *)); // Master list
// of 'acc' pointers i.e. pointer to a set of pointers.
int i, j;
for(i=0; i<iNumAccounts; i++) // Go through the 5 Accounts, one at
// a time ... and allocate & store tel #s/email ids.
{
index.list[i] = (acc *)malloc(sizeof(acc));
p = index.list[i];
p->tel = (char **)malloc(iNumTels * sizeof(char*));
for(j=0; j<iNumTels; j++)
{
p->tel[iNumTels] = (char *)malloc(11 * sizeof (char)); // 10 digit tel # + 1 byte for '\0' ...
strcpy(p->tel[iNumTels], "1234567890");
}
p->email = (char **)malloc(iNumEmails * sizeof(char*));
for(j=0; j<iNumEmails; j++)
{
p->email[iNumEmails] = (char *)malloc(51 * sizeof(char)); // 50 char long email id + 1 byte for '\0' ...
strcpy(p->email[iNumEmails], "kingkong#ihop.yum");
}
}
for(i=0; i<iNumAccounts; i++) // Go through the 5 Accounts, one at a time ... and display.
{
p = index.list[i];
for(j=0; j<iNumTels; j++)
{
printf("Tel # is: %d\n", p->tel[iNumTels]);
}
for(j=0; j<iNumEmails; j++)
{
printf("Email id is: %s\n", p->email[iNumEmails]);
}
printf("----------\n");
}
}
If I've understood the case correct, a stack implementation will be best suited in this case. You can use the standard stack library header (of gcc) or create your own stack implementation suited for your own need.
An example may be something like the code below but you'd better follow the Jerry Cain's videos about the stack procedures (you'll find these videos on youtube: Stanford - Programming Paradigms videos. Stack session should be between video number 6 to 8). link from here
note: be careful! Killing stack elements (via StackPop) will not kill the char strings created by strdup. You'll need to free them individually. These are explained in the videos but I don't exactly remember how (again, you'd find some valuable info in those videos for your case).
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
typedef struct {
char *tel;
char *email;
} account;
typedef struct {
int *ptrElement; // starting address of the stack
int sizeAllocat; // total size allocated
int sizeCurrent; // current size
int sizeElement; // byte length of the stack element
} Stack;
// create a new stack pointer
void StackNew (Stack *s, int sizeElement) {
assert (s->ptrElement > 0);
s->sizeElement = sizeElement;
s->sizeCurrent = 0;
s->sizeAllocat = 4;
s->ptrElement = malloc (4 * sizeElement);
assert (s->ptrElement != NULL);
}
// kills a stack pointer
void StackDispose (Stack *s) {
free (s->ptrElement);
}
// expands stack space
static void StackGrow (Stack *s) {
s->sizeAllocat *= 2;
s->ptrElement = realloc (s->ptrElement, s->sizeAllocat * s->sizeElement);
}
// insert new stack pointer (of type account for example)
void StackPush (Stack *s, void *ptrElement) {
if (s->sizeCurrent == s->sizeAllocat) {
StackGrow (s);
}
void *target = (char *) s->ptrElement + s->sizeCurrent * s->sizeElement;
memcpy (target, ptrElement, s->sizeElement);
s->sizeCurrent++;
}
// pops (deletes) an element from stack
void StackPop (Stack *s, void *ptrElement) {
void *source = (char *) s->ptrElement +
(s->sizeCurrent - 1) * s->sizeElement;
memcpy (ptrElement, source, s->sizeElement);
s->sizeCurrent--;
}
// relocate stack element
void StackRotate (void *first, void *middle, void *after) {
int foreSize = (char *) middle - (char *) first;
int backSize = (char *) after - (char *) middle;
char tmp [foreSize];
memcpy (tmp, first, foreSize);
memmove (first, middle, backSize);
memcpy ((char *) after - foreSize, tmp, foreSize);
}
int main () {
Stack s;
account *acc;
StackNew (&s, sizeof (acc));
// your code here
// example
// acc->tel = strdup("some number");
// acc->email = strdup("some text");
// StackPush(&s, &acc);
...
// StackPop(&s, &acc);
...
...
StackDispose (&s);
return 0;
}