I have the task of going through a binary search tree and collecting the keys in every node and storing them in an array. After this, I have to set the last element of this array as NULL. This is all done inside a function, which then returns this array of *char.
I believe the part about collecting the keys and storing them in the array is working well, as I've tested that enough. However, I'm not able to define the last element of the array as NULL correctly. I thought it should be done like this:
list_keys[tree_size(tree)] = NULL;
However, after some printf's, I realized that this was cleaning my tree. When I print tree_size(tree) before this line, it gives me the size of the tree correctly. However, when I do it after that line, it gives me 0. I don't believe that the problem is in the function tree_size() because when I attempt to access an element of the tree before that line, it works well, but after the line, I get a Segmentation fault (core dumped) error, probably due to trying access something that doesn't exist anymore.
I have no idea what's wrong, so any help is appreciated. Thanks in advance.
EDIT:
The tree is of the type tree_t and it's defined as:
struct tree_t {
struct entry_t *entry;
struct tree_t *right_child;
struct tree_t *left_child;
};
The tree_size() is defined as:
int tree_size(struct tree_t *tree) {
if (tree->entry != NULL) {
//if two children exist
if (tree->left_child != NULL && tree->right_child != NULL) {
return 1 + tree_size(tree->left_child) + tree_size(tree->right_child);
}
//there's a right child and there isnt a left child
if (tree->right_child != NULL && tree->left_child == NULL) {
return 1 + tree_size(tree->right_child);
}
//there's a left child and there isnt a right child
if (tree->left_child != NULL && tree->right_child == NULL) {
return 1 + tree_size(tree->left_child);
}
//there are no children
if (tree->left_child == NULL && tree->right_child == NULL) {
return 1;
}
}
//if the entry is empty
return 0;
}
Basically, what I'm doing right now is:
First, I define list_keys and allocate memory:
char **list_keys;
list_keys = (char *)malloc((tree_size(tree)+1)*sizeof(char));
Then, I call an auxiliary function tree_get_keys_aux(tree, list_keys, 0) that will do the initial part I mentioned. It is defined as:
void tree_get_keys_aux(struct tree_t *tree, char **list_keys, int index) {
//N
list_keys[index] = (char *)malloc((strlen(tree->entry->key))*sizeof(char)); //allocating memory for each string that I want to add to the list
strcpy(list_keys[index],tree->entry->key); //copying the content
index = index + 1;
//L
if (tree->left_child != NULL) {
tree_get_keys_aux(tree->left_child, list_keys, index);
}
//R
if (tree->right_child != NULL) {
tree_get_keys_aux(tree->right_child, list_keys, index);
}
return;
}
Then, I do the line that's bringing me problems,
list_keys[tree_size(tree)] = NULL;
And lastly,
return list_keys;
Firstly,
list_keys = (char *)malloc((tree_size(tree)+1)*sizeof(char));
is wrong. The elements are char*, so you have to allocate for that or you will cause out-of-range access.
It should be:
list_keys = malloc((tree_size(tree)+1)*sizeof(char*));
or
list_keys = malloc((tree_size(tree)+1)*sizeof(*list_keys));
See also: c - Do I cast the result of malloc? - Stack Overflow
Secondly,
list_keys[index] = (char *)malloc((strlen(tree->entry->key))*sizeof(char)); //allocating memory for each string that I want to add to the list
strcpy(list_keys[index],tree->entry->key); //copying the content
is wrong. You have to allocate one more element for terminating null-character.
It should be:
list_keys[index] = malloc((strlen(tree->entry->key) + 1)*sizeof(char)); //allocating memory for each string that I want to add to the list
strcpy(list_keys[index],tree->entry->key); //copying the content
Thirdly, tree_get_keys_aux is not recoginizing number of elements in left child and data from left child will be overwritten by data from right child.
To avoid this overwriting, you can use tree_size to determint the tree size and advance index according to that.
//L
if (tree->left_child != NULL) {
tree_get_keys_aux(tree->left_child, list_keys, index);
index += tree_size(tree->left_child); // add this
}
//R
if (tree->right_child != NULL) {
tree_get_keys_aux(tree->right_child, list_keys, index);
}
Related
I'm trying to do a project to check if the word in a list is repeated, but it seens AuxLista allways returns temp-palavra as null?
void AuxLista(Lista *L, char tmp_word) {
if (!L) return;
Lista *temp = L;
printf("\n");
while (temp != NULL)
{
if(temp->palavra == tmp_word){
printf("%s Está repetida. ", temp->palavra);
}
printf("TESTE %s", temp->palavra);
temp = temp->prox;
}
}
Lista *criarLista(char *word){
Lista *result = malloc(sizeof(Lista));
AuxLista(result, word);
result->palavra = word;
result->prox = NULL;
if (result->NOCORRENCIAS == NULL) result->NOCORRENCIAS = 1;
else result->NOCORRENCIAS = result->NOCORRENCIAS + 1;
return result;
}
List item
Your AuxLista function is void -- it doesn't return anything.
Also, be aware that the result of malloc() is uninitialized memory. If you don't set each byte to a value, then you must assume that the byte has the worst possible value it could have. You might consider using calloc or passing the memory to memset to initialize everything to 0.
And you should check that malloc didn't return NULL, which it will do if you aren't looking.
Finally, don't use NULL to mean 0. Your NOCORRENCIAS appears to be an integer (because you set it to 1) which means you would normally be checking it against 0 rather than NULL. But that whole if/else appears to be wasted -- you could probably just do result->NOCORRENCIAS += 1 and handle the zero/non-zero cases the same.
while I was trying to implement my own (string type) Map, I ran into a problem that causes a segmantation fault, while trying to put data in the "value" corresponds to the key Im trying to update.
this is the declaration of the structs, the map struct is an abstract one, so its pointer is in the header file:
typedef struct KeyValue {
char* key;
char* value;
} *keyValue;
struct Map_t {
keyValue* elements;
int size;
int max_size;
int iterator;
};
this is the function that allocates the memory for all the elements inside the map, and initializing them:
Map mapCreate() {
Map map = malloc(sizeof(*map));
if (map == NULL) {
return NULL;
}
map->elements = malloc(INITIAL_SIZE * sizeof(keyValue));
if (map->elements == NULL) {
free(map);
return NULL;
}
map->size = 0;
map->max_size = INITIAL_SIZE;
map->iterator = 0;
return map;
}
this is the function that put an element to a key - override it if there is already an existing key, or making a new one if needed:
MapResult mapPut(Map map, const char* key, const char* data) {
if (map == NULL || key == NULL || data == NULL) {
return MAP_NULL_ARGUMENT;
}
int index = mapFind(map,key);
char* tmp_key = copyString(key); //making a copy of the const key
char* tmp_value = copyString(data); //making a copy of the const data
if (index != ELEMENT_NOT_FOUND) {
keyValue element = map->elements[index];
element->value = tmp_value; //assigning the requested data to the value corresponds to the key
free(tmp_value);
free(tmp_key);
return MAP_SUCCESS;
}
if (map->size == map->max_size) {
if (expand(map) == MAP_OUT_OF_MEMORY) {
return MAP_OUT_OF_MEMORY;
}
}
return createKeyValue(map, tmp_key ,tmp_value); //creates a new key-value
}
and this is the createKeyValue function:
static MapResult createKeyValue(Map map, char* tmp_key, char* tmp_value) {
// we use this function inside another one that checks for null arguments
assert(map != NULL);
if (tmp_key == NULL || tmp_value == NULL) {
return MAP_OUT_OF_MEMORY;
}
int index = map->size;
keyValue element = map->elements[index];
strcpy(element->key,tmp_key); // segmantation fault here
strcpy(element->value,tmp_value);
free(tmp_key);
free(tmp_value);
map->size++;
return MAP_SUCCESS;
}
Im getting the segmentation fault while this function is trying to access with the strcpy function. I already checked if I allocated memorry correctly, and to me it seems that I did everything I should do.
Im really lost because for 2 days I tried everything and cant find the solution.
You have many problems, some of which are listed in comments.
But the root cause seems to be that you don't actually allocate memory for the struct KeyValue structure objects. That means map->elements[index] will be an indeterminate and invalid pointer, for all possible indexes.
You need to allocate memory for map->elements[index].
A possibly fixed version of the createKeyValue function could look something like:
static MapResult createKeyValue(Map map, char* tmp_key, char* tmp_value) {
// we use this function inside another one that checks for null arguments
assert(map != NULL);
if (tmp_key == NULL || tmp_value == NULL) {
return MAP_OUT_OF_MEMORY;
}
int index = map->size;
// Allocate memory for the structure
map->elements[index] = malloc(sizeof map->elements[index]);
// Make the key and value pointers point to the newly allocated "temporary" strings
map->elements[index]->key = tmp_key;
map->elements[index]->value = tmp_value;
map->size++;
return MAP_SUCCESS;
}
I wrote a piece of code that recursively finds the smallest string in a tree and deletes it. However, printing the tree after deleting the node returns (null).
static char* findMinimum(TreeNodePtr treePtr){
if(treePtr->left == NULL){
printf("Minimum node is %s\n", treePtr->item);
char * temp = treePtr->item;
(treePtr)->item = NULL;
return(temp);
}
else{
findMinimum(treePtr->left);
}
}
I THINK this function works since it deletes the minimum value even using different strings. Should I write a condition to make sure NULL pointers won't get printed? Just in case, here's the print function as well:
static void printTree(TreeNodePtr treePtr) {
if (treePtr != NULL) {
level++;
printTree(treePtr->left);
printf(">%*s%s\n", level*5, "", treePtr->item);
printTree(treePtr->right);
level--;
}
}
I see couple of problems in your findMinimum function:
1) What do you return in else-case in findMinimum? I guess you forgot to add return:
static char* findMinimum(TreeNodePtr treePtr){
if(treePtr->left == NULL){
printf("Minimum node is %s\n", treePtr->item);
char * temp = treePtr->item;
(treePtr)->item = NULL;
free(treePtr->item);
return(temp);
}
else{
return findMinimum(treePtr->left); // added return
}
}
2) Is your tree a binary search tree? Consider what happens in this case:
root-node
/ \
smallest largest
\
not-smallest
You should rehang not-smallest node instead of smallest.
3) Why do you free NULL?
(treePtr)->item = NULL;
free(treePtr->item);
You're deleting the smallest node without changing the child of its parent. If you delete a node, you should also change its parent to point to NULL. Note that setting treePtr->item to NULL doesn't accomplish this as treePtr->left points to a TreeNodePtr, not to its item member.
This function
static char* findMinimum(TreeNodePtr treePtr){
if(treePtr->left == NULL){
printf("Minimum node is %s\n", treePtr->item);
char * temp = treePtr->item;
(treePtr)->item = NULL;
free(treePtr->item);
return(temp);
}
else{
findMinimum(treePtr->left);
}
}
does not make sense.
For starters this part or the code
else{
findMinimum(treePtr->left);
}
returns nothing. So the function already have undefined behavior.
Also consider these statements
(treePtr)->item = NULL;
free(treePtr->item);
The call of free does nothing.
Otherwise if you will exchange the statements when the function returns a pointer to already deleted string. And again the progarm will have undefined behavior.
Also it is unclear how the function will behave when it will be called the second time when the smallest string was already deleted.
And you have to dynamically create a copy of the smallest string that will be returned from the function.
I would suggest the following function implementation (without testing).
static char * findMinimum( TreeNodePtr treePtr )
{
if ( treePtr == NULL || treePtr->item == NULL ) return NULL;
if ( treePtr->left == NULL || treePtr->left->item == NULL )
{
char *s = malloc( strlen( treePtr->item ) + 1 );
strcpy( s, treePtr->item );
free( treePtr->item );
treePtr->item = NULL;
return s;
}
else
{
return findMinimum( treePtr->left );
}
}
EDIT: So, it turns out that 'index' was not being returned to 0. Well then. That fixed one segfault. But still getting a different segfault. Working on it.
node* new_node(void){
node* ptr = malloc(sizeof(node));
for (int i = 0; i<27; i++) {
ptr->next[i] = NULL;
}
return ptr;
}
bool load(const char* dictionary)
{
FILE* dict = fopen(dictionary, "r");
node* ptr = new_node;
char word[LENGTH+1];
int index = 0;
for (int c = fgetc(dict); c!=EOF; c = fgetc(dict)){
if(c!='\n'){
word[index]=c;
index++;
}
else {
for(int x=0; x<=index; x++){
int ch = (word[x] == '\'') ? 26 : tolower(word[x])-'a';
if (ptr->next[ch] == NULL){
ptr->next[ch] = new_node;
}
ptr = ptr->next[ch];
}
ptr->end=true;
}
}
return true;
}
I'm trying to implement a trie data structure for a dictionary but my program seems to segfault somewhere in this function. I can't seem to pin it down even with the help of GDB, so can someone give me a hand?
Node is defined as such:
typedef struct node{
bool end;
struct node* next[27];
} node;
Dictionary file:
a
aaa
aaas
aachen
aalborg
aalesund
aardvark
aardvark's
aardvarks
aardwolf
(...)
You have many issues in your code:
When you allocate memory with malloc, it is uninitialised. initialise it directly after allocating it, so that NULL pointers really are null. (calloc, a cousin of ´malloc´, initialises all memory to zero.)
When you loop over the word, you should nor include index:
for (int x = 0; x < index; x++) ...
When you have found the end of a word, you must reset the index to 0. Otherwise, you will append to the old word and overflow the buffer. (You should probably also enforce the upper bound of ´index´.)
Likewise, when you insert a word into the trie, you must reset your pointer for trie traversal to the trie's root. You need two pointers here: A root node pointer and an auxiliary pointer for traversing the trie.
As is, your trie is local to your function. Return the root node, so that other functions can use the trie, or NULL on failure.
Fix these, and you will have a non-crashing function. (It still leaks memory and may not construct the trie properly.)
node *load(const char *dictionary)
{
FILE *dict = fopen(dictionary, "r");
node *head = calloc(1, sizeof(node));
char word[LENGTH + 1];
int index = 0;
for (int c = fgetc(dict); c != EOF; c = fgetc(dict)) {
if (c != '\n') {
word[index] = c;
index++;
} else {
node *ptr = head;
for (int x = 0; x < index; x++) {
int ch = (word[x] == '\'') ? 26 : tolower(word[x]) - 'a';
if (ptr->next[ch] == NULL) {
ptr->next[ch] = calloc(1, sizeof(node));
}
ptr = ptr->next[ch];
}
ptr->end = true;
index = 0;
}
}
return head;
}
The line:
node* ptr = new_node;
and
ptr->next[ch] = new_node;
are not calling the function, but assigning the address of the function to ptr. Call the function instead.
This problem could have been prevented if compiler warnings: -Wall and -Wextra were enabled.
There is no bounds checking done on the array word. Use the value LENGTH to check if the index is in bounds before using it.
It isn't clear what the if statement inside the for loop is doing. It appears that every time a newline is found the whole array word is added to the tree, but the index isn't reset so the same array is added multiple times. At some point index will point out of bounds causing undefined behavior. You should reset index after you use the array word.
You forgot to reset index to 0 at the beginning of the loop.
You should also use calloc(1, sizeof(node)) instead of malloc(sizeof(node)) to avoid leaving memory uninitialized. I suggest you use valgrind to help you track problems of this kind in your code.
You should filter punctuation\unsupported characters a bit more. Any character outside of [a-z|A-Z|\n|\\] will crash your program because of
int ch = (word[x] == '\'') ? 26 : tolower(word[x])-'a';
if (ptr->next[ch] == NULL){
Given that you open a file, there might be a space somewhere or some unexpected character. You need something like
if(c!='\n'){
int num = (c == '\'') ? 26 : tolower(c)-'a');
if(num >=0 && num < 27)
{
word[index]=c;
index++;
}
}
This block of code reads a dictionary file and stores it in a hashed array. This hashing array uses linked list collision resolution. But, for some incomprehensible reason, the reading stops in the middle. (i'm assuming some problem occurs when linked list is made.) Everything works fine when data is being stored in a empty hashed array element.
#define SIZE_OF_ARRAY 350
typedef struct {
char* key;
int status; // (+1) filled, (-1) deleted, 0 empty
LIST* list;
}HASHED_ARRAY;
void insertDictionary (HASHED_ARRAY hashed_array[])
{
//Local Declaration
FILE* data;
char word[30];
char* pWord;
int index;
int length;
int countWord = 0;
//Statement
if (!(data = fopen("dictionaryWords.txt", "r")))
{
printf("Error Opening File");
exit(1);
}
SetStatusToNew (hashed_array); //initialize all status to 'empty'
while(fscanf(data, "%s\n", word) != EOF)
{
length = strlen(word) + 1;
index = hashing_function(word);
if (hashed_array[index].status == 0)//empty
{
hashed_array[index].key = (char*) malloc(length * sizeof(char));//allocate word.
if(!hashed_array[index].key)//check error
{
printf("\nMemory Leak\n");
exit(1);
}
strcpy(hashed_array[index].key, word); //insert the data into hashed array.
hashed_array[index].status = 1;//change hashed array node to filled.
}
else
{
//collision resolution (linked list)
pWord = (char*) malloc(length * sizeof(char));
strcpy (pWord, word);
if (hashed_array[index].list == NULL) // <====== program doesn't enter
//this if statement although the list is NULL.
//So I'm assuming this is where the program stops reading.
{
hashed_array[index].list = createList(compare);
}
addNode(hashed_array[index].list, pWord);
}
countWord++;
//memory allocation for key
}
printStatLinkedList(hashed_array, countWord);
fclose(data);
return;
}
createList and addNode are both ADT function. Former takes a function pointer (compare is a function that I build inside the main function) as a parameter, and latter takes list name, and void type data as parameters. compare sorts linked list. Please spot me the problem .
Depending on where you declare the hashed_array you pass to this function, the contents of it may not be initialized. This means that all contents of all entries is random. This includes the list pointer.
You need to initialize this array properly first. The easiest way is to simple use memset:
memset(hashed_array, 0, sizeof(HASHED_ARRAY) * whatever_size_it_is);
This will set all members to zero, i.e. NULL for pointers.