1

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.

Ash
  • 21
  • 2
  • 4
  • Note that they say [you shouldn't cast the result of `malloc()` in C](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc). – MikeCAT Mar 08 '16 at 02:03
  • @MikeCAT. I know that it is an error, but I'm also not sure how to fix it as well. The & is how I normally store values for variables, but for char arrays, should it only be fscanf(ifp, "%s", input) then? Also, for that 3rd comment, should I maybe then just use a loop and assign the char values to ensure that I also copy the null zero? – Ash Mar 08 '16 at 02:20
  • Yes, it should be. `&` is used to retrieve pointers to objects (variables). You use `&hoge` to retrieve the pointer to `int hoge;`. Arrays are automatically converted to pointers to the first elements of the arrays, so you don't have to use `&` to retrieve the pointers for passing to `fscanf()`. – MikeCAT Mar 08 '16 at 02:22

1 Answers1

0

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 */
}
MikeCAT
  • 73,922
  • 11
  • 45
  • 70
  • Thank you. I'm glad to know that I wasn't that off in my thinking of how to do the dynamically allocated string, but just off in the way I was handling the pointers and the mallocs. Thank you for the help. – Ash Mar 08 '16 at 03:53