1
int main(int argc, char *argv[])
{
  printf("successfully started main\n");
  struct uf_list myList;
  uf_list_allocate(&myList);
  printf("successfully allocated myList\n");
  insert_node(&myList, 'c');
  printf("successfully inserted into myList\n");

  return 0;
}

...

void uf_list_allocate(struct uf_list *list)
{
  list = malloc(sizeof(struct uf_list));
  if(list == NULL)
    {fprintf(stderr, "no memory for allocate");}
  list->head = list->tail = NULL;
}
//--------------------------------------------------------------------------------------
void insert_node(struct uf_list *list, const char label)   
{
  struct uf_node *it = malloc(sizeof(struct uf_node));
  if(it == NULL)
    {fprintf(stderr, "no memory for insert");}

  it->c = label;
  it->next = NULL;                                     
  it->rep = NULL;

  if(list->head == NULL)                                //the list is empty
    { list->head = list->tail = it;}
  else
    { list->tail->next = it; list->tail = it; }

  it->rep = list->head;
}
/*----------------------------------------------------------------------------*/
struct uf_node
{
  char c;
  struct uf_node *next;
  struct uf_node *rep;
};
/*----------------------------------------------------------------------------*/
struct uf_list
{
  struct uf_node *head;
  struct uf_node *tail;
};

I am getting a segmentation fault once I try to insert an element into my list from main. What is causing the segmentation fault? If you need any more information such as the definitions of the structs let me know!

EDIT: I realize what I did. Inside allocate I changed the address of local variable list. This means that nothing has happened to myList. However, now I have the following puzzle: I placed the declaration of myList outside of main, and everything works:

struct uf_list myList;

int main(int argc, char *argv[])
{
  printf("successfully started main\n");
  uf_list_allocate(&myList);
  printf("successfully allocated myList\n");
  insert_node(&myList, 'c');
  insert_node(&myList, 'd');
  insert_node(&myList, 'e');
  printf("successfully inserted into myList\n");
  print_uf_list(&myList); 


  return 0;
} 

I can't quite figure out why. It appears that the same logic should apply, namely, since I pass the address of myList into allocate but then change the local variable list address and operate on that address, how is this being reflected on myList whose memory address is not being operated on?

CodeKingPlusPlus
  • 15,383
  • 51
  • 135
  • 216
  • 3
    -1: You have 1.2k rep, surely you know better than to ask a question about a seg-fault without doing some basic debugging first. – Oliver Charlesworth Mar 31 '14 at 19:57
  • 1
    I put a bunch of print statements everywhere with no luck... I rarely program in C, most everything I do is in higher level languages. Everything here seems logical to me. I don't know what else to do... – CodeKingPlusPlus Mar 31 '14 at 19:59
  • step through and see what the last line that executes is maybe? and where's the declaration of the list struct? The problem may well be start from uf_list_allocate() which you didn't post. – RobP Mar 31 '14 at 20:00
  • BTW if malloc fails, the code should not continue. Best oractice... – Gábor Buella Mar 31 '14 at 20:02
  • It's a doubly linked list right? so `it->rep` should point to the previous node and not the `list->head`? – avmohan Mar 31 '14 at 20:02
  • I want `rep` to always point to the head of the list. – CodeKingPlusPlus Mar 31 '14 at 20:05
  • ok..That's a little weird. There's no need to have a `rep` in each node to point to the head. You already have a `head` pointer so the `rep`s are just wasting memory AFAIK. – avmohan Mar 31 '14 at 20:25
  • I'm actually implementing a more involved data-structure using linked-lists. I will have another structure that will have pointers to the nodes. Now, from the other structure it can find what the head is in constant time rather than potentially traversing the entire list. – CodeKingPlusPlus Mar 31 '14 at 20:29
  • OK. But can't it be done by simply passing the list pointer `struct uf_list *myList` also to the other structure? It saves 8n bytes of memory and can find head in O(1) time? – avmohan Mar 31 '14 at 20:50

2 Answers2

1

In allocate, you dont return anything. Tat is the problem. In main, you should have just a pointer as local variable, and assign to it what the allocator function returns.

EDIT

Even more simple, since it is already allocated ( on the stack of main ), you can just remove the allocating code from that function, and have an initializing function. This is all you need:

  Uf_list_init(struct uf_list *list)
 {
    list->head = list->tail = NULL;
 }

In the original code:

list = malloc(sizeof(struct uf_list));

You have a pointer to te struct, but you overwrite it with a brand new pointer.

