UPDATE:
I've changed the static array for a dynamic, but I still get the segment violation error, although eclipse says:
*** glibc detected *** (path to file) double free or corruption (!prev): 0x00000000004093d0 ***
StructHashTable is a typedef...
int main() {
...
StructHashTable *B0 = (StructHashTable *) malloc(N_ELEMS*sizeof(StructHashTable));
...
}
void resizeHash(StructHashTable *hash) {
int size = currentElements + N_ELEMS;
StructHashTable newHash[size];
int i;
for (i = 0; i < size; i++) newHash[i].key = FREE;
for (i = 0; i < currentElements; i++) insertHash(newHash, hash[i]);
currentElements = size;
hash = (StructHashTable *) realloc(hash, size*sizeof(StructHashTable));
if (hash != NULL) {
for (i = 0; i < size; i++) hash[i] = newHash[i];
}
}
What's wrong now? Am I using in a bad way realloc? or what? C is driving me crazy...
OLD:
I'm working in university homework and I need to resize an static array in C, it has to be static, the debbugger says segment violation...
I have a main function that declares the array...
// File: main.c
int main() {
...
StructHashTable hash[N_ELEMS];
...
}
At some point on runtime I need more elements than N_ELEMS and I've written a function to do it in HashTable.c, that's the method:
// File: HashTable.c
#define N_ELEMS 32
int currentElements = N_ELEMS
void resizeHashTable(StructHashTable *hash) {
int size = currentElements + N_ELEMS;
StructHashTable newHash[size];
int i;
// Inicialize newHash
for (i = 0; i < size; i++) newHash[i].key = FREE;
// Insert old hash elements to the new table...
for (i = 0; i < currentElements; i++) {
insertHash(newHash, hash[i]);
}
currentElements = size;
// I've tried making hash null with no luck...
//hash = NULL;
//free(hash);
// HERE'S THE ERROR...
hash = newHash;
// I've tried *hash = *newHash with the same result...
}
Can someone tell me how to do what I'm trying to do?
Thanks.
You are getting error because you are trying to modify the l-value for a statically allocated array 'hash'.
Whenever you define an array (as in main)
StructHashTable hash[N_ELEMS];
sizeof(StructHashTable)*N_ELEMS bytes of memory is allocated for hash, and hash points to the first byte. Such an allocation is called static allocation and you cannot make hash point to some other memory allocation. It will give error as you specified, because the l-value i.e left hand side value cannot be modified. Neither you can free the memory assigned to hash. Memory assigned to hash will be freed only when main will terminate.
// HERE'S THE ERROR...
hash = newHash;
I suggest you to use dynamic memory allocation for hash in main, if you wish to resize hash during run time.
int main() {
...
//Initial hash table creation
StructHashTable *B0 = (StructHashTable *) malloc(N_ELEMS*sizeof(StructHashTable));
...
...
//Do something
...
...
//hash table full. So resize hash table. This function call need not be inside main. Just for illustration purpose I am doing it here.
B0 = resizeHash(B0);
...
}
And here's your modified resizeHash function.
StructHashTable* resizeHash(StructHashTable *oldHash) {
int size = currentElements + N_ELEMS;
int i;
//Allocate memory for newHash with larger number of elements.
StructHashTable *newHash = (StructHashTable*) malloc(size * sizeof(StructHashTable));
if (newHash != NULL) {
for (i = 0; i < currentElements; i++) {
// Copy one by one oldHash table elements into newhash table
//Something like below, or whatever you have been doing before to copy.
newHash[i] = oldHash[i];
}
}
//Free memory occupied by oldHash
free(oldHash);
//Set new value for currentElements
currentElements = size;
//return the newHash address to calling function.
return (newHash);
}
Related
I am creating a deque to store stings in C, and when I call the free() function, the program crashes. I have implemented a similar structure but only storing integers, and encountered no problems, but this seems to be causing me a few. I created a struct containing a multidimensional array or characters, and i think maybe I am not using the pointers correctly? I have searched far and wide and cannot solve it The main area of concern is when i call clear() from the ain body. That in turn calls free(), and the program just stalls. :-( Any help would be extremely useful.
#include <stdio.h>
#define MAX 20 // number of characters for word
typedef struct {
char **deque;
int size;
int pFront;
int pRear;
} deque;
typedef int bool;
enum { false, true };
void initDeque(deque *d, int initialSize)
{
d->size = initialSize;
d->pFront = -1;
d->pRear = -1;
d->deque = (char **)malloc(sizeof(char*)*initialSize);
int idx;
for(int idx = 0; idx < d->size; idx++)
{
d->deque[idx] = (char *)malloc((MAX+1) * sizeof(char));
d->deque[idx] = "";
}
printf("d->size: %zu\n", d->size);
}
void clear(deque *d) {
if(d->pFront == -1)
{
printf("Queue is empty\n");
}
else
{
printf("Attempting to clear...\n");
for(int idx = 0; idx < d->size; idx++)
{
printf("Attempting to clear columns...");
free(d->deque[idx]);
}
printf("Attempting to clear rows...");
free(d->deque);
printf("Freed!!!!\n");
d->deque = NULL;
d->size = 0;
d->pFront = -1;
d->pRear = -1;
}
}
bool isEmpty(deque *d)
{
if(d->pFront == -1){
return true;
}
else
{
return false;
}
}
bool isFull(deque *d)
{
if(d->size == d->pRear+1)
{
return true;
}
else
{
return false;
}
}
void display(deque *d)
{
if(isEmpty(d)){
printf("empty\n");
}
else{
printf("Deque Values:\n");
int idx;
for(int idx = 0; idx <= d->pRear; idx++)
{
printf("Index: %zu\tValue: %s\n", idx, d->deque[idx]);
}
printf("Size: %zu\n", d->size);
}
}
void rAppend(deque *d, char item[]) // as in rear append - same enqueue for queue structure.
{
if(isFull(d))
{
printf("Is Full\n");
int idx;
deque dTemp;
initDeque(&dTemp, d->size);
printf("dTemp Initialised\n");
for(idx = 0; idx < d->size; idx++)
{
dTemp.deque[idx] = d->deque[idx];
}
printf("deque copied to dTemp:\n");
for(idx = 0; idx < d->size; idx++)
{
printf("dTemp[%zu]: %s\n", idx, dTemp.deque[idx]);
}
clear(&d);
printf("d cleared\n");
initDeque(&d, dTemp.size*2);
printf("New deque of double length initialised\n");
for(idx = 0; idx < dTemp.size; idx++)
{
d->deque[idx] = d->deque[idx];
}
printf("dTemp Copied to new deque\n");
clear(&dTemp);
printf("dTemp Cleared\n");
char **tmp = realloc( d->deque, sizeof (d->deque) * (d->size*2) );
if (tmp)
{
d->deque = tmp;
for (int i = 0; i < d->size; i++)
{
d->deque[d->size + i] = malloc( sizeof(char) * MAX );
}
}
}
printf("Appending to rear.. %s\n", item);
d->pRear++;
d->deque[d->pRear] = item;
if(d->pFront == -1)
d->pFront = 0;
}
int main(void)
{
deque d;
initDeque(&d, 5);
rAppend(&d, "when");
rAppend(&d, "will");
rAppend(&d, "wendy");
rAppend(&d, "walk");
rAppend(&d, "with");
display(&d);
clear(&d);
return 0;
}
The problem is your are calling free() on static chain "when", "will",...
You can replace insertion in the function void rAppend(deque *d, char item[]) :
d->deque[d->pRear] = item;
with:
d->deque[d->pRear] = strdup(item);
Doing like this chains are allocated in the heap and free from the heap.
After there is others problems in the code, but it run without crash.
The main problem seems to be that you don't appreciate the difference between copying / assigning pointers and copying / assigning the data to which they point. Secondarily, it seems you may not appreciate the utility of pointers that don't point to anything, especially null pointers. Some details follow.
You are dynamically allocating space for a bunch of strings ...
for(int idx = 0; idx < d->size; idx++)
{
d->deque[idx] = (char *)malloc((MAX+1) * sizeof(char));
... and then leaking all of that space by replacing the pointer to each with a pointer to an empty string literal:
d->deque[idx] = "";
}
As if the leak were not bad enough, you are not permitted to free a string literal or modify its content, which you nevertheless try to do to any of those pointers that remain in the dequeue whenever you clear() it. This is likely the cause of some of your errors.
If you want to set each allocated string to an empty one then modify its content instead of replacing the pointer to it. For example:
d->deque[idx][0] = '\0';
In fact, however, you probably don't need to do even that. You are already performing bookkeeping to know which arrays contain valid (string) data and which don't, and that should be sufficient to do the right thing. Supposing you maintain copies of the strings in the first place.
But that's not all. When you rAppend() elements to your deque you have a similar problem. You create a temporary deque, and then copy the string pointers from your original deque into the temporary:
dTemp.deque[idx] = d->deque[idx];
Not only does this leak the original (empty) data in the temporary deque, it aliases that deque's contents with the main deque's. When you later clear the temporary deque, therefore, you free all the string pointers in the original. Subsequently using or freeing them produces undefined behavior.
Perhaps you instead want to strcpy() all the elements of the main deque into the temp and back, but I suggest instead skipping the temp deque altogether with something along these lines:
void rAppend(deque *d, char item[]) // as in rear append - same enqueue for queue structure.
{
if(isFull(d))
{
printf("Is Full\n");
char **tmp = realloc(d.deque, d->size * 2);
if (tmp)
{
d->deque = tmp;
for (int i = 0; i < d->size; i++)
{
// Copied from the original, but see below
d->deque[d->size + i] = malloc( sizeof(char) * MAX );
}
d->size * 2;
} // else?
}
printf("Appending to rear.. %s\n", item);
d->pRear++;
// Oops, this is another leak / aliasing issue:
d->deque[d->pRear] = item;
if(d->pFront == -1)
d->pFront = 0;
}
The whole point of the temporary deque is lost on me, since the realloc() you need to do preserves the original data anyway (as long as it succeeds, anyway).
Note too, however, that this still has an aliasing issue: you have aliased a deque element with the appended string, and leaked the memory allocated for that element. Furthermore, when you clear the deque, you free that string for everyone holding a pointer to it. Or at least you attempt to do so. You're not permitted to do that to string literals.
I suggest not allocating space in your deque for the individual strings at all, and not freeing it. Continue to use assignment to store elements in your deque, understanding and embracing that these are aliases. This will be more analogous to your implementation for ints.
#include<memory>
#include<iostream>
using namespace std;
struct S {
S() { cout << "make an S\n"; }
~S() { cout << "destroy an S\n"; }
S(const S&) { cout << "copy initialize an S\n"; }
S& operator=(const S&) { cout << "copy assign an S\n"; }
};
S* f()
{
return new S; // who is responsible for deleting this S?
};
unique_ptr<S> g()
{
return make_unique<S>(); // explicitly transfer responsibility for deleting this S
}
int main()
{
cout << "start main\n";
S* p = f();
cout << "after f() before g()\n";
// S* q = g(); // this error would be caught by the compiler
unique_ptr<S> q = g();
cout << "exit main\n";
// leaks *p
// implicitly deletes *q
}
In my below code I am trying to create a dynamically expandable array of memory.
#include <stdio.h>
#include <stdlib.h>
#define BLOCKSIZE 5
int hash_table_length = 0;
int *currentblock = NULL;
int size_left;
int *hash_table = NULL;
int *start = NULL;
int *create_hash_table() {
int *tmp;
if (currentblock == NULL || size_left == 0) {
if (currentblock == NULL) {
currentblock = (int *) malloc( BLOCKSIZE * sizeof(int));
start = currentblock;
size_left = BLOCKSIZE;
} else {
currentblock = (int *) malloc( BLOCKSIZE * sizeof(int));
size_left = BLOCKSIZE;
}
}
tmp = currentblock++;
size_left -= 1;
return tmp;
}
void build() {
int hash;
int i = 0;
for (i = 0; i < 20; i++) {
hash = i + 3;
if (hash_table_length == 0) {
hash_table = create_hash_table();
hash_table_length++;
} else {
hash_table = create_hash_table();
hash_table_length++;
}
hash_table = &hash;
printf("hash value is %d\n", *hash_table);
}
}
int main() {
build();
// How do I reach the start of the hash table again?
// the below start does not give me the first value
printf("Hash table first value is %d\n", *start);
return 0;
}
My problem here is I wish to traverse through the values stored in the hash_table. I am unable to reach to the first element/address of the hash_table. I wish to print out all the values stored in my hash table. How can this be done?
In your code the hash values never get stored inside the hash table(inside currentblock). Inside the create_hash_table() function you allocate memory for a new block but never store values inside this block. Thus if you try dereferencing any of these int* locations you might get a garbage value(which may be a 0).
This is what is precisely happening inside your main() function when you dereference the start pointer. It is infact pointing to the start of the hash table and as that location is uninitialized it gives an output of 0.
To actually store values inside the hash table change the following inside build():
hash_table = &hash;
to:
*hash_table = hash; // Store value of 'hash' inside the memory location pointed to by hash table(which happens to be 'current_block' inside build())
Now if you try running the code, it will output 3.
Coming to the second part of question as to how you'll traverse the entire hash table: It cannot be done using this code. This is because there is no linkage between your malloc'd blocks of integers. The malloc() call can assign any block of free memory from the heap. Thus in the current form you have disconnected blocks of locations which cannot be traversed.
Instead of malloc you can use realloc to increase the size of your current block. realloc allocates memory for the larger block and copies your previous data to this new block. This will essentially allow you to traverse the entire hash table using start.
Here is how you might do that:
#include <stdio.h>
#include <stdlib.h>
#define BLOCKSIZE 5
int hash_table_length = 0;
int *currentblock = NULL;
int size_left;
int *hash_table = NULL;
int *start = NULL;
int *create_hash_table() {
int *tmp;
if (currentblock == NULL || size_left == 0) {
if (currentblock == NULL) {
currentblock = (int *) malloc(BLOCKSIZE * sizeof(int));
start = currentblock;
size_left = BLOCKSIZE;
} else {
/* Call realloc() to allocate new memory block of size (hash_table_length+BLOCKSIZE) and copy previous data*/
currentblock = ((int *) realloc(start,(hash_table_length + BLOCKSIZE) * sizeof(int))) + hash_table_length;
size_left = BLOCKSIZE;
}
}
tmp = currentblock++;
size_left -= 1;
return tmp;
}
void build() {
int hash;
int i = 0;
for (i = 0; i < 20; i++) {
hash = i + 3;
if (hash_table_length == 0) {
hash_table = create_hash_table();
hash_table_length++;
} else {
hash_table = create_hash_table();
hash_table_length++;
}
/* Store value of hash inside the hash_table */
*hash_table = hash;
printf("hash value is %d\n", *hash_table);
}
}
int main() {
int i;
build();
printf("Hash table first value is %d\n", *start);
/* Traverse the hash table */
for(i = 0; i < hash_table_length; ++i)
printf("hash_table[%d] = %d\n",i,*start++);
return 0;
}
I have a dynamic 2d array inside this struct:
struct mystruct{
int mySize;
int **networkRep;
};
In my code block I use it as follows:
struct myStruct astruct[100];
astruct[0].networkRep = declareMatrix(astruct[0].networkRep, 200, 200);
// do stuff...
int i;
for(i=0; i<100; i++)
freeMatrix(astruct[i].networkRep, 200);
This is how I declare the 2d array:
int** declareMatrix(int **mymatrix, int rows, int columns)
{
mymatrix = (int**) malloc(rows*sizeof(int*));
if (mymatrix==NULL)
printf("Error allocating memory!\n");
int i,j;
for (i = 0; i < rows; i++)
mymatrix[i] = (int*) malloc(columns*sizeof(int));
for(i=0; i<rows; i++){
for(j=0; j<columns; j++){
mymatrix[i][j] = 0;
}
}
return mymatrix;
}
And this is how I free the 2d array:
void freeMatrix(int **matrix, int rows)
{
int i;
for (i = 0; i < rows; i++){
free(matrix[i]);
}
free(matrix);
matrix = NULL;
}
The strange behvior that I'm seeing is that when I compile and run my program everything looks OK. But when I pipe the stdout to a txt file, I'm getting a seg fault. However, the seg fault doesn't occur if I comment out the loop containing the "freeMatrix" call. What am I doing wrong?
I don't see any problem in free code, except, freeMatrix get called for 100 times whereas your allocation is just 1.
So, either you allocate as below:
for(int i=0; i<100; i++) //Notice looping over 100 elements.
astruct[i].networkRep = declareMatrix(astruct[i].networkRep, 200, 200);
Or, free for only 0th element which you have allocated in your original code.
freeMatrix(astruct[0].networkRep, 200);
On sidenote: Initialize your astruct array.
mystruct astruct[100] = {};
struct myStruct astruct[100];
astruct[0].networkRep = declareMatrix(astruct[0].networkRep, 200, 200);
// do stuff...
int i;
for(i=0; i<100; i++)
freeMatrix(astruct[i].networkRep, 200);
You allocated one astruct but free 100 of them; that will crash if any of the 99 extra ones isn't NULL, which probably happens when you do your redirection. (Since astruct is on the stack, it will contain whatever was left there.)
Other issues:
You're using numeric literals rather than manifest constants ... define NUMROWS and NUMCOLS and use them consistently.
Get rid of the first parameter to declareMatrix ... you pass a value but never use it.
In freeMatrix,
matrix = NULL;
does nothing. With optimization turned on, the compiler won't even generate any code.
if (mymatrix==NULL)
printf("Error allocating memory!\n");
You should exit(1) upon error, otherwise your program will crash and you may not even see the error message because a) stdout is buffered and b) you're redirecting it to a file. Which is also a reason to write error messages to stderr, not stdout.
astruct[0].networkRep = declareMatrix(astruct[0].networkRep, 200, 200);
your not passing the address of the pointer. It just passes the value in the memory to the function which is unncessary.
And your only initializing first variable of struct but while you are trying to free the memory you are unallocating memory which is not yet allocated (astruct[1] and so on till 100 ).
When you use a malloc , it actually allocates a bit more memory than you you specified. extra memory is used to store information such as the size of block, and a link to the next free/used block and sometimes some guard data that helps the system to detect if you write past the end of your allocated block.
If you pass in a different address, it will access memory that contains garbage, and hence its behaviour is undefined (but most frequently will result in a crash)
To index and count an unsigned integer type is enough. size_tis the type of choice for this as it is guaranteed to be larger enough to address/index every byte of memory/array's element on the target machine.
struct mystruct
{
size_t mySize;
int ** networkRep;
};
Always properly initialise variables:
struct myStruct astruct[100] = {0};
Several issues with the allocator:
Give it a chance to returned specific error codes. This typically is done by setting using the function returned value to to so.
Use size_t for counters and indicies and sizes ("rows", "columns")(for why please see above).
Do proper error checking.
Clean up in case an error occurs during work.
do not cast the value returned by malloc(), as in C it's not necessary, not recommended
Use perror() to log error, as it gets the most from the OS about the as possibe.
A possible to do this:
int declareMatrix(int *** pmymatrix, size_t rows, size_t columns)
{
int result = 0; /* Be optimistc. */
assert(NULL != pmatrix);
*pmymatrix = malloc(rows * sizeof(**pmymatrix));
if (NULL == *pmymatrix)
{
perror("malloc() failed");
result = -1;
goto lblExit;
}
{
size_t i, j;
for (i = 0; i < rows; i++)
{
(*pmymatrix)[i] = malloc(columns * sizeof(***pmymatrix));
if (NULL == (*pmymatrix)[i])
{
perror("malloc() failed");
freeMatrix(pmymatrix); /* Clean up. */
result = -1;
goto lblExit;
}
for(i = 0; i < rows; ++i)
{
for(j = 0; j < columns; ++j)
{
(*pmymatrix)[i][j] = 0;
}
}
}
}
lblExit:
return 0;
}
Two issues for the de-allocator:
Mark it's work as done be properly de-initilaising the pointer.
Perform validation of input prior to acting on it.
A possible to do this:
void freeMatrix(int *** pmatrix, size_t rows)
{
if (NULL != pmatrix)
{
if (NULL != *pmatrix)
{
size_t i;
for (i = 0; i < rows; ++i)
{
free((*pmatrix)[i]);
}
}
free(*pmatrix);
*pmatrix = NULL;
}
}
Then use the stuff like this:
struct myStruct astruct[100] = {0};
...
int result = declareMatrix(&astruct[0].networkRep, 200, 200);
if (0 != result)
{
fprintf("declareMatrix() failed.\n");
}
else
{
// Note: Arriving here has only the 1st element of astruct initialised! */
// do stuff...
}
{
size_t i;
for(i = 0; i < 100; ++i)
{
freeMatrix(&astruct[i].networkRep, 200);
}
}
So I'm having a problem where I have an array of char*s (declared as) char** where the array is dynamically allocated (by calloc), but the char*s within it are static.
This works fine for me until I attempt to free the array (during resizing), at which point I get
*** glibc detected *** ./hash_table_test: free(): invalid next size (normal): 0x0891f028 ***
I tried setting all of the ptrs in the array to NULL, but then I got the error
*** glibc detected *** ./hash_table_test: double free or corruption (!prev): 0x0815b028 ***
Here is the relevant code:
Table struct:
struct string_hash_table {
//Array of c-strings
char** table;
//number of elements in the table
int num_elements;
//size of table
int table_size;
//Primes
int *primes;
//Current position in primes array
int primes_index;
//the size of the primes array
int primes_size;
};
//TypeDefs--------------------------------
typedef struct string_hash_table HashTable;
Rehash Function (source of errors)
void rehash_string(HashTable *table) {
int prev_size = table->table_size;
int i;
table->table_size = table->table_size * 2;
//create new array
char** new_table = calloc(table->table_size, sizeof(char*));
printf("new table created\n");
int index;
printf("before loop prev_size is %d\n", prev_size);
//add all elements to new_table
for (i = 0; i < prev_size; i++) {
printf("on %d\n", i);
index = find_spot_string(new_table, table->table_size, table->table[i]);
printf("after find_spot_string\n");
if (index != -1) {
table->table[index] = table->table[i];
}
}
//free and swap
printf("before free\n");
empty_string_array(table->table, table->table_size);
free(table->table);
table->table = new_table;
Initialization of HashTable struct:
//Takes a HashTable and initializes it
void init_hash_table(HashTable *table) {
table->primes_index = 0;
table->num_elements = 0;
table->primes_size = 297;
table->primes = prime_list;
table->table_size = table->primes[0];
table->table = calloc(table->table_size, sizeof(char*));
}
declarations of static strings within:
char* temp = "hello";
add_hash_table_string(table, temp);
temp = "luck";
add_hash_table_string(table, temp);
temp = "stuck";
add_hash_table_string(table, temp);
temp = "buck";
add_hash_table_string(table, temp);
temp = "muck";
add_hash_table_string(table, temp);
temp = "much";
add_hash_table_string(table, temp);
Currently I'm just testing my code here, everything works except for the rehashing function above. Anyone have any ideas? or leads I should follow?
EDIT: adding code for add_hash_table_string
void add_hash_table_string(HashTable *table, char* element) {
//if non-null element, and element is not in the HashTable
if (element != NULL && contains_hash_table_string(table, element) == 1) {
//if the table is full
if (table->table_size / 2 < table->num_elements) {
rehash_string(table);
}
int index = find_spot_string(table->table, table->table_size, element);
table->table[index] = element;
table->num_elements++;
}
}
EDIT2:
forgot to be precise, the error is occurring at the line with free(table->table) in the rehash function
One possible problem, you free the old table with the new size
empty_string_array(table->table, table->table_size);
Another one might be
index = find_spot_string(new_table, table->table_size, table->table[i]);
printf("after find_spot_string\n");
if (index != -1) {
table->table[index] = table->table[i];
If this is supposed to copy the entries to the new_table, it doesn't AFAICS. When index is greater than prev_size, you write beyond the end of table.
I'm trying to find a memory leak in the folowing code. valgrind gives me this:
==14160== 1,850 (592 direct, 1,258 indirect) bytes in 9 blocks are definitely lost in loss record 2 of 5
==14160== at 0x4904A06: malloc (vg_replace_malloc.c:149)
==14160== by 0x405B1F: tsCreate (ticket_set.c:55)
==14160== by 0x401ECA: test1TS (main.c:62)
==14160== by 0x40557C: main (main.c:424)
and here's the function:
TicketSetStatus tsCreate(TicketSet* t, int n, int c) {
if(t==NULL){
return TS_CANNOT_CREATE;
}
if (n <= 0){
return TS_ILLEGAL_PARAMETER;
}
t->usedTravels = 0;
t->originalTravels = n;
t->cost = c;
t->moneyLeft = n * c;
//Date time is array of travels:
t->dates = malloc(sizeof(DateTime *)* (n)); //todo maybe c99 allows dynamic arrays?
for (int i = 0; i < n; i++) {
t->dates[i] = malloc(sizeof(char)*GOOD_LENGTH+1);
if (t->dates[i] == NULL) {
free( t->dates);
return TS_CANNOT_CREATE;
}
}
return TS_SUCCESS;
}
TicketSetStatus tsDestroy(TicketSet* t, int* moneyLeft) {
if (t == NULL) {
return TS_FAIL;
}
*moneyLeft = (t->cost) * (t->originalTravels-t->usedTravels);
for (int i = 0; i < t->originalTravels; i++){
free(t->dates[i]);
}
free(t->dates);
t=NULL;
return TS_SUCCESS;
}
when the struct is:
struct TS_element {
int usedTravels;
int originalTravels;
int cost;
DateTime* dates;
int moneyLeft;
};
and
typedef char* DateType
actually playing with free crashes the program more often than not so i'm inclined to live with the memory leak as long as the program functions correctly.
How are you using this array of DateTime? If you are stomping on the values later you will get leaks. Perhaps a confusion about string assignment? ie
char someDateValue[] = "2012-08-15";
t->dates[0] = someDateValue; // Leak -- your allocated string is lost
Instead:
strcpy( t->dates[0], someDateValue );
There is a definite leak in your error condition in tsCreate:
for (int i = 0; i < n; i++) {
t->dates[i] = malloc(sizeof(char)*GOOD_LENGTH+1);
if (t->dates[i] == NULL) {
free(t->dates); // Leak -- every element up to i-1 is lost
return TS_CANNOT_CREATE;
}
}
Are you calling tsDestroy after you've finished with data initialised by tsCreate? Perhaps you're returning from main without cleaning up.
If none of this helps, you should post additional code to show how you are using your data structure.
For at least one error you can focus solely on
...
t->dates = malloc(sizeof(DateTime*) * (n)); /* first malloc */
for (int i = 0; i < n; i++) { /* call this loop 1 */
t->dates[i] = malloc(sizeof(char)*GOOD_LENGTH+1); /* second malloc */
if (t->dates[i] == NULL) { /* test for malloc error */
free( t->dates); /* free the base array/list */
return TS_CANNOT_CREATE; /* exit function */
}
}
...
The problem is if the second malloc fails, the free only frees the base (first) malloc. It does not free any other memory allocations created by the second malloc
in loop 1, on a previous loop 1 iteration. I.e. if t->dates[i] = malloc(... fails when i is equal to 5 then the memory blocks allocated in the iterations 0 to 4 are not freed before exiting the function.
Hopefully that makes sense.
Update #paddy is correct in noting the error of t->dates[0] = someDateValue
which in this case what that is saying is:
char someDateValue[] = "2012-08-15";
could also be written in this case as
char *someDateValue = "2012-08-15";
so that
t->dates[0] = someDateValue;
simply assigns the pointer of the string, replacing the pointer to the freshly allocated block in the preceding malloc.
Ref: If you are still confused you can read the C FAQ question 6.3 So what is meant by the ``equivalence of pointers and arrays'' in C? as well as the rest of the C FAQ.
And is correct to suggest str[n]cpy (or similar replacements) to copy the array contents (rather than its pointer) to the freshly allocated memory block.