0

I have a structure node which is used to create a binary search tree. Inside each node I am storing a integer KEY and a corresponding string value. I am performing some searches within the tree and wish to return arrays containing key value pairs of only specific nodes.

TO do so I am passing arrays by reference and saving the integer KEY to that array. This works fine, however when I try to the the same with the string I am getting poor results.

In the below code I am trying to copy the string inside root[count].value; to p_value_arr[*p_unique_count] which is a char array.

Struct definition:

   typedef struct node {
        int KEY;
        char *value;
        int node_count;
        struct node *left, *right;
        int unique_count;
    } node;

Function to traverse graph and copy unique key value pairs. KEY is being copied correctly to an array while value is not.

void unique_key(node *root, int *p_unique_count, int p_unique_arr[], char *p_value_arr[]) { 
    int count = 0;      
    //unique *temp = (unique *)malloc(n * sizeof(unique));
        
    if (root != NULL)
    {
        unique_key(root->left, p_unique_count, p_unique_arr, p_value_arr);
        if (root->node_count == 1) {

            root[count].unique_count = *p_unique_count;
            p_unique_arr[*p_unique_count] = root[count].KEY;
            printf("%s\n", root[count].value);
            //"warning: assignment makes integer from pointer without a cast"
            strcpy(p_value_arr[*p_unique_count],root[count].value);  
                        
            printf("%d(%d) -> %s %d\n", root->KEY, root->node_count, root->value, root->unique_count);
                        (*p_unique_count)++;
            count++;
        }
        unique_key(root->right, p_unique_count, p_unique_arr, p_value_arr);
    }
}

A utility function to insert a new node with given key in BST

node* insert_node(node* node, int key, char *value)
{
    /* If the tree is empty, return a new node */
    if (node == NULL) 
        return newNode(key,value);

    // If key already exists in BST, icnrement count and return 
    if (key == node->KEY)
    {
        (node->node_count)++;
    //  return node;
    }

    /* Otherwise, recur down the tree */
    if (key < node->KEY)
        node->left = insert_node(node->left, key, value);
    else
        node->right = insert_node(node->right, key, value);

    /* return the (unchanged) node pointer */
    return node;
}

node *newNode(int KEY, char *value)
{
    struct node *temp = (struct node *)malloc(sizeof(struct node));
    temp->KEY = KEY;
    strcpy(temp->value, value);
    temp->left = temp->right = NULL;
    temp->node_count = 1;
    return temp;
}

Main driver code

int main() {
    int unique_count = 0;
    int in_count = 0;
    int unique_arr[10]; /
    char *value_arr[10];   // an array of pointers  
    
    /* Let us create following BST.  Passing values along with key */
    node *root = NULL;
    
    //this is for storing commands 
    root = insert_node(root, 2, "Hello");
    root = insert_node(root, 3, "Thanks");

    printf("\nkeys of the given tree \n");
    unique_key(root, &unique_count, unique_arr, *value_arr);
    for(int i = 0; i < 10; i++) {                
        printf("%d %s\n", unique_arr[i], value_arr[i]);  //Mismatching the argument type "char" and conversion specifier "s" and nothing prints here
    }
   
}

Output:

Hello

keys of the given tree

Segmentation fault

Any suggestions on how I can effectively copy a string inside a struct member to an array of chars?


EDIT:

Full code: https://pastebin.com/CB4Gp0gY

Since char *value_arr[10]; is an array of pointers I followed chapter 5.6 of K&R The C programming language to pass the array of pointers to the function. I get no warnings now, but the seg fault persists.

I also have more warnings set on my NetBeans 8.2.

Output from debugger:

/cygdrive/C/Users/****/AppData/Roaming/NetBeans/8.2/bin/nativeexecution/dorun.sh: line 71: 16516 Segmentation fault      (core dumped) sh "${SHFILE}"
Community
  • 1
  • 1
