Search code examples
c++stringdoubly-linked-list

How to resolve free(): invalid pointer error while assigning multiple variables in a doubly linked list?


C++ newbie here.

I have 2 data variables in my doubly linked list; instr_num and opcode. When I copy a value into instr_num, it works, but throws an error when I do it for opcode.

struct Node {
    int instr_num;
    std::string opcode;
    struct Node* next;
    struct Node* prev;
};

void initialize_DLL(Node** tail, Node** head, int s_instr_num, string s_opcode) {
    Node* new_node = (Node*) malloc(sizeof(Node));
    if (new_node == NULL) {
        exit(1);
        return;
    }
    
    new_node->instr_num = s_instr_num; // THIS EXECUTES
    new_node->opcode = s_opcode;        // THIS THROWS AN ERROR:  free(): invalid pointer
    new_node->prev = NULL;
    new_node->next = NULL;
    
    *tail = new_node;
    *head = new_node;
}

int main(){
    Node* tail = NULL;
    Node* head = NULL;
    std::string temp_opcode = "ADD"
    
    initialize_DLL(&tail, &head, 1, temp_opcode);
    return 0;
}

I'm guessing it might have to do something with malloc, but I'm not sure. What am I doing wrong?


Solution

  • In C++, you would use new to initialize the allocated memory:

    #include <string>
    #include <utility>
    
    struct Node {
      int instr_num;
      std::string opcode;
      Node* next;
      Node* prev;
    };
    
    void initialize_DLL(Node** tail, Node** head, int s_instr_num,
                        std::string s_opcode) {
      *tail = *head =
          new Node{s_instr_num, std::move(s_opcode)};  // NULLs are implicit
    }
    
    int main() {
      Node* tail = NULL;
      Node* head = NULL;
      std::string temp_opcode = "ADD";
    
      initialize_DLL(&tail, &head, 1, temp_opcode);
    
      delete tail;  // don't leak
    }
    

    Note that playing with raw pointers is dangerous.

    In C, you would use a flexible array member:

    #include <stdlib.h>
    #include <string.h>
    
    struct Node {
      int instr_num;
      struct Node* next;
      struct Node* prev;
      char opcode[];
    };
    
    void initialize_DLL(struct Node** tail, struct Node** head, int s_instr_num,
                        char const* s_opcode) {
      size_t str_sz = strlen(s_opcode) + 1;
      *tail = *head = malloc(sizeof(struct Node) + str_sz);
    
      if (!*head)
        return;
    
      (*head)->instr_num = s_instr_num;
      (*head)->next = NULL;
      (*head)->prev = NULL;
      memcpy((*head)->opcode, s_opcode, str_sz);
    }
    
    int main() {
      struct Node* tail = NULL;
      struct Node* head = NULL;
      char const* temp_opcode = "ADD";
    
      initialize_DLL(&tail, &head, 1, temp_opcode);
    
      free(head);
    }
    

    The "flexible array member" concept doesn't exist in C++. You could use an array of 1 char to emulate something similar but indexing other characters would be undefined behavior according to the C++ standard. So, don't use that in C++, unless your compiler explicitly allows and you're OK with non-portable code.