freeing a dynamic array of static strings - c

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.

Related

Realloc does not work (following the instruction in cs50 lecture 5)

I'm going to add a new value 4 to the list array.
The original values in list array are 1,2,3.
But when i run the following code, i didn't get 1,2,3,4 but several random numbers.
Each time I run I get different output.
Can someone help me figure out what's going wrong here?
Thanks a lot.
#include <stdio.h>
#include <stdlib.h>
int main(void){
int *list = malloc(3 * sizeof(int));
//如果直接写int list[3] 就没有办法修改大小了
if (list == NULL)
{
free(list);
return 1;
}
list[0] = 1;
list[1] = 2;
list[2] = 3;
//resize the old array to be of size 4
//用realloc指定下old array,无需再做copy的工作
int *tmp = realloc(list, 4 * sizeof(int));
if (list == NULL)
{
free(list);
//a safety check, free the original list
return 1;
}
tmp[3] = 4;
//free old array
free(list); //这里就可以free之前的list了
//remember new array
list = tmp;
//所以不需要在free(tmp), free(list)相当于free(tmp)
//print new array
for (int i = 0; i < 4; i++)
{
printf("%i\n", list[i]);
}
//free new array
free(list);
return 0; //最后记得加上这个
}
the output is like this:
1609039888
25764
2043
4
try this code
#include <stdio.h>
#include <stdlib.h>
int main(void){
int *list = malloc(3 * sizeof(int));
//如果直接写int list[3] 就没有办法修改大小了
if (list == NULL)
{
free(list);
return 1;
}
list[0] = 1;
list[1] = 2;
list[2] = 3;
//resize the old array to be of size 4
//用realloc指定下old array,无需再做copy的工作
int *tmp = realloc(list, 4 * sizeof(int));
if (list == NULL)
{
free(list);
//a safety check, free the original list
return 1;
}
tmp[3] = 4;
//free old array
//free(list); //这里就可以free之前的list了 // this free the last memory location which empty the array.
//remember new array
list = tmp;
//所以不需要在free(tmp), free(list)相当于free(tmp)
//print new array
for (int i = 0; i < 4; i++)
{
printf("%i\n", list[i]);
}
//free new array
free(list);
return 0; //最后记得加上这个
}
There's a fundamental misunderstanding on how dynamic memory management actually works...
At first, if an allocation fails (malloc returning a null pointer), then there's nothing to free anyway, so you simply don't need to (even though it's legal – then effectively a no-op...).
int* list = malloc(...);
if(!list) // shorter for list == NULL
{
return -1;
}
Then realloc replaces the old array for you already! You can imagine it to work like this:
void* realloc(void* oldData, size_t desired)
{
// retain currently allocated memory size from pointer
// that's OS/compiler specific knowledge, usually stored somewhere
// in front of the memory the pointer points to, but not (legally)
// accessible by you...
size_t old = ...;
if(desired <= old)
{
return oldData;
}
void* newData = malloc(desired);
if(newData)
{
memcpy(newData, oldData, oldSize);
free(oldData); // !!!
}
return newData;
}
Note how the old data remains intact if re-allocation fails, but gets deleted on success!
Correct usage of realloc thus looks as follows:
int* tmp = realloc(list, desiredSize);
if(!tmp)
{
// appropriate error handling
// usually you cannot meaningfully go on anyway, so let's just exit
// but HERE list still points to valid memory, so clean up first:
free(list);
return -1;
}
// we can safely use the temporary as the list now; note that the old
// memory already HAS been deleted!
list = tmp;
// and now we simply use it:
list[3] = 4;
free(list); // when done

How to fix "Pointer being realloc'd was not allocated" error

I have a structure called House and it includes a char array for neighborhood. I am trying to read a database of houses and create an array for the houses with the same neighborhood. Reading part is working fine but while I'm creating a new array for a neighborhood my function only enters the loop for 23 times and finds first 23 houses with the same neighborhood and gives this error.
pro(4740,0x10d06f5c0) malloc: * error for object 0x7ff724d00030: pointer being realloc'd was not allocated
pro(4740,0x10d06f5c0) malloc: * set a breakpoint in malloc_error_break to debug
typedef struct house{
int id;
int lotarea;
char street[5];
int saleprice;
char neighborhood[10];
int yearbuilt;
int overallqual;
int overallcond;
char kitchenqual[3];
} House;
House* get_neighborhoods(House house, House* array) {
printf("Get neighborhoods of house with id %d\n", house.id);
House* temp = array;
int counter = 1;
House* newarray = malloc(sizeof(House));
if (newarray == NULL) {
printf("Malloc error...");
}
while (temp != NULL) {
if (!strcmp(temp->neighborhood, house.neighborhood)) {
if (counter > 1) {
realloc(newarray, sizeof(House) * counter);
}
copy_house(newarray + counter - 1, temp);
print_house(newarray[counter - 1]);
counter++;
}
temp++;
}
return newarray;
}
You must assign the return value from realloc. You have
realloc(newarray, sizeof(House) * counter);
The second time you call it (in the loop), the pointer you previously passed to it has already been freed, but you discarded the new value.
newarray = realloc(newarray, sizeof(House) * counter);
Ideally you would assign the return value to a temporary variable, so you can check if the realloc succeeded.

Problem freeing memory from multidimensional array in C, free() crashes program

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
}

Creating a dynamically expandable array of memory in C

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;
}

Resize static array in C

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);
}

Resources