简体   繁体   中英

Why is malloc allocating an address that is already being used?

I am writing a linked list that has a struct for a node and a struct for a list I am getting a problem where when I malloc a new node it has the same address of the list->head hence over writing the list head making the list wrong.

driver.c

#include "target.h"



int main(int argc, char * argv[]){
  struct target_list * target = target_list_alloc("list");
  target_list_print(target);
  target_list_append(target, "G");
  target_list_append(target, "B");
  target_list_print(target);
  target_list_append(target, "S");  
  target_list_print(target);
  target_list_remove(target,"B");
  target_list_print(target);
  target_list_remove(target,"Bl");
  target_list_remove(target,"Br");
  target_list_print(target);
  target_list_append(target,"Ba"); //Here is the problem node
  target_list_print(target);
  return 0;
}

target.h

#ifndef TARGET_H
#define TARGET_H


#include <stdbool.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

/*-----------------------------------------*/

extern char * prog;

/*-----------------------------------------*/

struct source_list{
  char * name;
};
struct recipe_list{
  char * name;
};

struct target_node{
  char * name;
  struct target_node * next;
  struct source_list * src_list;
  struct recipe_list * rec_list;
};

struct target_list{
  char * name;
  struct target_node * head;
  struct target_node * tail;
};

/*-----------------------------------------------------*/

void target_list_init(struct target_list * list, const char * targetname);
struct target_list * target_list_alloc(const char * targetname);
void target_list_deallocate(struct target_list * list);
void target_list_print(struct target_list * list);
void target_list_append(struct target_list * list, const char * nodename);
bool is_in_target_list(struct target_list * list, const char * nodename);
void target_list_remove(struct target_list * list, const char * nodename);

/*-----------------------------------------------------*/

#endif

target.c

#include "target.h"

/*----------------------------------------------------------*/

//This function will initialize a new target_list with name targetname
//This function will assume that target_list is already allocated
void target_list_init(struct target_list * list, const char * targetname){
  verify(list != NULL, "null arg list");
  verify(targetname != NULL, "null arg targetname");
  verify(targetname[0] != '\0',"empty arg targetname");
  list->name = Strdup(targetname);
  list->head = list->tail = NULL;
}

/*----------------------------------------------------------*/

//This function will allocate a new target_list and return a pointer to it
struct target_list * target_list_alloc(const char * targetname){
  verify(targetname != NULL, "null arg targetname");
  verify(targetname[0] != '\0',"empty arg targetname");
  struct target_list * list = malloc(sizeof(struct target_list));
  list->name = Strdup(targetname);
  list->head = list->tail = NULL;
  return list;
}    

/*---------------------------------------------------------*/

//This function will deallocate a target_list
void target_list_deallocate(struct target_list * list){
  verify(list != NULL,"null arg list");
  free(list->name);
  struct target_node * prev = NULL;
  for(struct target_node * p = list->head; p != NULL; p= p->next){
    free(prev);
    free(p->name);
    prev = p;
  }
  free(prev);
  free(list);
}

/*----------------------------------------------------------*/

//This function will print a target_list 
void target_list_print(struct target_list * list){
  verify(list != NULL, "null arg list");
  printf("list of targets: %s\n",safe_string(list->name));
  if(list->head == NULL){
    printf("  <empty>\n");
  }
  else{
    for(struct target_node * p = list->head; p != NULL; p = p->next){
      printf("  %s\n",p->name);
    }
  }
}

/*-----------------------------------------------------------*/

//This function will append a new target_node onto target_list at the end of it
void target_list_append(struct target_list * list, const char * nodename){
  verify(list != NULL, "null arg list");
  verify(nodename != NULL, "null arg nodename");
  verify(nodename[0] != '\0', "empty arg nodename");

  struct target_node * new_node = malloc(sizeof(struct target_node));
  new_node->next = NULL;
  new_node->name = Strdup(nodename);
  new_node->src_list = NULL;
  new_node->rec_list = NULL;
  if(list->head == NULL){
    list->head = list->tail = new_node;
  }
  else{
    list->tail->next = new_node;
    list->tail = new_node;
  }
}

/*--------------------------------------------------------*/

