I'm creating a table with linked lists where the data is duplicated when it is passed to the insertion methods.
To test this, I create an array with the values that I will insert and then insert them into the table. When I free the array and then free the table, I receive a SEG FAULT error. As such, I have concluded that the pointers in both structures must be pointing to the same memory region. However, as I duplicate the data, I cannot see where the problem could be coming from...
Here is the code for the test:
for(i=0; i<1024; i++) {
key[i] = (char*)malloc(16*sizeof(char));
sprintf(key[i],"a/key/b-%d",i);
data[i] = data_create2(strlen(key[i])+1,strdup(key[i]));
table_put(table,key[i],data[i]);
}
assert(table_size(table) == 1024);
result = (table_size(table) == 1024);
for(i=0; i<1024; i++) {
d = table_get(table,key[i]);
assert(d->datasize == data[i]->datasize);
assert(memcmp(d->data,data[i]->data,d->datasize) == 0);
assert(d->data != data[i]->data);
result = result && (d->datasize == data[i]->datasize &&
memcmp(d->data,data[i]->data,d->datasize) == 0 &&
d->data != data[i]->data);
data_destroy(d);
}
for(i=0; i<1024; i++) {
free(key[i]);
//data_destroy(data[i]);
}
table_destroy(table);
When I uncomment that data_destroy(data[i]) line, the program gives the Seg Fault.
The code for the table_put:
int table_put(struct table_t *table, char * key, struct data_t *value) {
if(table == NULL || key == NULL || value == NULL) return -1;
struct entry_t *new_pair = entry_create(key, value);
int i = key_hash(key, table->size);
int l = 0;
if (list_get(table->list[i], new_pair->key) == NULL) {
l = 1;
}
if(list_add(table->list[i], new_pair)==-1){
entry_destroy(new_pair);
return -1;
}
table -> length = table -> length + l;
return 0;
}
The code for: entry_create, where I duplicate the data:
struct entry_t *entry_create(char *key, struct data_t *data){
if(data == NULL || key == NULL){
return NULL;
}
struct entry_t *entry = (struct entry_t *) malloc(sizeof(struct entry_t));
if(entry == NULL){
return NULL;
}
entry->key = (char*) malloc(sizeof(strlen(key))+1);
memcpy(entry->key,key,strlen(key)+1);
entry->value = data_dup(data);
//free(key);
data_destroy(data);
return entry;
}
segfaults on free are usually double free's means that you've tried to free something that's already been free'd . or when you try to free pointer with out any malloc.
is there any malloc on data_create2 function ?
if there is not it is the case you are trying to free data array members that where never malloc'd
Related
Goal is to read a web page, store all words in a trie with each node containing one letter and a count of the number of characters, print the words and number of occurrences. I keep getting a segmentation fault and I think the issue is in one of these functions. Thanks!
struct trieNode *indexPage(const char *url) {
if (url == NULL) {
return NULL;
printf("Web link must be provided.");
}
//get text from page and check return value
char *page = NULL;
int bytesRead = getText(url, page, MAX_BUFFER_SIZE);
if (page == NULL) {
printf("Page could not be indexed.");
return NULL;
}
//index buffer into separate words
int i = 0;
char *word = NULL;
struct trieNode *node = malloc(sizeof(struct trieNode));
if (node == NULL) {
printf("Node memory could not be allocated.");
return NULL;
}
while (i < bytesRead) {
while (isalpha(page[i])) {
word[i] = page[i];
}
addWordOccurrence(word, sizeof(word), i);
i++;
}
return node;
}
//Create space for node in heap and add to trie structure
int addWordOccurrence(const char* word, const int wordLength, int index) {
if (word == NULL)
return -1;
//allocate memory for new node
struct trieNode *node = malloc(sizeof(struct trieNode));
if (node == NULL) {
printf("Node memory could not be allocated.");
return -2;
}
//recursively add characters to trie and
//increase count
if (index < wordLength) {
setNodeData(node->child[index], word[index]);
node->count++;
}
addWordOccurrence(word, wordLength, index + 1);
return 0;
}
Using gdb I found the fault may be coming from the print function, possibly when trying to access pointers.
//Prints contents
void printTrieContents(struct trieNode *root) {
//if child is found with a non zero count
//add child character to string
char *word = NULL;
for (int i = 0; i < SIZE; i++) {
if ((root->count) && (root->child[i])) {
word[i] = i + 'a';
printTrieContents(root->child[i]);
}
}
if (root->child == NULL) {
printf("%s: %d", word, root->count);
}
}
There are multiple issues:
in indexPage, while (isalpha(page[i])) { word[i] = page[i]; } is potentially an infinite loop.
in printTrieContents, word[i] = i + 'a' dereferences a null pointer as word is never allocated.
addWordOccurrence always recurses, even after reaching the last character. There is no need for recursion, use a loop and a proper test.
more algorithmic issues: the code needs a lot a work.
superficially, it looks like addWordOccurrence(word, sizeof(word), i); should be addWordOccurrence(word, sizeof(word), 0); - the last parameter being the index of each letter that is handled in the recursion.
I am trying to add memory deallocations to old C code.
I have a hash table of custom objects (HASHREC). After analysis of current code and reading other SO questions, I know that I need to provide three levels of deallocations. Fist - word member, next HASHREC*, and then HASHREC**.
My version of free_table() function frees mentioned objects. Unfortunately, Valgrind still complains that some bytes are lost.
I am not able to provide full code, it will be too long, but I am presenting how HASHREC **vocab_hash is filled inside inithashtable() and hashinsert().
Could you give me a suggestion how should I fix free_table()?
typedef struct hashrec {
char *word;
long long count;
struct hashrec *next;
} HASHREC;
HASHREC ** inithashtable() {
int i;
HASHREC **ht;
ht = (HASHREC **) malloc( sizeof(HASHREC *) * TSIZE );
for (i = 0; i < TSIZE; i++) ht[i] = (HASHREC *) NULL;
return ht;
}
void hashinsert(HASHREC **ht, char *w) {
HASHREC *htmp, *hprv;
unsigned int hval = HASHFN(w, TSIZE, SEED);
for (hprv = NULL, htmp = ht[hval]; htmp != NULL && scmp(htmp->word, w) != 0; hprv = htmp, htmp = htmp->next);
if (htmp == NULL) {
htmp = (HASHREC *) malloc( sizeof(HASHREC) ); //<-------- problematic allocation (Valgrind note)
htmp->word = (char *) malloc( strlen(w) + 1 );
strcpy(htmp->word, w);
htmp->next = NULL;
if ( hprv==NULL ) ht[hval] = htmp;
else hprv->next = htmp;
}
else {/* new records are not moved to front */
htmp->count++;
if (hprv != NULL) { /* move to front on access */
hprv->next = htmp->next;
htmp->next = ht[hval];
ht[hval] = htmp;
}
}
return;
}
void free_table(HASHREC **ht) {
int i;
HASHREC* current;
HASHREC* tmp;
for (i = 0; i < TSIZE; i++){
current = ht[i];
while(current != NULL) {
tmp = current;
current = current->next;
free(tmp->word);
}
free(ht[i]);
}
free(ht);
}
int main(int argc, char **argv) {
HASHREC **vocab_hash = inithashtable();
// ...
hashinsert(vocab_hash, w);
//....
free_table(vocab_hash);
return 0;
}
I assume the problem is here:
current = ht[i];
while(current != NULL) {
tmp = current;
current = current->next;
free(tmp->word);
}
free(ht[i]);
You release the word but you don’t release tmp. After you release the first item in the linked list but not the others which causes a leak.
Free tmp in there and don’t free ht[i] after since it’s already freed here.
current = ht[i];
while(current != NULL) {
tmp = current;
current = current->next;
free(tmp->word);
free(tmp);
}
I'm trying to setup a graph in C. I tried the graph with user input and it works perfectly. However, i am trying to implement a read from file. The last else statement is where the error is coming from because when i commented it out it compiles without any problems. I have included a comment over the block i think that has the problem. Please let me know if there is anything else needed for this question.
#include <stdio.h>
#include <stdlib.h>
struct node{
int data;
struct node* next;
};
//int counter and mainVertex would be used to determine if graph is connected.
// void graphConnection(){
//
//
//
//
//
//
// }
char* deblank(char* input)
{
int i,j;
char *output=input;
for (i = 0, j = 0; i<strlen(input); i++,j++)
{
if (input[i]!=' ')
output[j]=input[i];
else
j--;
}
output[j]=0;
return output;
}
struct node *G[1000];
int counter = 0;
char *mainVertex;
void readingEachLine(){
FILE * fp;
char * line = NULL;
size_t len = 0;
ssize_t read;
//Read file and exit if fail
fp = fopen("test.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
while ((read = getline(&line, &len, fp)) != -1) {
line = deblank(line);
int i = 0;
struct node* cursor = malloc(sizeof(struct node));
struct node* secondcursor = malloc(sizeof(struct node));
struct node* tempitem;
while(line[i] != '\n'){
//If its the first of the line look into the array and set struct cursor to the corresponding
//array position
if (i == 0){
mainVertex[counter] = line[0];
int convertor = line[i] - '0';
cursor = G[convertor];
counter++;
}
//if its not the first, then set a struct with that number as data
else{
tempitem = malloc(sizeof(struct node));
int convertor = line[i] - '0';
tempitem->data = convertor;
tempitem->next = NULL;
}
//if there is no element connected to the struct in array, connect the tempitem
if (cursor->next == NULL){
cursor->next = tempitem;
}
//If there are already connected elements, loop until the end of the linked list
//and append the tempitem
//ERROR: I GET SEGMENTATION FAULT FROM HERE. TRIED AFTER COMMENTING IT OUT
else{
secondcursor = cursor;
while(secondcursor->next != NULL){
secondcursor = secondcursor->next;
}
secondcursor->next = tempitem;
}
i++;
}
printf("\n");
}
}
int main(void){
for (int i = 1; i < 1000; i++)
{
G[i]= malloc(sizeof(struct node));
G[i]->data = i;
G[i]->next = NULL;
}
readingEachLine();
}
EDIT: This is how the text file looks like:
1 3 4
2 4
3 1 4
4 2 1 3
Your code has several misconceoptions:
Apparently, you can have a maximum of 1,000 nodes. You have an array G of 1,000 head pointers to linked lists. Don't allocate memory for all 1,000 nodes at the beginning. At the beginning, all lists are empty and an empty linked list is one that has no node and whose head is NULL.
In your example, cursor is used to iterate oer already existing pointers, so don't allocate memory for it. If you have code like this:
struct node *p = malloc(...);
// next use of p:
p = other_node;
you shouldn't allocate. You would overwrite p and lose the handle to the allocated memory. Not all pointers have to be initialised with malloc; allocate only if you create a node.
Your idea to strip all spaces from a line and then parse single digits will fail if you ever have more then 9 nodes. (But you cater for 1,000 node.) Don't try to parse the numbers yourself. There are library functions for that, for example strtol.
It is not clear what mainVertex is supposed to be. You use it only once, when you assign to it. You treat it like an array, but it is a global pointer, initialised to NULL. When you dereference it, you get undefined behaviour, which is where your segmentation fault probably comes from.
Here's a program that does what you want to do. (It always inserts nodes at the head for simplicity and it should have more allocation checks.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum {
maxNodes = 1000
};
struct node{
int data;
struct node* next;
};
struct node *G[maxNodes];
size_t nnode = 0;
int read_graph(const char *fn)
{
FILE * fp;
char * line = NULL;
size_t len = 0;
fp = fopen(fn, "r");
if (fp == NULL) return -1;
while (getline(&line, &len, fp) != -1) {
char *p;
char *end;
int id;
int n;
id = strtol(line, &end, 10);
if (end == line) continue;
if (id < 1 || id > maxNodes) break;
if (id > nnode) nnode = id;
id--;
p = end;
n = strtol(p, &end, 10);
while (p != end) {
struct node *nnew = malloc(sizeof(*nnew));
nnew->data = n - 1;
nnew->next = G[id];
G[id] = nnew;
p = end;
n = strtol(p, &end, 10);
}
}
fclose(fp);
free(line);
return 0;
}
int main(void)
{
if (read_graph("test.txt") < 0) {
fprintf(stderr, "Couldn't gread raph.\n");
exit(1);
}
for (int i = 0; i < nnode; i++) {
struct node *p = G[i];
if (p) {
printf("%d:", i + 1);
for (; p; p = p->next) {
printf(" %d", p->data + 1);
}
puts("");
}
}
for (int i = 0; i < nnode; i++) {
struct node *p = G[i];
while (p) {
struct node *old = p;
p = p->next;
free(old);
}
}
return 0;
}
I am assigning an int pointer a value pulled from sscanf. I then want to pass it in to a method from a different file, counters_add. Although I can print out the value stored in the pointer and its address, as soon as I pass it to this method the program throws a seg fault. From testing I know that the program does not even get inside this method before seg faulting.
This method takes parameters of (counters_t *ctrs, const int key). The counters_t object is a struct I have defined earlier in the file.
I don't prematurely free anything and have verified that neither ctrs nor key are NULL. Why am I getting a segmentation fault?
int *key = malloc(sizeof(int));
//check if key is null here
sscanf(line, "%i", key);
printf("key: %i\n", *key); //this prints out the value
printf("key: %p\n", (void *)key); //this prints out the address
counters_add(ctrs, *key);//seg fault here, without even getting inside of method
Initialization of ctrs:
counters_t *ctrs = count_malloc(sizeof(counters_t));
if (ctrs == NULL) {
return NULL; // error allocating set
} else {
// initialize contents of set structure
ctrs->head = NULL;
}
The rest of the code:
void
counters_add(counters_t *ctrs, const int key)
{
if (key >= 0 && ctrs != NULL) {
// allocate a new node to be added to the list if the key is not already in the set
if(counters_get(ctrs,key) == 0) {//if it doesnt already exist
printf("after first if statement");
counternode_t *new = counternode_new(&key);//create it
printf("aftermaking new node");
new->next = ctrs->head;//add it to the head of the list
ctrs->head = new;
} else {
// increment the count
for(counternode_t *curr = ctrs->head; curr != NULL; curr = curr->next){
if (*(curr->key) == key){
*(curr->count) = *(curr->count) + 1;
}
}
}
}
}
int
counters_get(counters_t *ctrs, const int key)
{
printf("in counters_get");
if (ctrs == NULL) {
return 0; // null counter
} else if (ctrs->head == NULL) {
return 0; // set is empty
}//remove this in set
else {
for(counternode_t *curr = ctrs->head; curr != NULL; curr = curr->next)
{
if (*(curr->key) == key)
return *(curr->count);
printf("in loop");
}
return 0;
}
}
static counternode_t // not visible outside this file
*counternode_new(const int *key)
{
counternode_t *node = count_malloc(sizeof(counternode_t));
int *newkey = (int*)malloc(sizeof(int));
newkey = (int*) memcpy(newkey, key, (50 * sizeof(char)));
//make sure key is not over 50 ints
if (node == NULL || newkey == NULL) {
// error allocating memory for node or new key; return error
return NULL;
} else {
node->key = newkey;
*(node->count) = 1;
node->next = NULL;
return node;
}
}
Here is the counters struct:
typedef struct counters {
struct counternode *head; // head of the list of items in set
} counters_t;
here is the countersnode:
typedef struct counternode {
int *key;
int *count; //pointer to counter for this node
struct counternode *next; // link to next node
} counternode_t;
I see the problem in counternode_new:
static counternode_t // not visible outside this file
*counternode_new(const int *key)
{
counternode_t *node = count_malloc(sizeof(counternode_t));
int *newkey = (int*)malloc(sizeof(int));
newkey = (int*) memcpy(newkey, key, (50 * sizeof(char)));
...
}
In counters_add you pass to counternode_new a pointer to the variable d.
Then in counternode_new you want to copy in newkey 50 bytes with key as
the source. But key is pointer to a single integer, so you are reading 49
bytes out of bound, this leads to undefined behaviour which may lead to a
segfault. Also you are only allocating space for a single int for newkey. Besides, you are copying 50 bytes, not 50 integers. I don't get where the 50 comes from.
So your counternode_new call in counters_add makes no sense, first you have to
allocate space for a int[50] array and pass that to counternode_new.
I notice that newkey is having only sizeof(int) allocated, whereas you seem to copying 50 bytes to newkey.
int *newkey = (int*)malloc(sizeof(int));
newkey = (int*) memcpy(newkey, key, (50 * sizeof(char)));
Not sure why I'm getting a segmentation fault here. I'm trying to create a hash table that would have collisions and the data should be string. So I'm using a char array for the data.
Need to detect collisions at the end.
#define SIZE 20
struct DataItem
{
char data[50];
int key;
};
struct DataItem* hashArray[SIZE];
struct DataItem* dummyItem;
struct DataItem* item;
int hashCode(int key)
{
return key % SIZE;
}
void insert(int key, char data[50])
{
struct DataItem *item = (struct DataItem*) malloc(sizeof(struct DataItem));
strcpy(item->data, data);
item->key = key;
//get the hash
int hashIndex = hashCode(key);
//move in array until an empty or deleted cell
while (hashArray[hashIndex] != NULL && hashArray[hashIndex]->key != -1)
{
//go to next cell
++hashIndex;
//wrap around the table
hashIndex %= SIZE;
}
hashArray[hashIndex] = item;
}
struct DataItem* delete(struct DataItem* item)
{
int key = item->key;
//get the hash
int hashIndex = hashCode(key);
//move in array until an empty
while (hashArray[hashIndex] != NULL)
{
if (hashArray[hashIndex]->key == key)
{
struct DataItem* temp = hashArray[hashIndex];
//assign a dummy item at deleted position
hashArray[hashIndex] = dummyItem;
return temp;
}
//go to next cell
++hashIndex;
//wrap around the table
hashIndex %= SIZE;
}
return NULL;
}
int detect_collisions()
{
int i = 0;
int collision = 0;
for (i = 0; i < SIZE; i++)
{
if (hashArray[i] != NULL)
{
if (hashArray[i]->key == hashArray[i + 1]->key)
;
collision++;
}
}
return collision;
}
void display()
{
int i = 0;
for (i = 0; i < SIZE; i++)
{
if (hashArray[i] != NULL)
{
printf("Phli dafa: %d\n", i);
printf(" (%d,%s)", hashArray[i]->key, hashArray[i]->data);
}
else
printf(" ~~ ");
}
printf("\n");
}
int main()
{
dummyItem = (struct DataItem*) malloc(sizeof(struct DataItem));
strcpy(dummyItem->data, NULL);
dummyItem->key = -1;
insert(1, "check");
display();
delete(item);
}
You have a problem here:
strcpy(dummyItem->data,NULL);
as strcpy will dereference a NULL pointer.
To put in an empty string use:
strcpy(dummyItem->data, "");
Your second problem is:
delete(item);
item is NULL at this point. So here:
struct DataItem* delete(struct DataItem* item)
{
int key = item->key;
^^^^^^
you dereference a NULL pointer.
Maybe your delete function should rather be:
struct DataItem* delete(int key)
{
The third problem is here (there are actually 3 problems here):
for (i = 0; i < SIZE; i++)
{
if (hashArray[i] != NULL)
{
if (hashArray[i]->key == hashArray[i + 1]->key)
^^^^^^^^^^^^^^^^
a) May be NULL
b) Out of range when i == size-1
;
^^^
c) Delete this
collision++;
}
}
My guess is that you want:
for (i = 0; i < SIZE-1; i++) // Notice
{
if (hashArray[i] != NULL && hashArray[i + 1] != NULL) // Notice
{
if (hashArray[i]->key == hashArray[i + 1]->key)
{
collision++;
}
}
}
The problem is
strcpy(dummyItem->data, NULL);
It will try to copy a string pointed by NULL (that is not pointing a string..) to your array. So it invokes Undefined Behavior
What you want (I guess) is
memset(dummyItem->data, 0x00, sizeof(dummyItem->data));
or
dummyItem->data[0] = 0x00;
or
dummyItem->data[0] = '\0';
Take also note that you must always check malloc return value: it can fail returning NULL
dummyItem = malloc(sizeof(struct DataItem));
if ( dummyItem != NULL)
{
// YOUR STUFF
}
You have also a ; just after
if (hashArray[i]->key == hashArray[i + 1]->key)
remove it.
Last thing I can see
delete(item);
is UB because of item is not initialized because the code in insert function uses a local variable, maybe you want change
struct DataItem *item = (struct DataItem*) malloc(sizeof(struct DataItem));
to
item = malloc(sizeof(struct DataItem));
EDIT 1
You are also accessing the array out of bounds with
if (hashArray[i]->key == hashArray[i + 1]->key)
hashArray[i + 1] is out of bounds when i = SIZE-1
Moreover hashArray[i + 1] can be NULL or not initialized.
If you compile your code with debugging turned on ( ie -g option with gcc ) and then run it in a debugger it will tell you what lines the seg faults are happen on. There are two places that are causing a seg fault that I can see - the strcpy being passed a NULL and also the call to delete(item) where item is still NULL. Presumably because you think you're using the one defined in insert rather than the globally defined one.