rrz0
  • 2,182
  • 5
  • 30
  • 65
  • You are assigning values of type `char *` to array elements of type `char`. If your compiler is not emitting warnings about that, then turn up the warning level. In any case, that appears to be your problem. – John Bollinger Dec 07 '18 at 15:17
  • 3
    `temp->value = value;` this is a soft copy of the pointer, not a copy of the string itself. Meaning all your value pointers will potentially point at the same text. See [How to correctly assign a new string value?](https://stackoverflow.com/questions/3131319/how-to-correctly-assign-a-new-string-value) If you are coming from some higher-level language, you need to acknowledge that C does not have a string class. – Lundin Dec 07 '18 at 15:19
  • @JohnBollinger, I upped my warnings and now I do get "warning: assignment makes integer from pointer without a cast" – rrz0 Dec 07 '18 at 15:21
  • So fix it. By providing an array whose elements match the type of the values you are assigning. – John Bollinger Dec 07 '18 at 15:26
  • That will also correct the problem of a mismatch between your `printf` format and the corresponding arguments. – John Bollinger Dec 07 '18 at 15:27
  • @JohnBollinger, for some reason I still get the warning when I have `char *value;` in my struct and `char *value_arr;` in main function. Both are pointers to char as expected by [strcpy](https://www.tutorialspoint.com/c_standard_library/c_function_strcpy.htm). – rrz0 Dec 07 '18 at 16:32
  • You are not copying from `value` to `value_arr`. You are copying from `value` to `value_arr[i]`. `value_arr[i]` is *not* a pointer, it is a single `char`. Switching `value_arr` from an array to an (allocated) pointer makes no difference at all in that respect. An array of pointers would be declared like so: `char *value_arr[NUMBER_OF_ELEMENTS];`. – John Bollinger Dec 07 '18 at 16:42
  • @JohnBollinger thanks for taking the time to comment. I understand what you are saying, and it makes sense. However on editing the code as suggested (shown above) the error persists. I am misunderstanding something more fundamental here. – rrz0 Dec 07 '18 at 16:53
  • 1
    The compiler is your friend. Ensure that the warning levels are turned up, and *pay attention to the warnings it issues*. In particular, pay attention to (and resolve) the warnings you should now be getting about mismatched pointer types in the call to function `unique_key`. – John Bollinger Dec 07 '18 at 16:59
  • Warning levels should be at their highest. Indeed the warnings were as you suggest and thus `*value_arr` was passed. However when I wrote before, the warning persisted with the change already in place. Strangely I get no other warnings when declaring the function. The strcpy is the only warning I receive – rrz0 Dec 07 '18 at 17:08
  • 1
    Why is this not a specific programming question? I am unable to use `strcpy()` to copy strings from a struct member to an array. Please advise and I will try to make the question more specific. – rrz0 Dec 07 '18 at 17:31
  • I have to ask "what does the debugger say?" When you segfault, it generally will point to the bad memory read/write. From there, with data breakpoints, you can often find who read or wrote out of bounds. – Michael Dorgan Dec 07 '18 at 18:03
  • @MichaelDorgan I updated the question, with the output from the debugger. That is all the information I get. WIll see if I can see what line 71 of `dorun.sh` is doing. – rrz0 Dec 07 '18 at 18:06
  • 1
    I don't see where you actually allocate any space for the char arrays. I see you allocating space for a node, and then immediately copying a string to an unitialized (and potentially completely random) pointer. See @Lundin's link. – Phil M Dec 07 '18 at 18:10
  • Also, I cannot see how you could get that print out with the given driver. No where does the string Deepest Unique exist, unless it is from a higher level. Also, if you cannot debug, start adding tracing printf calls. That should help you to see when an assignment has gone wrong and when the last bit of pre-fault work occurred. And I also missed that you need to allocate a new structure and the internal string if you are using char * to represent. – Michael Dorgan Dec 07 '18 at 18:11

3 Answers3

0

Gonna follow up for Lundin here

node *newNode(int KEY, char *value)
{
  // Allocate a new overall structure
  struct node *temp = (struct node *)malloc(sizeof(struct node));

  //Copy the integer key
  temp->KEY = KEY;

  // uh oh - copy the given string into a random location in memory and segfault.
  // Hint - you need to allocate enough memory to hold the incoming string.
  // Advanced hint - If you don't want to make a copy of the string, you can 
  // just store its pointer, but it would want to be marked constant at the least... 
  strcpy(temp->value, value);

  // Set tree stuff and count, but we are already dead...
  temp->left = temp->right = NULL;
  temp->node_count = 1;
  return temp;
}

Also,

printf("%d %s\n", unique_arr[i], value_arr[i]);  //Mismatching the argument type "char" and conversion specifier "s" and nothing prints here

Will fail because value_arr[i] is not a string, it is a char *. For this to work, it would have to point at a valid C string, or would need to point to memory that has a properly '\0' terminated string.

Take a look at his given link as you need a deeper understanding of how C strings work.

Michael Dorgan
  • 12,453
  • 3
  • 31
  • 61
  • Thanks for your answer. I am printing `root[count].value` correctly, after I store using `temp->value = value;`. Wouldn't this indicate that `*newNode` is storing the string correctly? – rrz0 Dec 07 '18 at 18:24
  • As-is, you are invoking undefined behavior (UB) with your `strcpy()`. As such, anything can happen. Since you are having issues with string memory management, consider using fixed size char arrays until you get the hang of them. – Michael Dorgan Dec 07 '18 at 18:27
0

char *value_arr[10]; // the problem is here

That initializes an array of pointers but does not assign memory to those pointers before using them for stuff like strcpy(). As per K&R chapter 5.6 one should allocate memory using alloc() for the pointer array inside the function.

If you want a pointer to point to some memory for storing a string, then you have to create such an area of memory and set the pointer to point to it.

 char *p;

 alloc(strlen(root[count].value) +1);

 strcpy(p, root[count].value);
 p_value_arr[*p_unique_count] = p;
rrz0
  • 2,182
  • 5
  • 30
  • 65
-1
struct node_t*curr = head;

  struct node_t*node = (struct node_t*)malloc(sizeof(struct node_t));
  strcpy(node -> str,str);
  node -> prev = NULL;
  node -> next = NULL;

  if(curr == NULL)
  {
      head = node;
      tail = node;

      return
  }

  int value = strcmp(curr -> str,str);

  if(value>0)
  {
    head = node;
    node -> next = curr;
    curr -> prev = node;
    return;
  }

  struct node_t* prev = curr;
  curr = prev -> next;

  while(curr != NULL)
  {
    value=strcmp(prev -> str,str);
    if(value < 0)
    {
      int value1 = strcmp(curr -> str,str)

      if(value1>0)
      {
        node -> prev = prev;
        node -> next = curr;
        node -> next = node;
        node -> prev = node;
      }
      else if(value1 == 0)
      {
        if(curr -> next == NULL)
          tail=prev;

        prev -> next = curr -> next;
        curr -> prev = NULL
        return;

      }
    }
    prev = curr;
    curr = prev -> next;
  }
  prev -> next = node;
  node -> prev = prev;
  tail = node;
eljok7
  • 1