//This function returns 1 if the nodename is already in the target_list and 0 if not
bool is_in_target_list(struct target_list * list, const char * nodename){
  verify(list != NULL, "null arg list");
  verify(nodename != NULL, "null arg nodename");
  verify(nodename[0] != '\0', "empty arg nodename");
  for(struct target_node * p = list->head; p != NULL; p = p->next){
    if(strcmp(nodename,p->name) == 0){
      return 1;
    }
  }
  return 0;
}

/*------------------------------------------------------*/

//This function removes a node with name nodename from target_list */
void target_list_remove(struct target_list * list, const char * nodename){
  verify(list != NULL, "null arg list");
  verify(nodename != NULL, "null arg nodename");
  verify(nodename[0] != '\0', "empty arg nodename");
  if(is_in_target_list(list,nodename)){
    struct target_node * prev = NULL;
    struct target_node * cur = list->head;
    while(cur != NULL){
      if(strcmp(cur->name,nodename) == 0){
    break;
      }
      prev = cur;
      cur = cur->next;
    }
    //case 1: removing head pointer
    if(cur == list->head){
      free(cur->name);
      free(cur->src_list);
      free(cur->rec_list);
      free(cur);
      list->head = NULL;
      list->tail = NULL;
      free(prev);
      cur = NULL;
      prev = NULL;
    }
    //case 2: removing tail pointer
    else if(cur == list->tail){
      free(cur->name);
      free(cur->src_list);
      free(cur->rec_list);
      free(cur);
      list->tail = prev;
      free(prev);
      prev = NULL;
      cur = NULL;
    }
    //case 3: removing a middle node
    else{
      prev->next = cur->next;
      free(cur->name);
      free(cur->src_list);
      free(cur->rec_list);
      free(cur);
      cur = NULL;
      free(prev);
      prev = NULL;
    }
  }
  else{
    fprintf(stderr,"%s: Error %s is not in %s, cannot remove it from %s\n",prog,nodename,list->name,list->name);
  }
} 

/*----------------------------------------------------*/

There are a couple of helper functions defined else where (verify,..) but they don't affect malloc

compile:

gcc -Wall -Wextra -std=c99 -g -o test driver.c target.c cmpsc311.c
driver.c:5: warning: unused parameter ‘argc’
driver.c:5: warning: unused parameter ‘argv’

output:

list of targets: list
  <empty>
list of targets: list
  G
  B
list of targets: list
  G
  B
  S
list of targets: list
  G
  S
[no name]: Error Bl is not in list, cannot remove it from list
[no name]: Error Br is not in list, cannot remove it from list
list of targets: list
  G
  S
list of targets: list
  Ba

After running gdb and looking at the list, list->head,list->tail and the new_node (Ba) I don't know why new_node gets the address of the list->head when malloc ed

gdb:

78    struct target_node * new_node = malloc(sizeof(struct target_node));
4: new_node = (struct target_node *) 0x3a00000000
3: list->tail = (struct target_node *) 0x100100940
2: list->head = (struct target_node *) 0x1001008e0
1: list = (struct target_list *) 0x1001008b0
(gdb) n
79    new_node->next = NULL;
4: new_node = (struct target_node *) 0x1001008e0
3: list->tail = (struct target_node *) 0x100100940
2: list->head = (struct target_node *) 0x1001008e0
1: list = (struct target_list *) 0x1001008b0
(gdb) n

Can anyone tell me why this is and how to fix it? Thank you

Your target_list_remove() function looks bogus. After traversing the list, prev points to the element to be deleted ( prev = cur; ) and cur points to the next one, while prev should point to the element before the one to be deleted.

Also, you are calling free() on cur and prev , but my guess is that you only want to remove one element.

Fix your pointers and call free() only once.

The problem is in your remove function. You are calling free(prev) so in the call target_list_remove(target,"B"); the head of the list is being freed as well. The allocator is then reusing the storage for your next call to malloc . You can use a tool such as valgrind to debug memory problems like this.

When you are removing "B" in target_list_remove it would fall to " case 3: removing a middle node " because your list looks like this at that point:

G -> B -> S

In that section of code you remove the cur node from the list ( prev->next = cur->next ) and continue to free the resources for the current node. At which point your list looks like this:

G -> S

...but after freeing the cur node you proceed to free the prev node (which is the list head) and set it to null.

When appending the "Ba" node when it hits the condition list->head == NULL it is true and thus sets the head and tail of the list to the new "Ba" node.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM