I am new to C and am trying to code up a data structure, primarily, a ternary search tree. I am working under the assumption (for now) that valid char inputs are being passed in. I am having some issues with my insert function. Note that I am also inserting the original string in the last TSTnode where the last character of str will also be held.
Here is what I have so far
struct TSTnode {
char* word; // NULL if no word ends here
char self;
struct TSTnode *left, *sub, *right;
};
int insert_tst(struct TSTnode** tree, const char* str) {
return _insert(tree, str, 0);
}
int _insert(struct TSTnode** tree, const char* str, int position) {
if((*tree) == NULL) {
*tree = new_tst_node(*(str+position));
position = position + 1;
if(*(str+position) == '\0') {
(*tree)->word = strcpy((*tree)->word,str);
return 1;
}
}
else if ((*tree)->self > *(str+position)) {
position = position + 1;
_insert( &((*tree)->left), str, position);
}
else if ((*tree)->self < *(str+position)) {
position = position + 1;
_insert( &((*tree)->right), str, position);
}
else {
position = position + 1;
_insert( &((*tree)->sub), str, position);
}
return 0;
}
struct TSTnode* new_tst_node(char self) {
struct TSTnode* newNode = (struct TSTnode*) malloc(sizeof(struct
TSTnode));
if (newNode == NULL) {
return NULL;
}
newNode->word = NULL;
newNode->self = self;
newNode->left = NULL;
newNode->right = NULL;
newNode->sub = NULL;
return newNode;
}
Here is how I am testing:
struct TSTnode* tree = NULL;
char* words[1] = {"hello"};
for (int i = 0; i < 1; i++) {
if (insert_tst(&tree, words[i]) == 0) {
//print some error
}
else { //success }
EDIT - My issue is that none of my conditional branches are being taken and the insert function simply goes straight to return 0.
Note: You confusingly use tree for both TSTnode* and TSTnode**. I'm going to use tree_ptr for the latter, and pretend that you did the same.
Your claim is false. The body of if((*tree_ptr) == NULL) is executed. You do have a number of problems, though.
You don't handle the case where *tree_ptr == NULL && *(str+position+1) != '\0'.
You don't correctly handle the case where *tree_ptr != NULL && *(str+position+1) == '\0'.
You always return 0 when *tree_ptr != NULL || str[1] != '\0'.
You never allocate word, but you deference it. The thing is, you shouldn't be storing the string again anyway!
You don't handle the case where str[0] == '\0' (empty string).
Fixed:
int insert_tst(struct TSTnode** tree_ptr, const char* str) {
if (!*str)
return 0; /* Zero-length strings are not supported. */
return insert_tst_helper(tree_ptr, str, 0);
}
int insert_tst_helper(struct TSTnode** tree_ptr, const char* str, int position) {
if (*tree_ptr == NULL) {
*tree_ptr = new_tst_node(*(str+position));
if (*tree_ptr == NULL)
return 0; /* Memory allocation error. */
}
if (*(str+position+1) == '\0') { /* If the next char is a NUL */
(*tree_ptr)->is_word = 1;
return 1;
}
else if ((*tree_ptr)->self > *(str+position)) {
position = position + 1;
return insert_tst_helper( &((*tree_ptr)->left), str, position);
}
else if ((*tree_ptr)->self < *(str+position)) {
position = position + 1;
return insert_tst_helper( &((*tree_ptr)->right), str, position);
}
else {
position = position + 1;
return insert_tst_helper( &((*tree_ptr)->sub), str, position);
}
}
Untested.
Let's clean this up, though.
*(str+position)simplifies tostr[position]
ch == '\0'simplifies toch == 0then to!ch
position = position + 1; return insert_tst_helper(..., str, position);simplifies to++position; return insert_tst_helper(..., str, position);then toreturn insert_tst_helper(..., str, position+1);then toreturn insert_tst_helper(..., str+1, 0);then toreturn insert_tst(..., str+1);
Why is recursion being used at all???
Fixed:
int insert_tst(struct TSTnode** tree_ptr, const char* str) {
if (!*str)
return 0; /* Zero-length strings are not supported. */
while (1) {
if (*tree_ptr == NULL) {
*tree_ptr = new_tst_node(*str);
if (*tree_ptr == NULL)
return 0; /* Memory allocation error. */
}
if (!*(str+1)) { /* If the next char is a NUL */
(*tree_ptr)->is_word = 1;
return 1;
}
int cmp = *str - (*tree_ptr)->self;
if (cmp < 0) { tree_ptr = &( (*tree_ptr)->left ); }
else if (cmp > 0) { tree_ptr = &( (*tree_ptr)->right ); }
else { tree_ptr = &( (*tree_ptr)->sub ); }
++str;
}
}
Untested.
Related
I am trying to insert Node to Binary tree. This is my function for creating Node (rest is done).
void BVSCreate_function(TNodef *rootPtr, function_save token) {
TNodef *newPtr = malloc(sizeof(struct tnodef));
if (newPtr == NULL) {
fprintf(stderr, "99");
return;
}
TNodef init;
string initStr;
initStr.str = NULL;
initStr.length = 0;
initStr.alloc = 0;
newPtr = &init;
newPtr->content = &initStr;
newPtr->leftPtr = NULL;
newPtr->rightPtr = NULL;
newPtr->return_type = token.ret_value;
newPtr->parameters = token.param_count;
strCpyStr(newPtr->content, token.content);
rootPtr = newPtr;
}
void BVSInsert_function(TNodef *rootPtr, function_save token) {
if (rootPtr == NULL) {
BVSCreate_function(rootPtr, token);
} else {
if ((strCmpStr(token.content, rootPtr->content)) < 0) {
BVSCreate_function(rootPtr->leftPtr, token);
} else
if ((strCmpStr(token.content, rootPtr->content)) > 0) {
BVSCreate_function(rootPtr->rightPtr, token);
}
}
}
When TNodef and function_save are structs:
typedef struct {
string *content;
int param_count;
int ret_value;
} function_save;
typedef struct tnodef {
string *content;
struct tnodef *leftPtr;
struct tnodef *rightPtr;
int parameters;
int return_type;
} TNodef;
Where string is defined as this struct:
typedef struct {
char *str; // content of string
int length; // length of string
int alloc; // amount of memory allocated
} string;
strCpystr function :
int strCpyStr(string *s1, string *s2) {
int len2 = s2->length;
if (len2 > s1->alloc) {
if (((s1->str) = (char *)realloc(s1->str, len2 + 1)) == NULL) {
return 1;
}
s1->alloc = len2 + 1;
}
strcpy(s1->str, s2->str);
s1->length = len2 + 1;
return 0;
}
I am trying to create a node in binary tree and put there information from struct function_save.
But when I try to print this tree after insert it shows me that tree is still empty.
Your code in BVSCreate_function has undefined behavior because:
newPtr = &init; discards the allocated node and instead uses a local structure that will become invalid as soon as the function returns.
newPtr->content = &initStr; is incorrect for the same reason: you should allocate memory for the string too or possibly modify the TNodeDef to make content a string object instead of a pointer.
Function BVSInsert_function does not return the updated root pointer, hence the caller's root node is never updated. You could change the API, passing the address of the pointer to be updated.
There is also a confusion in BVSInsert_function: it should call itself recursively when walking down the tree instead of calling BVSCreate_function.
Here is a modified version:
/* Allocate the node and return 1 if successful, -1 on failure */
int BVSCreate_function(TNodef **rootPtr, function_save token) {
TNodef *newPtr = malloc(sizeof(*newPtr));
string *newStr = malloc(sizeof(*content));
if (newPtr == NULL || newStr == NULL) {
fprintf(stderr, "99");
free(newPtr);
free(newStr);
return -1;
}
newStr->str = NULL;
newStr->length = 0;
newStr->alloc = 0;
newPtr->content = newStr;
newPtr->leftPtr = NULL;
newPtr->rightPtr = NULL;
newPtr->return_type = token.ret_value;
newPtr->parameters = token.param_count;
strCpyStr(newPtr->content, token.content);
*rootPtr = newPtr;
return 1;
}
int BVSInsert_function(TNodef **rootPtr, function_save token) {
if (*rootPtr == NULL) {
return BVSCreate_function(rootPtr, token);
} else {
if (strCmpStr(token.content, rootPtr->content) < 0) {
return BVSInsert_function(&rootPtr->leftPtr, token);
} else
if ((strCmpStr(token.content, rootPtr->content)) > 0) {
return BVSInsert_function(&rootPtr->rightPtr, token);
} else {
/* function is already present: return 0 */
return 0;
}
}
}
Note also that function strCpyStr may write beyond the end of the allocated area is len2 == s1->alloc, assuming s1->len is the length of the string, excluding the null terminator.
Here is a modified version:
int strCpyStr(string *s1, const string *s2) {
int len2 = s2->length;
if (len2 >= s1->alloc) {
char *newstr = (char *)realloc(s1->str, len2 + 1);
if (newstr == NULL) {
return 1;
}
s1->str = newstr;
s1->alloc = len2 + 1;
}
strcpy(s1->str, s2->str);
s1->length = len2;
return 0;
}
I'm trying to print a struct's field in C, and I have troubles with it.
I'm having 2 problems.
First, when I'm trying to print the field "courseName" of a course named "math" I get: "(null)math══════════════════════════"
Second, when I try to print the field courseNumber, it prints the courseNumber and the courseName.
can someone please tell me what I'm doing wrong?
struct Course {
char courseNumber[6];
char courseName[30];
struct Course* next;
};
struct Course* updateCoursesList(char courseDetails[], struct Course* coursesList)
{
struct Course* head = NULL;
int i = 6, j = 0;
if (coursesList == NULL) {
coursesList = (struct Course*)malloc(sizeof(struct Course));
if (coursesList == NULL)
return NULL;
head = coursesList;
strncpy(coursesList->courseNumber, courseDetails, 5);
coursesList->next = NULL;
while (courseDetails[i] != '\n') {
coursesList->courseName[j] = courseDetails[i];
i++;
j++;
}
}
else {
head = coursesList;
if (strncmp(courseDetails, coursesList->courseNumber, 5) == 0) {
while (courseDetails[i] != '\n') {
coursesList->courseName[j] = courseDetails[i];
i++;
j++;
}
return head;
}
while (coursesList->next != NULL) {
if (strncmp(courseDetails, coursesList->courseNumber, 5) == 0) {
while (courseDetails[i] != '\n') {
coursesList->courseName[j] = courseDetails[i];
i++;
j++;
}
return head;
}
else
coursesList = coursesList->next;
}
coursesList->next = (struct Course*)malloc(sizeof(struct Course));
if (coursesList->next == NULL)
return head;
strncpy(coursesList->next->courseNumber, courseDetails, 5);
coursesList->next->next = NULL;
while (courseDetails[i] != '\n') {
coursesList->next->courseName[j] = courseDetails[i];
i++;
j++;
}
}
return head;
}
int main()
{
struct Course* coursesList = NULL;
char array[30] = "12345 math\n";
coursesList = updateCoursesList(array, coursesList);
printf("%s", coursesList->courseNumber);
printf("%s", coursesList->courseName);
}
You can actually use strncpy for copying both your courseNumber and courseName because both are strings.
strncpy does not terminate the copied string with \0 terminator, so we have add the \0 at the end.
For your input char array[30] = "12345 math\n"; do as follows.
strncpy(coursesList->courseNumber, courseDetails, 5);
coursesList->courseNumber[5] = '\0';
//since your courseName is also string, you need not do piece by piece copy
strncpy(coursesList->courseName, courseDetails+6, 5);
coursesList->courseName[5] = '\0';
and below code is not required in both of your if and else parts
while (courseDetails[i] != '\n')
{
coursesList->courseName[j] = courseDetails[i];
i++;
j++;
}
NOTE: But this very weird way of reading your inputs , instead take inputs from stdin and use them, because the lengths may not be same for all courses.
I am writing a function to read from a text file and extract the strings word by word and store them to a binary search tree. The function should ignore all punctuations and discard duplicate words(only adds to the word frequency).
My problem with the code now is that every time "while (fscanf(fp, "%s", line)!=EOF)" runs, my rootWord gets replaced by the newly read word. I cannot figure out how is it possible for fscanf to be able to do this.
typedef struct word * wordPtr;
typedef struct position * positionPtr;
typedef struct position
{
int position;
positionPtr nextPosition;
} Position;
typedef struct word
{
char * word;
unsigned freq;
positionPtr firstPosition;
wordPtr leftWord;
wordPtr rightWord;
} Word;
typedef struct bstWord
{
wordPtr rootWord;
unsigned wordCount;
} BSTWord;
int BSTCreate(BSTWord* bst, char* fileName)
{
FILE * fp = fopen(fileName,"r");
char line[MAX_WORD_LEN + 1];
int charCount = 0;
char * token;
char delimit[] = "\t\r\n\v\f,.-;:\"\' ";
while (fscanf(fp, "%s", line)!=EOF)
{
wordPtr prev = NULL, curr = bst->rootWord;
wordPtr newWord;
positionPtr newPosition;
int lessThen;
int status = 1;
token = strtok(line, delimit);
charCount = charCount + 1;
while(curr!=NULL)
{
prev = curr;
if(strcmp(token, curr->word)<0)
{
printf("\nless");
lessThen = 1;
curr = curr->leftWord;
status = 1;
}
else if(strcmp(token, curr->word)>0)
{
printf("\nmore");
lessThen = 0;
curr = curr->rightWord;
status = 1;
}
else if(strcmp(token, curr->word)==0) //If word is already in tree, add freq + update position
{
if ( ( newPosition = malloc( sizeof( Position ) ) ) == NULL )
return FAILURE;
newPosition->position = charCount;
newPosition->nextPosition = NULL;
positionPtr prevPosition = NULL, currPosition = curr->firstPosition;
while(currPosition!=NULL)
{
prevPosition = currPosition;
currPosition = currPosition->nextPosition;
}
prevPosition->nextPosition = newPosition;
status = 0;
curr = NULL;
break;
}
}
if(status == 1)
{
if ( ( newWord = malloc( sizeof( Word ) ) ) == NULL )
return FAILURE;
if ( ( newPosition = malloc( sizeof( Position ) ) ) == NULL )
return FAILURE;
newPosition->position = charCount;
newWord->word = token;
newWord->freq = 1;
newWord->firstPosition = newPosition;
newWord->leftWord = NULL;
newWord->rightWord = NULL;
if(bst->rootWord == NULL)
bst->rootWord = newWord;
else
{
if(lessThen)
{
prev->leftWord = newWord;
}
else
{
prev->rightWord = newWord;
}
}
}
bst->wordCount++;
}
fclose(fp);
free(fp);
return SUCCESS;
}
newWord->word = token;
Every token points to the same memory you allocated at:
char line[MAX_WORD_LEN + 1];
You need to allocate additional memory and copy the string there:
newword->word = malloc(strlen(token) + 1);
strcpy(newword->word, token);
this is the function that i am calling from main:
char** readTokens(char *userInput, const char *seperator)
{
char** c;
char line1[512],line2[512];
int wordCount = 0;
int index;
char* tmp;
strcpy(line1, userInput);
for (index=0;line1[index]!='\n';index++);
line1[index]='\0';
strcpy(line2,line1);
tmp = strtok(line1,seperator);
while (tmp!=NULL)
{
tmp=strtok(NULL,seperator);
wordCount = wordCount + 1;
}
if((wordCount) == ERROR)
{
return NULL;
}
c=(char**)malloc(((wordCount)+1)*sizeof(char*));
if (c == NULL)
{
printf("failed to allocate memory.\n");
return NULL;
}
tmp = strtok(line2,seperator);
index=0;
while (tmp!=NULL)
{
c[index]=(char*)malloc((strlen(tmp)+1*sizeof(char)));
if (c[index]==NULL)
{
printf("failed to allocate memory.\n");
return NULL;
}
strcpy(c[index],tmp);
tmp=strtok(NULL,seperator);
index++;
}
c[index] = NULL;//put NULL on last place
return c;
}
And this how i use it in main:
while (fgets(words, sizeof(words), filePointer) != NULL) // this line is a command of reading a line from the file.
{
/*here i am calling the function*/
array = readTokens(words, " ");
theGraph->graphEdges[index_i].sourceVertex = array[ZERO];
theGraph->graphEdges[index_i].destinationVertex = array[ONE];
theGraph->graphEdges[index_i].arcValue = atoi(array[TWO]);
for(index_j = ZERO ; index_j < vertexes ; index_j++)
{
if(theGraph->placeInTableIndex[index_j] == NULL)
{
theGraph->placeInTableIndex[index_j] = array[ZERO];
break;
}
else if(strcmp(theGraph->placeInTableIndex[index_j],array[ZERO]) == ZERO)
break;
}
for(index_j = ZERO ; index_j < vertexes ; index_j++)
{
if(theGraph->placeInTableIndex[index_j] == NULL)
{
theGraph->placeInTableIndex[index_j] = array[ONE];
break;
}
else if(strcmp(theGraph->placeInTableIndex[index_j],array[ONE]) == ZERO)
break;
}
theGraph->graphEdges[index_i+ONE].sourceVertex = array[ONE];
theGraph->graphEdges[index_i+ONE].destinationVertex = array[ZERO];
theGraph->graphEdges[index_i+ONE].arcValue = atoi(array[TWO]);
index_i+= TWO;
//freeTokens(array);
}
I tried to do free to the array in the end of the while but it not work i still have memory leak from this function (valgrind check). i am using this function to free:
void freeTokens(char** tokens)
{
while(*tokens != NULL)
{
*tokens = NULL;
free(*tokens);
*tokens++;
}
tokens = NULL;
free(tokens);
}
You're losing the original value of tokens (the thing you need to free) by incrementing it; then you set it to NULL, then try to free NULL.
Instead:
void freeTokens(char** tokens)
{
char **freeTokens = tokens;
while (*freeTokens != NULL)
{
free(*freeTokens);
*freeTokens = NULL; // not actually necessary, but must happen *after* if at all
freeTokens++;
}
free(tokens);
// tokens = NULL; // accomplishes nothing; doesn't change the caller's version
}
I don't know why my code gives me a segmentation fault.
Before it worked fine, so i must have done something to it,
but I don't know what.
This function gets some Markdown-Elements from a specific line in a file
and returns a dynamic array ( int * elements) with the elements
int * parser_getElements(char * string)
{
int * elements;
char * token, * temptoken;
int charcount = 0, elemcount = 1, i = 0;
assert(string != NULL);
elements = malloc(sizeof(int));
*elements = 0;
token = strtok(string," ");
while(token != NULL)
{
temptoken = token;
while (*temptoken)
{
charcount++;
++temptoken;
}
if(token[0] == '*' && token[charcount - 1] == '*')
{
if(token[1] == '*' && token[charcount - 2] == '*')
{
elements[elemcount-1] = EMPH;
}
else
{
if(token[1] != '*' && token[charcount - 2] != '*')
{
elements[elemcount-1] = ITAL;
}
}
elemcount++;
elements = realloc(elements,sizeof(int) * elemcount);
}
else if(token[0] == '_' && token[charcount - 1] == '_')
{
if(token[1] == '_' && token[charcount - 2] == '_')
{
elements[elemcount-1] = EMPH;
}
else
{
if(token[1] != '_' && token[charcount - 2] != '_')
{
elements[elemcount-1] = ITAL;
}
}
elemcount++;
elements = realloc(elements,sizeof(int) * elemcount);
}
else
{
elements[elemcount-1] = TEXT;
elemcount++;
elements = realloc(elements,sizeof(int) * elemcount);
}
token = strtok(NULL, " ");
charcount = 0;
}
return elements;
}
The problem occurs in the function below.
void parser_printMDElements(List markdown, FILE * out)
{
MD_Element element;
int count = 0, no_text = 0;
if(markdown != NULL)
{
element = markdown->value->elements[0];
while(element <= EMPH)
{
if(element > TEXT)
{
no_text = 1;
}
count++;
element = markdown->value->elements[count];
}
count = 0;
element = markdown->value->elements[0];
while(element <= EMPH)
{
if(no_text)
{
printf("%s\n",MD_Element_Descr[element]);
}
count++;
element = markdown->value->elements[count];
}
parser_printMDElements(markdown->next,out);
}
}
This line(first appearance)
element = markdown->value->elements[0];
gives me a segmentation fault and I don't know why.
The program stops after this line.
This is the main program
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "errors.h"
#include "parser.h"
int main(int argc, char * argv[])
{
Errorcode error = ERR_NULL;
char * gap;
int gapcount = 1;
List md = parser_newList();
/** Save name of document in firstnode */
md->value = malloc(sizeof(struct NodeValue));
md->value->string = argv[1];
md->type = DOCUMENT;
md->next = malloc(sizeof(struct Node));
error = parser_parseFile(argv[1],&(md->next));
if(error == ERR_NULL)
{
gap = malloc(sizeof(char));
*gap = '\0';
parser_printMDElements(md, gap, gapcount, stdout);
printf("\n");
}
return 0;
}
This code is extracted from parser.h
enum MD_Element
{
DOCUMENT,
LINE,
SECTION,
LISTELEMENT,
H1,
H2,
H3,
H4,
H5,
H6,
TEXT,
IMG,
LINK,
QUOTE,
CODE,
ITAL,
EMPH
};
typedef enum MD_Element MD_Element;
typedef struct NodeValue * NodeValue;
struct NodeValue
{
char * string;
int * elements;
};
typedef struct Node * Node;
struct Node
{
MD_Element type;
NodeValue value;
Node next;
};
typedef Node List;
Can sb. tell me what I've done wrong?
Thanks!
There are still many pieces of your code missing here.
I'm guessing that your problem is possibly in md->value->string = argv[1];.
(athough your question does not show how you are using md->value->string).
If you attempt to change the contents of the memory pointed by md->value->string, then you will have a memory access violation, since this variable is pointing to a read-only memory segment.
If that is indeed the case, then you can solve it by replacing md->value->string = argv[1]; with:
md->value->string = malloc(strlen(argv[1])+1);
strcpy(md->value->string,argv[1]);