The folowing program is intended to store words alphabetically in the Binary Search Tree using the strcmp function. The issue, detailed under the program, is that no pointer is passed in the recursive call of the function in the last part of the function.
typedef struct NodT{
char word[30];
struct NodT *left, *right;
} NOD;
void reset_field(NOD *nod){
int i;
for(i=0; i<30; i++){
nod->word[i]='\0';
}
}
void enter_recursively(NOD *nod, char *word){
if(nod==NULL){
nod= (NOD *) malloc(sizeof(NOD));
nod->left=NULL;
nod->right=NULL;
reset_field(nod);
strcpy(nod->word, word);
return;
}
if(nod->word[0]=='\0'){
strcpy(nod->word, word);
return;
}
if(strcmp(nod->word, word)==0) return;
if(strcmp(nod->word, word)<0){
enter_recursively(nod->right, word);//the problem seems to be here
printf("right\n");
}
else{
enter_recursively(nod->left, word);//...and here
printf("left\n");
}
//the NULL pointer is being sent over, which is peculiar
}
The thing is that, when I pass the pointers(left, right) from the structure to the recursive function in the if-else conditions, it takes a NULL value on the other side, which it shouldn't do because they are not NULL after alocating the first word in the root and the second in the right or left depending on strcmp, alocation when malloc is used to create the new storage space for the word.
UPDATE: The new script using double pointers:
typedef struct NodT{
int key;
char word[30];
struct NodT *left, *right;
} NOD;
void enter_recursively(NOD **nod, char *word){
printf("N: %p\n", nod);
printf("NL: %p\n", (**nod).left);
printf("NR: %p\n", (**nod).right);
if(nod==NULL){
nod=malloc(sizeof(NOD));
(**nod).left=NULL;
(**nod).right=NULL;
strcpy((**nod).word, word);
return;
}
if((**nod).word[0]=='\0'){
strcpy((**nod).word, word);
return;
}
if(strcmp((**nod).word, word)==0) return;
if(strcmp((**nod).word, word)<0){
enter_recursively((**nod).right, word);
}
else{
enter_recursively((**nod).left, word);
}
I get segmentation fault and I don't know why.
The problem is that *nod is modified but not returned: Change
void enter_recursively(NOD *nod, char *word)
by
void enter_recursively(NOD **nod, char *word)
in order to return legal pointer. Inside the function, use *nod instead nod, this is the correct way.
When you pass only NOD * to the function, the allocated memory is not stored properly. Is like when you want to modify a int value inside a function, you pass its address, instead the value.
Besides, verify always null pointers before use them. You can obtain a core.
The final code seams like:
void enter_recursively(NOD **nod, char *word){
if (*nod==NULL){
*nod=malloc(sizeof(NOD));
(*nod)->left=NULL;
(*nod)->right=NULL;
strcpy((*nod)->word, word);
return;
}
if((*nod)->word[0]=='\0'){
strcpy((*nod)->word, word);
return;
}
if(strcmp((*nod)->word, word)==0) return;
if(strcmp((*nod)->word, word)<0){
enter_recursively(&(*nod)->right, word);
}
else{
enter_recursively(&(*nod)->left, word);
}
Your enter_recursively() function allocates a node, maybe even assigns to it, but has no way to pass it back to the caller. Find a way to return something useful to the caller.
UPDATE:
for completeness: this is the other way of communicating information from the child back to to its parent: (via the return value)
NOD * enter_recursively(NOD *ptr, char *word){
int rc;
if (ptr==NULL){
ptr = malloc(sizeof *ptr);
ptr->left = NULL;
ptr->right = NULL;
strcpy(ptr->word, word);
return ptr;
}
rc = strcmp(ptr->word, word);
if (rc==0) return ptr;
if (rc < 0){
ptr->right = enter_recursively(ptr->right, word);
fprintf(stderr, "right\n");
}
else {
ptr->left = enter_recursively(ptr->left, word);
fprintf(stderr, "left\n");
}
return ptr; /* not reached */
}
Related
I am coding an algorithm that uses the trie data structure. Basically, if input is "he", it will return ["hello","help","held","hen",other_words_starting_with_he]. code:
//NOTE: The expression sizeof(array)/sizeof(node) must always evaluate to 26. Not more; not less, for all the node arrays.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *chars="abcdefghijklmnopqrstuvwxyz";
struct node{
char ch;
struct node *next[26];
};
void init_w_null(struct node **n, int len){
register int counter=0;
while(counter<len){
*(n+counter)=NULL;
counter++;
}
}
int index_of_char(char ch){
register int counter=0;
while(*(chars+counter)!='\0'){
if(*(chars+counter)==ch){
return counter;
}
counter++;
}
return -1;
}
void insert(struct node **root, char *key){
if(*root==NULL){
*root=(struct node*)malloc(sizeof(struct node));
if(*root==NULL){
perror("[malloc]");
exit(EXIT_FAILURE);
}
init_w_null((**root).next,26);
struct node *selected=*root;
(**root).ch=key[0];
register int counter=1;
while(counter<strlen(key)){
int ind=index_of_char(key[counter]);
(*selected).next[ind]=(struct node *)malloc(sizeof(struct node));
if(selected==NULL){
perror("[malloc]");
exit(EXIT_FAILURE);
}
(*(*selected).next[ind]).ch=key[counter];
selected=(*selected).next[ind];
counter++;
}
return;
}
register int counter=1;
struct node *selected=*root;
while(counter<=strlen(key)){
int ind=index_of_char(key[counter]);
if((*selected).next[ind]!=NULL){
selected=(*selected).next[ind];
counter++;
continue;
}
(*selected).next[ind]=(struct node*)malloc(sizeof(struct node));
if((*selected).next[ind]==NULL){
perror("[malloc]");
exit(EXIT_FAILURE);
}
(*(*selected).next[ind]).ch=key[counter];
selected=(*selected).next[ind];
init_w_null((*selected).next,26);
counter++;
}
}
void find(struct node *root, char *key){
register int counter=1;
struct node *selected=root;
int ind=0;
while(counter<=strlen(key)){
//if key param ends, and tree doesn't
if(key[counter]=='\0'){
printf("List of possible keys:\n");
construct_str(selected,key);
return;
}
ind=index_of_char(key[counter]);
//a character of key not found.
if((*selected).next[ind]==NULL){
puts("Similar keys not found.");
return;
}
selected=(*selected).next[ind];
counter++;
}
puts("Key found.");
}
void construct_str(struct node *n, char *str){
puts("[construct_str]");
//end of recursion
if(all_children_null(n)&&n!=NULL){
printf("%s\n",str);
return;
}
register int counter=0;
while(counter<26){
if((*n).next[counter]!=NULL){
char nstr[2];
nstr[0]=(*(*n).next[counter]).ch;
nstr[1]='\0';
str=strcat(str,nstr);
construct_str((*n).next[counter],str);
}
counter++;
}
}
int all_children_null(struct node *n){
register int counter=0;
while(counter<26){
if((*n).next[counter]!=NULL){
return 0;
}
counter++;
}
return 1;
}
void insert_full(struct node **arr, char *key){
int first=index_of_char(key[0]);
insert(&arr[first],key);
}
//a debugging function to see whether insertion is successful.
/*void raw_print(struct node *n){
//puts("[raw_print]");
if(n!=NULL){
putchar((*n).ch);
register int counter=0;
for(;counter<26;counter++){
raw_print((*n).next[counter]);
}
if(all_children_null(n)){
printf("\nAll children of %c are NULL.\n",(*n).ch);
}
}
}*/
int main(){
struct node *nds[26];
init_w_null(nds,26);
insert_full(nds,"hello");
insert_full(nds,"help");
insert_full(nds,"bruh");
insert_full(nds,"lmao");
find(nds[index_of_char('l')],"lm");
return 0;
}
output:
List of possible keys:
[construct_str]
Segmentation fault (core dumped)
I've narrowed the problem down to construct_str. Please tell me if I'm wrong, though.
FOR PEOPLE WHO DON'T KNOW WHAT A TRIE IS:
It's a structure which can store strings with the same prefix, so if we add "hello" and "help", the fourth character in both the strings would be siblings. the node of a trie contains a character and a node array with 26 members.
I see that my crystal ball is well attuned today.
Starting with ...
find(nds[index_of_char('l')],"lm");
... you are passing a string literal as the second argument to find(). In that function, a pointer to its first character is associated with parameter key.
Within find(), you are forwarding that pointer to construct_str():
construct_str(selected,key);
, wherein it is associated with parameter str.
In construct_str(), you are passing that pointer as the first argument to strcat():
str=strcat(str,nstr);
strcat appends the contents of the second string to the array containing the first. It does not create a new array for the concatenated result. Therefore, the left pointer must point (in)to an array that
is modifiable, and
contains enough space after the end of the string to accommodate the extra contents.
String literals do not satisfy either criterion. Oops. Undefined behavior results.
In the line
str=strcat(str,nstr);
str is a pointer to a string literal. You are not allowed to modify them. Attempting to modify a string literal using the function strcat will invoke undefined behavior.
When calling strcat, you must ensure that the first argument is pointing to a memory buffer which
you are allowed to write to, and
has sufficient space for adding the character(s) to the string.
I'm trying to insert into my BST but I'm struggling with creating a loop out of it.
The code works when I insert one by one, but when I try to put it into a loop it doesn't insert correctly.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h> // for strcmp()
#include <ctype.h> // for toupper()
typedef struct BstNode{
//char name[20];
// int data;
struct BstNode* left;
struct BstNode* right;
char* name;
}BstNode;
typedef int (*Compare)(const char*, const char*); // makes comparisons easier
/* Returns pointer address to newly created node */
BstNode* createNode(char* name){
BstNode* newNode = (BstNode*)malloc(sizeof(BstNode)); // Allocates memory for the newNode
newNode->name = name; // newNode->data is like newNode.data
newNode->left= NULL;
newNode->right = NULL;
return newNode;
}
//insert node into Tree recursively
BstNode* insertNode(BstNode* node, char* name, Compare cmp){
int i;
/* char *s1 = node->name;
char *s2 = name;
printf("s1: %s, s2: %s\n", s1,s2);
i = strcmp(s1, s2); // if =1, s1 is greater
printf("i: %d\n", i); */
if(node == NULL){// if tree is empty
// printf("inside NULL\n");
node = createNode(name);
//return node;
}
else{
i = cmp (name, node->name); // sweet
if(i == -1){
// printf("inside left\n");
node->left = insertNode(node->left, name, cmp);
//return node;
}
else if(i == 1){
// printf("inside right\n");
node->right = insertNode(node->right, name, cmp);
//return node;
}
else if(i == 0 ){ //avoid duplicates for now
// printf("inside 0\n");
printf("Name is in BST\n");
return NULL;
}
}
return node;
}
BstNode* printTree(BstNode* node){
if(node == NULL){
return NULL;
}
printTree(node->left);
printf("%s\n",node->name);
printTree(node->right);
}
int CmpStr(const char* a, const char* b){
return (strcmp (a, b)); // string comparison instead of pointer comparison
}
//void Insert(Person *root, char name[20]);
int main(){
BstNode* root = NULL; // pointer to the root of the tree
char buf[100];
char option = 'a';
while(1) {
printf("Enter employee name");
scanf("%s",buf);
printf ("Inserting %s\n", buf);
root = insertNode(root, buf, (Compare)CmpStr);
printTree(root);
}
}
I can do root = insertNode(root, name, (Compare)CmpStr)
several times in code, but if I try to loop it with user input it won't insert correctly. I'm not sure if it has to do with the fact that I'm using scanf() or root not being set correctly. I've tried using fgets() as well but I'm not too sure how to use it and keep messing that up.
Any help is appreciated.
In your loop, you always pass the same buffer to your insert function; Your createNode does not copy the content of the buffer but rather stores a reference to the (always) same buffer; Hence, changing the buffer content after insert will also change the "content" of previously inserted nodes.
I'd suggest to replace newNode->name = name in createNode with newNode->name = strdup(name). This will actually copy the passed "contents" and gives your BST control over the memory to be kept. Thereby don't forget to free this memory when deleting nodes later on.
I am having some issues with dynamically allocating a string for a node in a tree. I have included my node structure below for reference.
struct node
{
char *string;
struct node *left;
struct node *right;
};
typedef struct node node;
I am supposed to read words from a text file and then store those words into a tree. I am able to store char arrays that have been defined, such as char string[20] without problems, but not strings that are supposed to be dynamically allocated.
I am only going to post the code I am using to read my file and try to create the dynamically allocated array. I have already created the file pointer and checked that it is not NULL. Every time I try to run the program, it simply crashes, do I need to try and read the words character by character?
//IN MAIN
node *p, *root ;
int i;
int u;
root = NULL;
char input[100];
while(fscanf(fp, "%s", &input) != EOF)
{
//Create the node to insert into the tree
p = (node *)malloc(sizeof(node));
p->left = p->right = NULL;
int p = strlen(input); //get the length of the read string
char *temp = (char*) malloc(sizeof(char)*p);
//malloc a dynamic string of only the length needed
strcpy(local, input);
strcpy(p->word,local);
insert(&root, p);
}
To be completely clear, I only want advice regarding the logic of my code, and only would like someone to help point me in the right direction.
You are invoking many undefined behaviors by
passing pointer to object having wrong type to scanf(). i.e. In fscanf(ifp, "%s", &input), char(*)[100] is passed where char* is expected
accessing out-of-range of allocated buffer when storeing terminating null-character in strcpy(local, input);
using value of buffer allocated via malloc() and not initialized in strcpy(curr->word,local);
Your code should be like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef struct node_t {
struct node_t* left, *right;
int count;
char* word;
} node;
void insert(node ** tree, node * item);
int main(void) {
FILE* ifp = stdin;
node * curr, * root;
int i;
int u;
root = NULL;
char input[100];
/* you should specify the maximum length to read in order to avoid buffer overrun */
while(fscanf(ifp, "%99s", input) != EOF)
{
//Create the node to insert into the tree
curr = malloc(sizeof(node));
if(curr == NULL) /* add error check */
{
perror("malloc 1");
return 1;
}
curr->left = curr->right = NULL;
curr->count = 1;
int p = strlen(input); //get the length of the read string
char *local = malloc(sizeof(char)*(p + 1)); /* make room for terminating null-character */
if (local == NULL) /* add error check again */
{
perror("malloc 2");
return 1;
}
//malloc a dynamic string of only the length needed
//To lowercase, so Job and job is considered the same word
/* using strlen() in loop condition is not a good idea.
* you have already calculated it, so use it. */
for(u = 0; u < p; u++)
{
/* cast to unsigned char in order to avoid undefined behavior
* for passing out-of-range value */
input[u] = tolower((unsigned char)input[u]);
}
strcpy(local, input);
curr->word = local; /* do not use strcpy, just assign */
insert(&root, curr);
}
/* code to free what is allocated will be here */
return 0;
}
//Separate insert function
void insert(node ** tree, node * item)
{
if(!(*tree))
{
*tree = item;
return;
}
if(strcmp(item->word,(*tree)->word) < 0)
insert(&(*tree)->left, item);
else if(strcmp(item->word,(*tree)->word) > 0)
insert(&(*tree)->right, item);
/* note: memory leak may occur if the word read is same as what is previously read */
}
I have a C linked list that looks like this:
typedef struct Node {
struct Node *child;
void *value;
} Node;
typedef struct LinkedList {
Node *head;
} LinkedList;
To test that everything is working properly, I have a main program that reads from a file, line by line, and stores each line in the following Node. Then, once the file reaches its end, I run through the linked list and print all of the lines.
However, when I test it, it only prints blank lines, except for the last line in the file, which gets printed normally. In addition, despite the fact that all the strings are malloc'd before they are stored in the nodes, I get a "pointer being free was not allocated error." I've gone through this pretty extensively in gdb and can't seem to figure out what I'm doing wrong. Perhaps somebody else can help me out here? Here's the rest of my code:
int main(int argc, char **argv) {
if (argc>1) {
FILE *mfile = fopen(argv[1], "r");
if (mfile!=NULL) {
char c;
char *s = (char*) malloc(1);
s[0] = '\0';
LinkedList *lines = (LinkedList*) malloc(sizeof(LinkedList));
while ((c=fgetc(mfile))!=EOF) {
if (c=='\n') {
setNextLine(lines, s);
free(s);
s = (char*) malloc(1);
s[0] = '\0';
}
else s = append(s, c);
}
if (strlen(s)>0) {
setNextLine(lines, s);
free(s);
}
fclose(mfile);
printList(lines);
LLfree(lines);
} else perror("Invalid filepath specified");
} else perror("No input file specified");
return 0;
}
void setNextLine(LinkedList *lines, char *line) {
struct Node **root = &(lines->head);
while (*root!=NULL) root = &((*root)->child);
*root = (Node*) malloc(sizeof(Node));
(*root)->child = NULL;
(*root)->value = line;
}
char *append(char *s, char c) {
int nl = strlen(s)+2;
char *retval = (char*) malloc(nl);
strcpy(retval, s);
retval[nl-2] = c;
retval[nl-1] = '\0';
free(s);
return retval;
}
void printList(LinkedList *lines) {
Node *root = lines->head;
while (root!=NULL) {
char *s = (char*) root->value;
printf("%s \n", s);
root = root->child;
}
}
void LLfree(LinkedList *list) {
if (list->head!=NULL) NodeFree(list->head);
free(list);
return;
}
void NodeFree(Node *head) {
if (head->child!=NULL) NodeFree(head->child);
free(head->value);
free(head);
return;
}
It appears that there are several things that could be changed in the code.
Perhaps the one that is most likely to help would be that memory improperly freed.
Change:
setNextLine(lines, s);
free(s);
s = (char*) malloc(1);
to:
setNextLine(lines, s);
// free(s);
s = (char*) malloc(1);
The pointer 's' is still pointing to what was just assigned to the previous node's 'value'. Hence, calling 'free(s)' is actually freeing the node's 'value'.
Try doing this
void NodeFree(Node *head)
if (head->child!=NULL)
NodeFree(head->child);
free(head->value);
free(head->child);
free(head);
head->value = NULL;
head->child = NULL;
head = NULL;
return;
}
The setNextLine() function is appending the 's' poitner to the node value, and then that same pointer is getting freed after that call in the while loop.
That's why you'll get a double free fault when NodeFree() tries to free head->value.
And the fact that you get the last line could be just because the address pointed by 's' for the last line (which got freed like all the previous lines) is still unused although its not allocated to your pointer anymore.
You should make a copy of the line pointed by 's' in setNextLine() so you can work with the 's' pointer for the rest of lines.
The first time addToEnd() is called it works but the second time it crashes the program. I've been trying to debug it and found it happens when head->next happens. I'm a little confused because it's just being read, can that crash the program? If yes how can you possible itterate through the file?
It seems to work on certain values of entry but not others. If two entry's are the same and composed all of one letter it crashes. So if addToEnd(head, "aaaaaaaaa") is called then addToEnd(head, "aaaaaaaaa") is called the program crashes but if addToEnd(head, "aaaaaaaaa") then addToEnd(head, "aaaaaaaab") it is fine.
Here is 100% all of the code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct node
{
char entry[21];
struct node* next;
} node;
void readDic();
void reverStr(char *str);
bool isInDic(char *reversed);
void addToEnd(node* head, char entry[21]);
unsigned int searchAndDestroy(node **head, char *entry);
void printList(node* head);
int main()
{
printf("Hello\n");
readDic();
printf("Goodbye!");
return EXIT_SUCCESS;
}
void readDic()
{
FILE* words;
char singleLine[21];
words = fopen("words.txt", "r");
node* head = malloc(sizeof(node));
fscanf(words, "%20s", head->entry);//need to do this initially
head->next = NULL;
printf("here 0");
printf("here 0.1");
if(words == NULL)
exit(EXIT_FAILURE);
printf("here 0.5");
while(fscanf(words, "%20s", singleLine) == 1)assigned input terms
{
printf("\nhere 0.6\n|%s|", singleLine);
addToEnd(head, singleLine);//problem here
printf("here 0.7");
reverStr(singleLine);
printf("here 1");
if(isInDic(singleLine)){
printf("here 2");
searchAndDestroy(&head, singleLine);
printf("here 3");
}
}
printf("here 4");
fclose(words);
printList(head);
printf("here 5");
}
//http://stackoverflow.com/questions/198199/how-do-you-reverse-a-string-in-place-in-c-or-c
/* PRE: str must be either NULL or a pointer to a
* (possibly empty) null-terminated string. */
void reverStr(char *str)
{
char temp, *end_ptr;
/* If str is NULL or empty, do nothing */
if(str == NULL || !(*str))
return;
end_ptr = str + strlen(str) - 1;
/* Swap the chars */
while( end_ptr > str )
{
temp = *str;
*str = *end_ptr;
*end_ptr = temp;
str++;
end_ptr--;
}
}
bool isInDic(char* reversed)
{
FILE* words;
char singleLine[21];
words = fopen("words", "r");
if(words == NULL)
exit(EXIT_FAILURE);
while(fscanf(words, "%20s", singleLine) == 1)//the length of the string has to be 1 less than declared size for the newline character
{
//printf("singline: %s reversed: %s\n", singleLine, reversed);
if(strcmp(singleLine, reversed) == 0)//strcmp returns 0 if both cstrings are equal
return true;
}
fclose(words);
return false;
}
void addToEnd(node* head, char entry[21])
{
printf("hi");
//printf("\naddress of next %p\n", (void *)head->next);
while(head->next != NULL)//just reading head->next screws it up
head = head->next;
printf("in addToEnd 2\n");
node* last = (node*)malloc(sizeof(node));
printf("in addToEnd 3\n");
head->next = last;
printf("in addToEnd 4\n");
strcpy(last->entry, entry);
printf("in addToEnd 5\n");
last->next = NULL;
printf("in addToEnd 6\n");
}
unsigned int searchAndDestroy(node **head, char *entry)
{
unsigned int count = 0;
while(*head)
{
node *del;
if(strcmp((*head)->entry, entry))
{ //this node stays
head = &(*head)->next;
continue;
}
/* this node goes
at this point head MUST point to the pointer that points at the node to be deleted
*/
del = *head;
*head = (*head)->next;
free(del);
count++;
}
return count; //number of nodes deleted
}
void printList(node* head)
{
printf("\nprinting everything\n");
if(head != NULL)
{
while(head->next != NULL)
{
printf("%s", head->entry);
head = head->next;
}
printf("%s", head->entry);
}
}
The answers are correct that head is being set to null but I don't see where?
When you create head, you don't set head->next to NULL.
One of three things:
If head is NULL this code will crash at the line you mention.
If last->entry is defined as char* you need to say 'last->entry = strdup(entry)
If the sizeof last->entry is less than 21, your strcpy will overflow it which
will result in undefined behavior.
Ok, with your recent edit, I assert that head is null or garbage when you call this function the second time.