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
Related
When I try to free my struct, the program crashes because of a segfault. Inspecting the program with valgrind I have found:
==9761== Invalid free() / delete / delete[] / realloc()
==9761== at 0x484827F: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9761== by 0x109242: destroyHashTable (hashtable.c:38)
==9761== by 0x10942E: main (hashtable_main.c:17)
==9761== Address 0x1ffefffa70 is on thread 1's stack
==9761== in frame #2, created by main (hashtable_main.c:7)
I cannot really say anything more useful than having no idea, how to solve it. The crash happens during the free(ht) in destroyHashTable(ht) in hashtable.c. What am I doing wrong?
Below the code hashTable_main.c:
#include <stdio.h>
#include <stdlib.h>
#include "hashtable.h"
int main() {
hashTable* ht = NULL;
initHashTable(&ht);
int totalColCount = 0;
totalColCount += addHashTableEntry(&ht, "PRPR2");
destroyHashTable(&ht);
return EXIT_SUCCESS;
}
hashtable.c:
#include <stdlib.h>
#include <stdio.h>
#include "hashtable.h"
/* private internal API */
int hash_funktion(char *string);
hashtableEntry* createTableEntry(char* newKey) ;
/* end of private internal API */
int hash_funktion(char *string) {
unsigned int hash_adresse;
unsigned char *pointer;
hash_adresse = 0;
pointer = (unsigned char *) string;
while(*pointer != '\0') {
hash_adresse = 19 * hash_adresse + *pointer;
pointer++;
}
return hash_adresse % MAX_HASH;
}
hashtableEntry* createTableEntry(char* newKey) {
hashtableEntry* e = (hashtableEntry*) malloc (sizeof(hashtableEntry));
e->hashKey = newKey;
return e;
}
void initHashTable(hashTable* ht) {
ht = (hashTable*) malloc (sizeof (struct hashTable));
ht->table = (hashtableEntry*) malloc (MAX_HASH * sizeof (hashtableEntry));
}
void destroyHashTable(hashTable* ht) {
if (ht) {
free(ht);
ht = NULL;
}
}
int addHashTableEntry(hashtableEntry* ht, char* keyValue) {
hashtableEntry *e = createTableEntry(keyValue);
int colCounter = 0;
int hashValue = hash_funktion(keyValue);
if (ht[hashValue].hashKey == NULL) {
ht[hashValue] = *e;
return 0;
} else {
int newVal = (hashValue + 1) % MAX_HASH;
colCounter++;
while (ht[newVal].hashKey != NULL && newVal != hashValue ) {
newVal = (newVal + 1) % MAX_HASH;
colCounter++;
}
if (newVal != hashValue) {
ht[newVal] = *e;
return colCounter;
} else {
return -1;
}
}
}
bool searchValue(hashtableEntry* ht, char* searchValue) {
for (int i = 0; i < MAX_HASH; i++)
{
if(ht[i].hashKey == searchValue) {
return true;
}
}
return false;
}
and hashtable.h:
#pragma once
#define MAX_HASH 20
#include <stdbool.h>
typedef struct hashtableEntry {
char* hashKey;
} hashtableEntry;
typedef struct hashTable {
hashtableEntry* table;
int elemCount;
} hashTable;
void initHashTable(hashTable* ht);
void destroyHashTable(hashTable* ht);
int addHashTableEntry(hashtableEntry* ht, char* keyValue);
bool searchValue(hashtableEntry* ht, char* searchValue);
There never was a hashtable to begin with. The issue lies in initHashTable. It should be accepting a double pointer since it is given a pointer to a pointer it should initialize. The reason it can segfault despite the check in destroyHashTable is that the pointer is left uninitialized and may be non-zero at the start of program execution.
void initHashTable(hashTable** ht) {
*ht = (hashTable*) malloc (sizeof (struct hashTable));
(*ht)->table = (hashtableEntry*) malloc (MAX_HASH * sizeof (hashtableEntry));
}
You may find it easier to instead return the newly created hash table. This better expresses that initHashTable is giving you a new hashTable * value.
hashTable *initHashTable() {
hashTable *ht = (hashTable *) malloc (sizeof (struct hashTable));
ht.table = (hashtableEntry *) malloc (MAX_HASH * sizeof (hashtableEntry));
return ht;
}
There are also a bunch of other places where pointers are not handled correctly.
void doThing(Foo *foo) {
// This changes foo, but not the data foo points to.
foo = something;
// This changes the data foo points to
*foo = someOtherThing;
}
void doStuff() {
Foo *foo;
// This is incorrect since it creates a double pointer. doThing would need to
// be defined as "void doThing(Foo **foo)" to be correct.
doThing(&foo);
// Instead we can just pass the existing pointer
doThing(foo);
// We only need to create a reference if the value does not start out as a pointer
Foo bar;
doThing(&bar);
}
I am trying to create a dynamic array of Clients, but i am not succeding. This is my code. When i run this code the output is
3
� H
3
4332
3
8939
I think it's printing memory stuff, however i don't know why. I put my code down here
int client_counter = 0;
typedef struct client
{
char *pid;
char *message;
}Client;
void store (Client * client_array, char *buf)
{
Client c;
c.pid = strdup (strtok (buf, ":"));
c.message = strdup (strtok (NULL, "\0"));
client_array[client_counter++] = c;
}
int main () {
Client* client_array = malloc (sizeof (struct client));
char buf1[50] = { "1245:message" };
store (client_array, buf1);
char buf2[50] = { "4332:message" };
store (client_array, buf2);
char buf3[50] = { "8939:message" };
store (client_array, buf3);
for (int i = 0; i < client_counter; i++)
{
printf ("%d\n", client_counter);
printf ("%s\n", client_array[i].pid);
}
return 0;
}
I already tried do use this:
client_array = realloc(client_array, sizeof(struct client) * (client_counter + 1));
in store function right after this line.
client_array[client_counter++] = c;
But it's not working too.
You need to allocate extra memory if there's not enough space. Right now, you allocate enough for one, but you try to access three.
Don't forget to return the pointer of the new memory block back to main! In the following, this is done by passing a pointer to the caller's pointer. store modifies the caller's pointer via the passed pointer.
// Sets errno and returns 0 on error.
int store(Client ** client_array_ptr, char *buf) {
Client* new_client_array = realloc(*client_array_ptr, sizeof(Client) * (client_counter + 1));
if (!new_client_array)
return 0;
*client_array_ptr = new_client_array;
// ...
new_client_array[client_counter++] = c;
return 1;
}
int main() {
Client* client_array = NULL;
// ...
if (!store(&client_array, buf1)) {
perror("malloc");
exit(1);
}
// ...
if (!store(&client_array, buf2)) {
perror("malloc");
exit(1);
}
// ...
free(client_array);
return 0;
}
The original code does out-of-range access because it is trying to store multiple data in a buffer which is allocated for only one element.
To use realloc(), you have to note that arguments of functions in C are copies of what are passed. Modifying arguments inside callee function do not affect what is passed in caller. You should pass pointers to what should be modified to have functions modify caller's local things.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int client_counter = 0;
typedef struct client
{
char *pid;
char *message;
}Client;
void store (Client ** client_array, char *buf)
{
Client c;
c.pid = strdup (strtok (buf, ":"));
c.message = strdup (strtok (NULL, "\0"));
*client_array = realloc(*client_array, sizeof(struct client) * (client_counter + 1));
(*client_array)[client_counter++] = c;
}
int main () {
Client* client_array = malloc (sizeof (struct client));
char buf1[50] = { "1245:message" };
store (&client_array, buf1);
char buf2[50] = { "4332:message" };
store (&client_array, buf2);
char buf3[50] = { "8939:message" };
store (&client_array, buf3);
for (int i = 0; i < client_counter; i++)
{
printf ("%d\n", client_counter);
printf ("%s\n", client_array[i].pid);
}
return 0;
}
I would do it a bit different way.
typedef struct
{
char *pid;
char *message;
}client_TypeDef;
typedef struct
{
size_t size;
client_TypeDef clients[];
}clients_TypeDef;
clients_TypeDef *add(clients_TypeDef *clients, const char *pid, const char *message)
{
size_t newsize = clients ? clients -> size + 1 : 1;
client_TypeDef client = {.pid = strdup(pid), .message = strdup(message)};
if(client.pid && client.message)
{
clients = realloc(clients, sizeof(*clients) + newsize * sizeof(clients -> clients[0]));
if(clients)
{
clients -> size = newsize;
clients -> clients[newsize - 1] = client;
}
}
else
{
free(client.pid);
free(client.message);
clients = NULL;
}
return clients;
}
Here is my problem: I have to make this program for school and I spent the last hour debugging and googling and haven't found an answer.
I have an array of structures in my main and I want to give that array to my function seteverythingup (by call by reference) because in this function a string I read from a file is split up, and I want to write it into the structure but I always get a SIGSEV error when strcpy with the struct array.
This is my main:
int main(int argc, char *argv[])
{
FILE* datei;
int size = 10;
int used = 0;
char line[1000];
struct raeume *arr = (raeume *) malloc(size * sizeof(raeume*));
if(arr == NULL){
return 0;
}
if(argc < 2){
return 0;
}
datei = fopen(argv[1], "rt");
if(datei == NULL){
return 0;
}
fgets(line,sizeof(line),datei);
while(fgets(line,sizeof(line),datei)){
int l = strlen(line);
if(line[l-1] == '\n'){
line[l-1] = '\0';
}
seteverythingup(&line,arr,size,&used);
}
ausgabeunsortiert(arr,size);
fclose(datei);
return 0;
}
and this is my function:
void seteverythingup(char line[],struct raeume *arr[], int size,int used)
{
char *token,raumnummer[5],klasse[6];
int tische = 0;
const char c[2] = ";";
int i=0;
token = strtok(line, c);
strcpy(raumnummer,token);
while(token != NULL )
{
token = strtok(NULL, c);
if(i==0){
strcpy(klasse,token);
}else if(i==1){
sscanf(token,"%d",&tische);
}
i++;
}
managesize(&arr[size],&size,used);
strcpy(arr[used]->number,raumnummer);
strcpy(arr[used]->klasse,klasse);
arr[used]->tische = tische;
used++;
}
Edit: Since there is more confusion I wrote a short program that works out the part you are having trouble with.
#include <cstdlib>
struct raeume {
int foo;
int bar;
};
void seteverythingup(struct raeume *arr, size_t len) {
for (size_t i = 0; i < len; ++i) {
arr[i].foo = 42;
arr[i].bar = 53;
}
}
int main() {
const size_t size = 10;
struct raeume *arr = (struct raeume*) malloc(size * sizeof(struct raeume));
seteverythingup(arr, size);
return 0;
}
So basically the signature of your functions is somewhat odd. Malloc returns you a pointer to a memory location. So you really dont need a pointer to an array. Just pass the function the pointer you got from malloc and the function will be able to manipulate that region.
Original Answer:
malloc(size * sizeof(raeume*));
This is probably the part of the code that gives you a hard time. sizeof returns the size of a type. You ask sizeof how many bytes a pointer to you raeume struct requires. what you probably wanted to do is ask for the size of the struct itself and allocate size times space for that. So the correct call to malloc would be:
malloc(size * sizeof(struct raeume));
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.
I got some C code:
typedef struct {
size_t len;
size_t alloclen;
char *buf;
} str;
void strnappnd(str **s, const char *buf, size_t n) {
if ((*s)->len + n >= (*s)->alloclen) {
size_t nalloclen = (*s)->len + n + 1;
void *tmp = realloc((*s)->buf, nalloclen);
if (!tmp) {
printf("failure");
exit(-1);
}
(*s)->buf = tmp;
(*s)->alloclen = nalloclen;
}
memccpy((*s)->buf + (*s)->len, buf, '\0', n);
(*s)->len += n;
(*s)->buf[(*s)->len] = '\0';
}
void strfree(str **s) {
free((*s)->buf);
free(*s);
*s = NULL;
}
Apparently, the strnappnd leaks at the realloc line. Why?
Consider:
void f() {
str *s = (str *)malloc(sizeof(str));
s->len = 5;
s->alloclen = 5;
s->buf = strdup("Hello");
strnappend(&s, " World!", 7);
free(s); /* courtesy of Eric */
}
If you had something like that, the memory allocated by realloc() would leak as f() is left.
If you wrote
(*s)->buf = realloc((*s)->buf, nalloclen)
that would be a memory leak, because if realloc fails and returns NULL, you lose the (*s)->buf pointer, which is still pointing to allocated memory.
Since you exit on failure it's not a problem, but maybe your static analyser ignores the exit?
Like this mtrace said "No memory leaks"
char *strnappnd(str **s, const char *buf, size_t n) {
...
return (*s)->buf;
}
void strfree(str *s){
free(s->buf);
free(s);
}
using the sample code given by Frerich
void f() {
str *s = (str *)malloc(sizeof(str));
s->len = 5;
s->alloclen = 5;
s->buf = strdup("Hello");
s->buf = strnappend(&s, " World!", 7);
strfree(s);
}
You create strfree() function and it is not used inside the code.
The memory need to be free always, if it is not used.
if (!tmp) {
printf("failure");
if (!(*s) && !((*s)->buf))
strfree(&(*s));
exit(-1);
}
Looking strfree(), looks as you reserved memory for *s too in somewhere.
Do the same before your code finish.
if (!(*s) && !((*s)->buf))
strfree(&(*s));