Gábor Buella
  • 1,840
  • 14
  • 22
  • Wait, I passed the address of `myList`, so why can't I change the values for `head` and `tail`? – CodeKingPlusPlus Mar 31 '14 at 20:15
  • You can. But in your original example, you did overwrite your original pointer, so you didn't modify your original struct. You just allocated memory, and threw away the pointer, leaking memory... – Gábor Buella Mar 31 '14 at 20:17
  • The point is: in C, you get a copy of the argument on new stack of the new function you call, and you don't have access to the original variable. But if you use a new address, while throwing away the original address.... You get it – Gábor Buella Mar 31 '14 at 20:22
  • Is this what happened? I passed a memory address into the function by value. I changed the value of this memory address, so the `struct` in main never got changed? – CodeKingPlusPlus Mar 31 '14 at 20:26
  • If I place the declaration of `myList` outside of `main` it works. Why is this the case? It will be a global variable in the data-segment with an address. If I pass the address into the function, the same thing should happen. – CodeKingPlusPlus Mar 31 '14 at 20:35
  • It shouldnt. Can you post that version of the code? – Gábor Buella Mar 31 '14 at 20:42
  • Also, likely your envirnment initializes global vars with zero bytes, so your var has NULL head and tail in it already. – Gábor Buella Mar 31 '14 at 20:43
  • 1
    I dont recall exactly, but that might be part of the standard, you can google it. But variables on stack surely don't get automatically inited with any values, they just start their life filled with random bits. – Gábor Buella Mar 31 '14 at 20:46
  • 1
    Yup, found it http://stackoverflow.com/questions/14049777/why-global-variables-are-always-initialized-to-0-but-not-local-variables – Gábor Buella Mar 31 '14 at 20:48
  • 1
    Also note, NULL pointer is not required to be equal to zero, so relying on zero filled memory containing NULL pointers is not strictly portable, infect against best practice. So please don't learn C relying on it. – Gábor Buella Mar 31 '14 at 20:51
  • Does this mean that when it is placed outside of main it already gets allocated and sets head and tail to NULL, thus making `allocate` superfluous? – CodeKingPlusPlus Mar 31 '14 at 20:58
  • Yes, it does. Today, in your computer, in your town, in this universe, yes. But first, the code is much much more readable if you implicitly assign NULL to thtose pointers, and second, NULL can be something other other than zero next time. Not likely, and I think POSIX C requires it to be zero, but not standard C. I can think of an Atari 2600, where the address zero can be a useful address, so maybe in a compiler targeting it NULL==255 . Or quantum computers in the future might have infinite as NULL or whatever. – Gábor Buella Mar 31 '14 at 21:08
  • 1
    Anyways, it is very bad practice to rely on it, allocate is not strictly superfluous, eliminating it this way is an ugly hack. – Gábor Buella Mar 31 '14 at 21:09
0

C passes parameters by value. uf_list_allocate should take uf_list **listRef so that it can be modified.

#include <stdio.h>
#include <stdlib.h>

struct uf_node
{
  char c;
  struct uf_node *next;
  struct uf_node *rep;
};

struct uf_list
{
  struct uf_node *head;
  struct uf_node *tail;
};

void uf_list_allocate(struct uf_list **listRef)
{
  *listRef = malloc(sizeof(struct uf_list));
  if(*listRef == NULL)
    {fprintf(stderr, "no memory for allocate"); exit(0);}
  (*listRef)->head = (*listRef)->tail = NULL;
}

void insert_node(struct uf_list *list, const char label)
{
  struct uf_node *it = malloc(sizeof(struct uf_node));
  if(it == NULL)
    {fprintf(stderr, "no memory for insert"); exit(0);}

  it->c = label;
  it->next = NULL;
  it->rep = NULL;

  if(list->head == NULL)                                //the list is empty
    { list->head = list->tail = it;}
  else
    { list->tail->next = it; list->tail = it; }

  it->rep = list->head;
}

int is_empty(const struct uf_list *list)
{
  return list->head == NULL;
}

void remove_node(struct uf_list *list)
{
  if (is_empty(list))
  {
    printf("List underflow\n"); exit(0);
  }
  else
  {
    struct uf_node *oldhead = list->head;
    list->head = list->head->next;
    if (list->tail == oldhead)
      list->tail = NULL;
    free(oldhead);
    printf("Node removed\n");
  }
}

void deallocate(struct uf_list **listRef)
{
  struct uf_list *list = *listRef;
  if(!is_empty(list))
  {
    while(!is_empty(list))
      remove_node(list);
  }
  free(list);
  list = NULL;
  printf("List deallocated\n");
}

void printList(const struct uf_list *myList)
{
  struct uf_node *cur = myList->head;
  while(cur!=NULL)
  {
    printf("%c -> ", cur->c);
    cur = cur->next;
  }
  printf("\n");
}

int main(int argc, char *argv[])
{
  printf("successfully started main\n");
  struct uf_list *myList;
  uf_list_allocate(&myList);
  printf("successfully allocated myList\n");

  insert_node(myList, 'c');
  printf("successfully inserted c into myList\n");

  insert_node(myList, 'd');
  printf("successfully inserted d into myList\n");
  printList(myList);

  insert_node(myList, 'e');
  printf("successfully inserted e into myList\n");
  printList(myList);

  remove_node(myList);
  printf("successfully removed c (head) from myList\n");
  printList(myList);

  deallocate(&myList);

  return 0;
}
avmohan
  • 1,820
  • 3
  • 20
  • 39
  • 1
    It is a stack variable of main, can't modify its address – Gábor Buella Mar 31 '14 at 20:11
  • Of course, you should make appropriate changes in main also. – avmohan Mar 31 '14 at 20:15
  • I just placed the declaration of `myList` outside of main, and it works. Why? Global variables are allocated in the data segment correct? – CodeKingPlusPlus Mar 31 '14 at 20:17
  • You can't modify `myList` in a function unless it's a global variable or it's passed by reference. I've addded a bit of code to `remove_node` (removes head node), `deallocate` (empties & deallocates the whole list) and a few more examples. It makes it a bit clearer. Hope you get it now. I'm posting the whole code - `insert_node` is unchanged except that it aborts execution if `malloc` fails. – avmohan Mar 31 '14 at 20:43