Pointer to a structure unable to access values of 2D array - c

I am currently in the process of writing a program that acts as a circuit. I have a gate structure that takes a 2D char array in order to hold variable names, yet when I try to access these variable names stored in the array outside of the while loop, the content is empty.
typedef struct Gate
{
kind_t kind;
int size; // size of DECODER and MULTIPLEXER
char **params; // length determined by kind and size (CHANGED FROM INT TO CHAR)
// includes inputs and outputs, indicated by variable numbers
} Gate;
typedef struct Node
{
Gate *data;
struct Node *next;
} Node;
// Linked list of gates & attributes
while (fscanf(fp, "%16s", str) != EOF)
{
if (strcmp(str, "AND") == 0)
{
head = makeGate(fp, head, AND);
length++;
}
else if (strcmp(str, "OR") == 0)
{
head = makeGate(fp, head, OR);
length++;
}
else if (strcmp(str, "NAND") == 0)
{
head = makeGate(fp, head, NAND);
length++;
}
else if (strcmp(str, "NOR") == 0)
{
head = makeGate(fp, head, NOR);
length++;
}
else if (strcmp(str, "XOR") == 0)
{
head = makeGate(fp, head, XOR);
length++;
}
else if (strcmp(str, "NOT") == 0)
{
//head = makeGate(fp, head, NOT);
//length++;
}
else if (strcmp(str, "PASS") == 0)
{
//head = makeGate(fp, head, PASS);
//length++;
}
else if (strcmp(str, "DECODER") == 0)
{
//
}
else if (strcmp(str, "MULTIPLEXER") == 0)
{
//
}
printf("%s\n", head->data->params[2]);
}
// plugs in values to circuit
for (int i = 0; i < 3; i++)
{
printf("Stored string: %s\n", head->data->params[i]);
}
`
Node *makeGate(FILE *fp, Node *head, kind_t inGate)
{
char str[17];
Node *new_node = (Node *)malloc(sizeof(Node)); // Node of linkedlist that contains gate structure
new_node->data = (Gate *)malloc(sizeof(Gate)); // Gate structure that keeps information about a gate
new_node->next = head;
new_node->data->kind = inGate;
new_node->data->size = 3;
new_node->data->params = malloc(3 * sizeof(char*));
for (int i = 0; i < 3; i++)
{
new_node->data->params[i] = malloc(17 * sizeof(char));
}
fscanf(fp, "%16s", str);
new_node->data->params[0] = str;
fscanf(fp, "%16s", str);
new_node->data->params[1] = str;
fscanf(fp, "%16s", str);
new_node->data->params[2] = str;
return new_node;
}
`
The printf statement inside the while loop works perfectly fine and is there purely for testing, however the for loop that prints each value of the array is different and prints nothing.
I tried to fix this multiple times to no avail, I originally found this problem as I noticed that I had gotten memory leak, and when I freed where the memory leak should be, it throws that I am freeing a address that is not malloced.
My only thought is I am somehow losing/skipping a node, but I am out of ideas

The following does not copy data from str into the struct ( you'd need strcpy):
new_node->data->params[0] = str;
What it does is copy the address of str into each element. They all point to the same buffer/string. And, str goes out of scope when the function returns.
You can [and should] just scan into the struct directly.
So, change:
fscanf(fp, "%16s", str);
new_node->data->params[0] = str;
fscanf(fp, "%16s", str);
new_node->data->params[1] = str;
fscanf(fp, "%16s", str);
new_node->data->params[2] = str;
Into:
fscanf(fp,"%16s",new_node->data->params[0]);
fscanf(fp,"%16s",new_node->data->params[1]);
fscanf(fp,"%16s",new_node->data->params[2]);

Related

What is wrong with my replace string with another string or character using linked list

I have a linked list with many chars which I input from my input (what is the weather today?), to be replaced with another string (for example what replaced with how, so I get how is the weather today?).
But if the given words are right next to each other for example whatwhat, it will change to howwhat, disregarding the second part.
I think the problem is in the compare function, but I have no clue how to fix it, but the logic of replace should go like this:
If the words from my list and the needed word are the same, then proceed to iterate to the position where the next node of the word that should be changed (unwanted word) should be (pretty much the end of the word), then I create a new linked list with character with the wanted word, and connect temp to the start of the list and the next of the list to the position where the next character of the word that needs to be changed (unwanted word), which I found in the first loop.
Also don't roast my input() function, I know it is unsafe I just want to see what unsafe means with my own eyes, while I still have nothing to lose.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node {
int value_c;
struct node *next_c;
struct node *prev_c;
};
typedef struct node string;
int compare(string *head, char *word) {
int counter = 0;
string *temp = head;
for (int i = 0; i < strlen(word); i++) {
if (temp->value_c == word[i]) {
temp = temp->next_c;
counter++;
}
}
if (counter == strlen(word))
return 1;
else
return 0;
}
void print_c(string *head) {
while (head != NULL) {
printf("%c", head->value_c);
head = head->next_c;
}
}
void append_c(string **head, char thing) {
string *newNode = (string *)malloc(sizeof(string));
newNode->value_c = thing;
newNode->next_c = NULL;
if (*head == NULL) {
*head = newNode;
newNode->prev_c = NULL;
return;
}
string *temp = *head;
while (temp->next_c != NULL)
temp = temp->next_c;
temp->next_c = newNode;
newNode->prev_c = temp;
}
string *replace_all1(string *head, char *what, char *with_what) {
string *temp = head;
while (temp != NULL) {
printf("%c ", temp->value_c);
if (compare(temp, what) == 1) {
printf("%i ", 1);
printf("%c ", temp->value_c);
string *new = temp;
for (int i = 0; i < strlen(what) - 1; i++) {
new = new->next_c;
}
string *word = NULL;
for (int i = 0; i < strlen(with_what); i++) {
append_c(&word, with_what[i]);
}
string *word_temp = word;
while (word_temp->next_c != NULL) {
word_temp = word_temp->next_c;
}
word_temp->next_c = new->next_c;
if (temp->prev_c != NULL) {
temp->prev_c->next_c = word;
} else {
head = word;
print_c(head);
temp = word;
print_c(temp);
word->prev_c = NULL;
}
}
temp = temp->next_c;
}
printf("\n");
return head;
}
string *String(char *str) {
string *st = NULL;
int i = 0;
while (str[i] != '\0') {
append_c(&st, str[i]);
i++;
}
return st;
}
string *input() {
char *a = (char *)malloc(sizeof(char));
scanf("%[^\n]", a); //maximum of 1408
string *stri = String(a);
return stri;
free(a);
}
int main() {
string *list = NULL;
string *big_boy_string = input();
//printf("%c", big_boy_string->value_c);
//print_c(big_boy_string);
//printf("\n");
//printf("%i", compare(big_boy_string, "what"));
//printf("%i ", len(big_boy_string));
//printf("\n");
//print_c(slice(big_boy_string, 1, 10));
//print_c(replace(big_boy_string, 'h', 'a'));
//printf("\n");
//print_c(reverse(big_boy_string));
print_c(replace_all1(big_boy_string, "a", "b"));
//getline();
}
char *a = (char*) malloc(sizeof(char));
scanf("%[^\n]",a); //maximum of 1408
The first statement allocates memory for just 1 byte. So the maximum is not 1408, but 1. It can store a single char, or the null-terminator if it's a string, but no more.
Next, scanf() will write to out of bounds memory, and invoke undefined behaviour. The subsequent functions all depend on this undefined behaviour, so I'm not going to look at them.
But then, you've a memory leak in the same function.
return stri;
free(a);
You return before freeing the allocated memory. The call to free() is never executed.
The return value of malloc() is also ignored. Code risks undefined behaviour if the subsequent dereferences are on a NULL pointer.
Aside: The cast is meaningless and may hide a bug. malloc() and family returns a void * that is implicitly converted to the right type.
Re: Also don't roast my input() function, I know its unsafe I just
want to see what unsafe means with my own eyes.
If you are already aware of this, then you shouldn't be asking why your code doesn't work. You are relying on undefined behaviour (playing with fire).
There is no need to look further than the input function: it has undefined behavior or the worst kind because you attempt to read the input string into a very small array, allocated for a single byte. You must fix this first. Since you know the maximum length of your input string, you can use this:
string *input(void) {
char a[1409];
if (scanf("%1408[^\n]", a) != 1) { //maximum of 1408
// invalid or missing input
return NULL;
}
scanf(%*[^\n]"); // consume any remaining characters on the input line
scanf(%*1[\n]"); // consume the newline if present
return String(a);
}
Here is an alternative using getchar() instead of scanf() which is quite tricky and error prone:
string *input(void) {
char a[1409];
int c;
size_t i = 0;
while ((c = getchar()) != EOF && c != '\n') {
if (i + 1 < sizeof(a))
a[i++] = (char)c;
}
if (c == EOF && i == 0) {
/* end of file without any input */
return NULL;
}
a[i] = '\0';
return String(a);
}
The compare function is incorrect: it should return false as soon as the comparison fails and it must test for the end of string (temp == NULL):
int compare(const string *head, const char *word) {
string *temp = head;
for (size_t i = 0; word[i] != '\0'; i++) {
if (temp == NULL || temp->value_c != word[i])
return 0;
temp = temp->next_c;
}
return 1;
}
The replace_all1() function has problems too:
for (int i = 0; i < strlen(what) - 1; i++) will cause undefined behavior if what is an empty string because strlen(what) - 1 is unsigned with the value SIZE_MAX in this case, causing the loop to proceed for a very long time, well beyond the end of the list pointed to by new.
while (word_temp->next_c != NULL) will cause a undefined behavior if the replaced word is empty as word_temp will be NULL.
once you replace the sublist, you do not update temp correctly to point to the node after the replaced one, which you could achieve by setting temp to word_temp.
the function does not free the replaced sublist.
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct node {
int value_c;
struct node *next_c;
struct node *prev_c;
};
typedef struct node string;
void string_append_char(string **head, int c) {
string *node = malloc(sizeof(*node));
if (node == NULL) {
fprintf(stderr, "out of memory\n");
exit(1);
}
node->value_c = c;
node->next_c = NULL;
if (*head == NULL) {
node->prev_c = NULL;
*head = node;
} else {
string *temp = *head;
while (temp->next_c != NULL)
temp = temp->next_c;
node->prev_c = temp;
temp->next_c = node;
}
}
string *string_new(const char *str) {
string *st = NULL;
for (int i = 0; str[i] != '\0'; i++) {
string_append_char(&st, str[i]);
}
return st;
}
string *string_input(const char *prompt) {
string *st = NULL;
int c;
if (prompt) {
printf("%s", prompt);
}
while ((c = getchar()) != EOF && c != '\n') {
string_append_char(&st, c);
}
return st;
}
void string_print(const char *before, const string *head, const char *after) {
printf("%s", before);
while (head != NULL) {
putchar(head->value_c);
head = head->next_c;
}
printf("%s", after);
}
void string_free(string *head) {
while (head != NULL) {
string *next = head->next_c;
free(head);
head = next;
}
}
int string_compare(const string *head, const char *word) {
const string *temp = head;
for (size_t i = 0; word[i] != '\0'; i++) {
if (temp == NULL || temp->value_c != word[i])
return 0;
temp = temp->next_c;
}
return 1;
}
int string_replace(string **head, const char *what, const char *with_what) {
int count = 0;
if (*what == '\0')
return 0;
string *temp = *head;
while (temp != NULL) {
if (string_compare(temp, what)) {
count++;
// locate the last node of the substring
string *temp_end = temp;
for (size_t i = 0; what[i + 1] != '\0'; i++) {
temp_end = temp_end->next_c;
}
string *next = temp_end->next_c;
if (*with_what == '\0') {
// just delete the substring
if (temp->prev_c != NULL) {
temp->prev_c->next_c = next;
} else {
*head = next;
}
if (next) {
next->prev_c = temp->prev_c;
}
} else {
// create a string from the replacement
string *word = string_new(with_what);
// locate the last node of the new substring
string *word_end = word;
while (word_end->next_c != NULL) {
word_end = word_end->next_c;
}
word->prev_c = temp->prev_c;
if (temp->prev_c != NULL) {
temp->prev_c->next_c = word;
} else {
*head = word;
}
word_end->next_c = next;
if (next) {
next->prev_c = word_end;
}
}
temp_end->next_c = NULL;
string_free(temp);
temp = next;
} else {
temp = temp->next_c;
}
}
return count;
}
int main() {
string *list = string_input("enter string: ");
string_print("input: ", list, "\n");
printf("replacing 'what' to 'how': %d matches\n", string_replace(&list, "what", "how"));
string_print("rep1: ", list, "\n");
printf("replacing 'a' to 'b': %d matches\n", string_replace(&list, "a", "b"));
string_print("rep2: ", list, "\n");
printf("deleting 'h': %d matches\n", string_replace(&list, "h", ""));
string_print("rep3: ", list, "\n");
string_free(list);
return 0;
}
Sample session:
enter string: what is the weather today?
input: what is the weather today?
replacing 'what' to 'how': 1 matches
rep1: how is the weather today?
replacing 'a' to 'b': 2 matches
rep2: how is the webther todby?
deleting 'h': 3 matches
rep3: ow is te webter todby?

Linked list being overwritten instead of attached to another linked list

This is written in C.
I'm trying to take user input and use it to create/add to a linked list, which I point to with struct Node *dict; Everything is accomplished using global memory.
Creating a new linked list works fine, but when the user tries to add to the linked list, it overwrites the extant linked list.
Here's my code for adding to the list (words is an array of nodes to be appended to the list):
if (dict == NULL) { // If previous list does not exist, global dict pointer should point to node array
dict = words;
} else { // Else find end of current linked list and point it to the new list
struct Node *head = dict;
while (head->next != NULL) {
head = head->next;
}
head->next = words;
}
When I create a list with the words "apple orange peach," for example, when I print the list, I get the output "apple orange peach." But then when I add "pear" to the list, "apple orange peach" is overwritten and I only see the output "pear," instead of "apple orange peach pear."
EDIT:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
//// GUI Stuff ////
void drawDashedLine() {
for (int i = 0; i < 30; i++) {
printf("-");
}
printf("\n");
}
void drawDottedLine() {
for (int i = 0; i < 30; i++) {
printf(".");
}
printf("\n");
}
void drawArrowLine() {
for (int i = 0; i < 30; i++) {
printf(">");
}
printf("\n");
}
void drawStarLine() {
for (int i = 0; i < 30; i++) {
printf("*");
}
printf("\n");
}
struct Node {
int length;
char word[5];
struct Node * next;
};
// Pointer to global linked list dictionary
struct Node *dict;
struct Node *newDict;
void printDict() {
drawDottedLine();
struct Node * head = dict;
while (head != NULL) {
printf("%s\n", head -> word);
head = head -> next;
}
drawDottedLine();
return;
}
void alphabetizeDict() { // Bubble sort
//printf("%p --- %p\n", dict, dict->next);
struct Node * head = dict;
if (head == NULL) {
return;
}
struct Node * ptr2 = NULL;
int swapped = 1;
while (swapped) {
swapped = 0;
head = dict;
while (head -> next != ptr2) {
char * temp1 = strdup(head -> word);
char * temp2 = strdup(head -> next -> word);
strupr(temp1);
strupr(temp2);
if (strcmp(temp1, temp2) > 0) {
char temp[5];
strcpy(temp, head -> word);
strcpy(head -> word, head -> next -> word);
strcpy(head -> next -> word, temp);
swapped = 1;
}
head = head -> next;
}
ptr2 = head;
}
return;
}
void createDict() {
// To hold the string entered by the user
char str[5000];
// Holds 1000 words, each up to 5 characters long (4 plus a NULL char)
char newString[1000][5];
printf("\n");
drawArrowLine();
printf("Enter word(s): \n");
fgets(str, sizeof str, stdin);
int i, j, ctr;
j = 0;
ctr = 0; // ctr to iterate through words, j to iterate through letters
for (i = 0; i <= (strlen(str)); i++) {
if (str[i] == ' ' || str[i] == '\0') { // This is whitespace. add null character to terminate string. Start next word
newString[ctr][j] = '\0';
ctr++;
j = 0;
} else { // Else add letter to string
newString[ctr][j] = str[i];
j++;
}
}
for (int i = 0; i < ctr; i++) {
struct Node n;
n.length = strlen(newString[i]);
int c = 0;
char sub[5];
// Only use word's first four letters
while (c < strlen(newString[i]) && c < 4) {
sub[c] = newString[i][c];
c++;
}
sub[c] = '\0';
strcpy(n.word, sub);
n.next = NULL;
if (dict == NULL) {
dict = &n;
} else {
n.next = dict;
dict = &n;
}
}
// alphabetizeDict();
printf("Word(s) added succesfully\n");
drawArrowLine();
printf("\n");
return;
}
void destroyDict() {
printf("Starting new dictionary......\n");
while (dict != NULL) {
struct Node * temp = dict;
dict = dict -> next;
temp -> next = NULL;
}
}
void caseInsensSearch(char * searchTerm) {
for (int i = 0; searchTerm[i]; i++) {
searchTerm[i] = tolower(searchTerm[i]);
}
struct Node * head = dict;
int index = 0;
while (head != NULL) {
char lowercaseWord[5];
for (int i = 0; head -> word[i]; i++) {
lowercaseWord[i] = tolower(head -> word[i]);
}
if (strcmp(lowercaseWord, searchTerm) == 0) {
printf("Found %s at index %i\n", head -> word, index);
drawDashedLine();
return;
}
head = head -> next;
index++;
}
printf("Sorry, I couldn't find %s in your dictionary.\n", searchTerm);
drawDashedLine();
return;
}
void caseSensSearch(char * searchTerm) {
struct Node * head = dict;
int index = 0;
while (head != NULL) {
if (strcmp(head -> word, searchTerm) == 0) {
printf("Found %s at index %i\n", head -> word, index);
drawDashedLine();
return;
}
head = head -> next;
index++;
}
printf("Sorry, I couldn't find %s in your dictionary.\n", searchTerm);
drawDashedLine();
return;
}
void search() {
int isSens;
drawDashedLine();
printf("Enter 1 for Case sensitive\n2 for case insensitive\n");
drawDashedLine();
scanf("%d", & isSens);
while (isSens < 1 || isSens > 2) {
printf("Please enter a number between 1 and 2:\n");
scanf("%d", & isSens);
}
drawDashedLine();
printf("Enter a word to search for:\n");
char searchTerm[5];
scanf("%s", searchTerm);
searchTerm[4] = '\0';
if (isSens == 1) {
caseSensSearch(searchTerm);
} else {
caseInsensSearch(searchTerm);
}
}
int promptUser() {
drawStarLine();
printf("1) Search for a word\n2) Add word(s)\n3) Print dictionary\n4) Start new dictionary\n5) Exit\n");
drawStarLine();
printf("\nEnter a number between 1 and 5:\n");
int choice;
scanf("%1d", & choice);
while (choice < 1 || choice > 5) {
printf("Please enter a number between 1 and 5:\n");
scanf("%d", & choice);
}
return choice;
}
int main() {
for (;;) {
int choice = promptUser();
fflush(stdin);
if (choice == 1) {
search();
} else if (choice == 2) {
createDict();
} else if (choice == 3) {
printDict();
} else if (choice == 4) {
destroyDict();
} else if (choice == 5) {
return 0;
}
}
return 1;
}
I've spent some time diagnosing this problem for you. The problem statement is bizarre... An array of words could be sorted (even with library qsort()) and grow to the fill the array to the brim, but you claim this must use both a linked list and a global "object pool" that is not dynamically allocated...
Here's some code I've compiled BUT NOT TESTED...
It should be simple to follow and expand to accommodate your requirements.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Node { // use 'typdef'. Less typing
char word[5]; // VERY SHORT WORDS guaranteed
struct Node *next;
} Node_t;
Node_t dict[ 1000 ]; // global data space
int nextNode = 0;
void printDict() { // traverse LL outputting value(s)
for( Node_t *pn = dict; pn; pn = pn->next )
puts( pn->word );
}
void addNode( char *data ) {
if( nextNode + 1 >= sizeof dict/sizeof dict[0] )
return; // Fixed size cannot grow.
Node_t *pn = &dict[ nextNode ];
strcpy( pn->word, data ); // "apple" WON'T fit
nextNode++;
//EDIT:
// This is not correct.
// See code block below for correction
if( nextNode +1 >= sizeof dict/sizeof dict[0] )
pn->next = NULL;
else
pn->next = &dict[ nextNode ];
}
void createDict() {
char str[5000]; // one lo-o-o-ong input string of short words
printf( "Enter word(s): \n" );
fgets( str, sizeof str, stdin );
// chop words on spaces (or tabs) and store to LL
for( char *cp = str; ( cp = strtok( cp, " \t" ) ) != NULL; cp = NULL )
addNode( cp );
}
void destroyDict() { // super simple!
memset( dict, 0, sizeof dict );
nextNode = 0;
// return; // Do not need those return(s) before final closing brace.
}
80% of any problem is the clear understanding of what the problem is to begin with.
EDIT: Realising the code must 'straddle' both array and LL, the above was not exactly correct. Below is the necessary fix to conform with a LL having a NULL next pointer at its 'tail' node.
void addNode( char *data ) {
if( nextNode + 1 >= sizeof dict/sizeof dict[0] )
return; // Fixed size cannot grow.
strcpy( dict[ nextNode ].word, data ); // "apple" WON'T fit
if( nextNode ) // at node 1 or greater
dict[ nextNode - 1 ].next = &dict[ nextNode ];
nextNode++;
}
try this
if (dict == NULL) { // If previous list does not exist, global dict pointer should point to node array
dict = words;
} else { // Else find end of current linked list and point it to the new list
struct Node *head = dict;
while (head->next != NULL) {
head = head->next;
}
struct Node *ptr = NULL;
ptr->length = strlen(word);
strcpy(ptr->word, sub);
ptr->next = NULL;
head->next = ptr;
}

Using Trie to Store Word Counts

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.

Sorting and Displaying a Linked List

I am writing a library management program where the user can add books to a database which just means that the program will take the user's input and store it into a text document. Then, when the program starts up it will read through the text document where all the books are stored and build a linked list where each book would be a node. So, I have been able to get to a point where I can read the text file and store the values into nodes. However, when I try testing the bookList function to view the entire book list by title my program crashes. Here is the code:
void loadingMenu(){
FILE *fp = fopen(filename, "r");
int lines = 0;
char line[254];
char *ti = malloc(MAX_STR_LEN);
char *au = malloc(MAX_STR_LEN);
char *ca = malloc(MAX_STR_LEN);
char *id = malloc(MAX_STR_LEN);
char *ti_chopped;
char *au_chopped;
char *ca_chopped;
char *id_chopped;
int id_num;
struct node *tempNode;
while(fgets(line, sizeof(line), fp)){
if(line == 'EOF'){
break;
}
if(lines == 7){
lines = 0;
}
if(lines == 0){
line[strcspn(line, "\n")] = 0; // remove '\n' from string
strcpy(ti, line);
}
else if(lines == 1){
line[strcspn(line, "\n")] = 0;
strcpy(au, line);
}
else if(lines == 3){
line[strcspn(line, "\n")] = 0;
strcpy(ca, line);
}
else if(lines == 6){
line[strcspn(line, "\n")] = 0;
strcpy(id, line);
}
lines++;
if(lines == 6){
// removing the identifiers from each string
ti_chopped = ti + 6;
au_chopped = au + 7;
ca_chopped = ca + 9;
id_chopped = id + 3;
id_num = atoi(id_chopped);
// ------create book node------------
tempNode = malloc(sizeof *tempNode);
// ----------------------------------
tempNode->next = NULL;
tempNode->titleptr = malloc(strlen(ti_chopped) + 1);
strcpy(tempNode->titleptr, ti_chopped);
tempNode->authorptr = malloc(strlen(au_chopped) + 1);
strcpy(tempNode->authorptr, au_chopped);
tempNode->categoryptr = malloc(strlen(ca_chopped) + 1);
strcpy(tempNode->categoryptr, ca_chopped);
tempNode->id = id_num;
//printf("%d", tempNode->id);
head = addNode(head, tempNode);
}
}
fclose(fp);
}
int compareNode(struct node *n1, struct node *n2){
int compareValue = strcmp(n1->titleptr, n2->titleptr);
if(compareValue == 0){
return 0;
}
else if(compareValue < 0){
return -1;
}
else {
return 1;
}
}
struct node *addNode(struct node *list, struct node *node1){
struct node* tmp = list;
if(list == NULL){
return node1;
}
if(compareNode(node1,list) == -1){
node1->next = list;
list = node1;
return list;
}
else
{
struct node* prev = list;
while(tmp != NULL && compareNode(node1, tmp) >= 0){
prev = tmp;
tmp = tmp->next;
}
prev->next = node1;
node1->next = tmp;
return list;
}
}
void bookList(){
system("cls");
struct node *tmp;
tmp = head;
printf("List of all Books: ");
while(tmp != NULL)
{
printf("%s\n", tmp->titleptr);
tmp = tmp->next;
}
printf("\n\nEnd of list.");
}
First off I would like to apologize for the bad code, also I trimmed away some of the fat of the program and just left the functions involved behind.
So, please if you could help me out on this or at least point me in the right direction I would be very grateful. Also, if you have any coding tips or comments go ahead and tell me, I am always hungry to learn!
EDIT: The code now runs and the list will print, however, the strings will not be in alphabetical order. Currently trying to figure that out.
In the first addNode() function, you need to set the previous pointer. That should fix the problem.
if(compareNode(node1,list) == -1)
{
node1->next = list;
list->prev = node1;
list = node1;
return list;
}

Seg Fault in Knowledge Tree

I am implementing a knowledge tree in c that can read from a file. I am getting a seg fault in my newStr function. I'm not able to test the rest of my code with this problem. I don't have much experience with c. Any help would be greatly appreciated.
my .c file
#include
#include
#include"animal.h"
#include
#include
/*returns a new node for the given value*/
struct Node * newNode (char *newValue)
{
struct Node * tree;
tree = (struct Node*)malloc(sizeof(struct Node));
tree -> value = newStr(newValue);
return tree;
}
/* returns a new string with value passed as an argument*/
char * newStr (char * charBuffer)
{
int i;
int length = strlen(charBuffer);
char newStr;
if(charBuffer[0] == 'A' || charBuffer[0] == 'Q'){
for(i=1; i<length; i++)
newStr += charBuffer[i];
}
return (newStr + "\0");
}
/*Read from a File and create a tree*/
struct Node * readATree(FILE * f)
{
char c;
char buffer[100];
struct Node * newTree;
c = fgetc(f);
if (c == 'A'){
fgets(buffer, 100, f);
newTree = newNode(buffer);
newTree -> left = NULL;
newTree -> right = NULL;
}
else{
fgets(buffer, 100, f);
newTree = newNode(newStr(buffer));
newTree->left = readATree(f);
newTree->right = (struct Node *) readAtree(f);
}
return newTree;
}
/*Write Tree to a File*/
void writeAFile(struct Node* tree, FILE * f)
{
char buffer[100];
strcpy(buffer, tree->value);
if(tree != 0){
if(tree->left == NULL && tree->right == NULL){
fputc((char)"A", f);
fputs(buffer,f);
} else{
fputc((char)"Q",f);
fputs(buffer,f);
writeAFile(tree->left, f);
writeAFile(tree->right,f);
}
}
}
/*The play should start from here*/
int main (){
struct Node* node;
struct Node* root;
char ans[100];
char q[100];
FILE * f;
f = fopen("animal.txt", "r+");
if(f != NULL)
readATree(f);
else{
node = newNode("Does it meow?");
node->right = NULL;
node->right->right=NULL;
node->left->left=NULL;
node->left=newNode("Cat");
root = node;
}
while(node->left != NULL && node->right != NULL){
printf(node->value);
scanf(ans);
if(ans[0] == (char)"Y" || ans[0] == (char)"y")
node = node->left;
else if(ans[0] == (char)"N" || ans[0] == (char)"n")
node = node->right;
else
printf("That is not a valid input.\n");
}
if(ans[0] == (char)"Y" || ans[0] == (char)"y")
printf("I win!");
else if(ans[0] == (char)"N" || ans[0] == (char)"n"){
printf("What is your animal");
scanf(ans);
printf("Please enter a yes or no question that is true about %s?\n", ans);
scanf(q);
node->right = newNode(q);
node->right->left = newNode(ans);
node->right->right = NULL;
}
writeAFile(root,f);
fclose(f);
return 0;
}
.h file
#include
struct Node {
char *value;
struct Node * left;
struct Node * right;
};
struct Node * newNode (char *newValue) ;
char * newStr (char * charBuffer);
struct Node * readATree(FILE * f);
void writeAFile(struct Node* tree, FILE * f);
There might be several more, but here's some points on what's wrong:
Your newStr function is just very,
very wrong. At a guess you'd want
something like:
char * newStr (char * charBuffer)
{
char *newStr;
if(charBuffer[0] == 'A' || charBuffer[0] == 'Q') {
newStr = strdup(&charBuffer[1]);
} else {
newStr = strdup("");
}
if(newStr == NULL) {
//handle error
}
return newStr;
}
You can't cast a string to a char
like you do here:
if(ans[0] == (char)"Y" || ans[0] == (char)"y")
Do instead(same for similar code
elsewhere too)
if(ans[0] =='Y' || ans[0] == 'y')
Same as above when you call putc,
don't do
fputc((char)"A", f);
Do
fputc('A', f);
scanf needs a format string, don't
do:
scanf(ans);
Do e.g. (or just use fgets again)
if(scanf("%99s",ans) != 1) {
//handle error
}
char * newStr (char * charBuffer)
{
int i;
int length = strlen(charBuffer);
char newStr;
if(charBuffer[0] == 'A' || charBuffer[0] == 'Q'){
for(i=1; i<length; i++)
newStr += charBuffer[i];
}
return (newStr + "\0");
}
Well, there's a few interesting things here... To get down to brass tacks, you're trying to copy the contents of a character pointer into another and this function isn't going to do that. All you're really doing is summing the value of each char in charBuffer into newStr because a char is really just an 8-bit integer and then you return that integer as a pointer through an implicit cast so it is now being treated as a memory address.
You should look to use strdup(), as has been noted, since this is exactly what the function is supposed to do. No need to reinvent the wheel. :)
"+" operator as string concatenation does not work in c.
If you actually want to copy the a string use strdup(). This function allocates memory and copies the string into it.
Don't forget to free the allocated memory when done using it.

Resources