Mysterious segmentation fault while creating a Linked List (Add function) - c

Im trying to create a Linked List in C but the program crashed due to some mysterious fault.
First I tried this:
typedef struct product_data product_data;
struct product_data {
int product_code;
int product_size;
product_data *next;
};
product_data *products_head = NULL;
product_data *products_tail = NULL;
int main() {
int newcode = 5;
int newsize = 5;
products_head->product_code = newcode;
products_head->product_size = newsize;
products_head->next = NULL;
return 0;
}
Unfortunately the program crashes without any error message.
Then I changed some parts:
typedef struct product_data product_data;
struct product_data {
int product_code;
int product_size;
product_data *next;
};
product_data *products_head = NULL;
product_data *products_tail = NULL;
int main() {
product_data *newproduct;
int newcode = 5;
int newsize = 5;
newproduct->product_code = newcode;
newproduct->product_size = newsize;
newproduct->next = NULL;
products_head = newproduct;
return 0;
}
No crash this time, it seems to work. I have no idea why though.
Any ideas?
Thanks in advance!

It doesn't really work. You're still dereferencing invalid pointers:
product_data *newproduct;
int newcode = 5;
int newsize = 5;
newproduct->product_code = newcode;
newproduct->product_size = newsize;
newproduct->next = NULL;
But while in the first version you were dereferencing pointers explicitly set to NULL, it crashed with a segmentation fault like it should. Here you are dereferencing a pointer that contains whatever data lay on the stack, and unfortunately it doesn't crash. It's undefined behaviour, so it need not necessarily crash.
You have to let your pointers point to valid memory,
newproduct = malloc(sizeof product_data);

You need to allocate memory for products_head. Right now you're just setting it to NULL. Either don't make it a pointer, or use malloc.

In your first sample, you are writing to a NULL pointer. You need to allocate space for products_head before you dereference it. Something like
products_head = malloc(sizeof(product_data));
I don't know why your second example worked. It shouldn't. newproduct is an uninitialized variable, it could point anywhere. Maybe you just got lucky and it pointed to an unused chunk of valid memory.

This will work until it does not. You still do not have any allocated memory for your struct. But due to some luck, newproduct points to some memory location that is valid. The issue you are facing, product_head was manually set to null (even though this is not needed, since all global variables are always initialized). Stack variables however are not initialized, and you were lucky (or unlucky that it would have caused you to miss a blatant programming mistake) that it happened to point to somewhere valid in your address space.
You can print the content of newproduct to see where it is pointing using printf("%p", newproduct); Unfortunately, inserting this line may change the behavior of the program.

The "->" is meant to access an element in a structure that is dynamically allocated and "." is used to access an element in a structure that is statically allocated.
Here is an example:
typedef struct product_data product_data;
struct product_data {
int product_code;
int product_size;
product_data *next;
};
product_data *products_head = NULL;
product_data *products_tail = NULL;
int main() {
/* Allocate memory */
product_data *newproduct = malloc(sizeof(product_data));
int newcode = 5;
int newsize = 5;
products_head = newproduct;
newproduct->product_size = newsize;
newproduct->next = NULL;
/* free memory */
free(product_data);
return 0;
}
But remember that for all the new nodes that you make in the linked list you will have to allocate memory and free that memory. A good program to use to check that all the memory you allocated was free'd is valgrind. And if you run into logic errors trying to make the linked list draw it out by hand first like this:
[head] [tail]
| |
V V
[ a ] -> [ b ] -> null
Just remember that head and tail are both pointers (so they do not need to be allocated memory, they just need to be POINTING at the node that you want.
If you still run into problems because your logic gets very complicated I sugget you try and learn GDB (it is a command line debugger) it will help you step through your code so you can see what is happening step by step. That is how I first learnt to create data structure.
Good Luck!

Related

I can alter a struct member from one location but not from the other

I am trying to implement a linked list in C - starting simple, with one list containing one node. However, I stumble upon some issues when trying to add data to the node. Here's my implementation thus far:
struct mylist_node {
int data;
};
struct mylist {
struct mylist_node *head_pt;
};
void mylist_init(struct mylist* l){
struct mylist_node head_node;
head_node.data = 5; //First try
l->head_pt = &head_node;
l->head_pt->data = 5; //Second try
};
And my main method:
int main()
{
struct mylist ml, *ml_pointer;
ml_pointer = &ml;
mylist_init(ml_pointer);
printf("%d\n", ml_pointer->head_pt->data);
ml_pointer->head_pt->data = 4;
printf("%d\n", ml_pointer->head_pt->data);
return 0;
}
This should print out
5
4
If my knowledge of pointers is correct. However, it prints out
0
4
As you can see I try to set the node data twice within the mylist_init method. Neither appears to be working - meanwhile, writing to and reading from it from my main method works just fine. What am I doing wrong?
In mylist_init, you're storing the address of a local variable in the struct pointed to by l. That variable goes out of scope when the function returns, so the memory it occupied is no longer valid, and thus the pointer that previously pointed to it now points to an invalid location. Returning the address of a local variable a dereferencing that address invokes undefined behavior.
Your function needs to allocate memory dynamically using malloc so the memory will still be valid when the function returns.
void mylist_init(struct mylist* l){
struct mylist_node *head_node = malloc(sizeof(*head_node));
l->head_pt = head_node;
l->head_pt->data = 5;
};
Also, don't forget to free the memory when you're done using it.
For starters, you have to allocate memory for your node, the way you were doing it, your node is a local variable on the stack which will likely get overwritten after the function exits.
void mylist_init(struct mylist* l)
{
struct mylist_node *head_node = (struct mylist_node *)malloc(sizeof(struct mylist_node));
head_node.data = 5; //First try
l->head_pt = head_node;
};

seg fault after trying to access ladder part of sbrk free space

Ok, so I have a struct that looks like
typedef struct meta {
size_t size;
struct meta* next;
struct meta* prev;
} meta_t;
I also statically allocate the beginning list pointer and the end list pointer as:
static meta_t* freelist_b = NULL;
static meta_t* freelist_e = NULL;
I then free a block of 4 MB of space and set the beginning list pointer to it
size_t max_bytes = ALIGN(1024*1024*4);
freelist_b = (meta_t*) sbrk(max_bytes);
Then I try to do the same for the end of list pointer
freelist_e = memcpy(freelist_b+max_bytes-sizeof(meta_t),freelist_b,sizeof(meta_t));
But this line gives a segmentation fault. Can anyone tell me what I'm doing wrong or how to further debug this? Thanks.
When you do freelist_b+max_bytes-sizeof(meta_t), you are using operator+ on a pointer to a meta_t structure. This adds a lot more than what you probably expect.
Change:
freelist_b
To:
(char*)freelist_b
Or change:
max_bytes-sizeof(meta_t)
To:
max_bytes/sizeof(meta_t)-1

A hashtable of pointers in C?

I am trying to build an initialize a hashtable whose pointers point to another struct in my program. But it seems to give me a segfault when I try to initialize(H). I think I may be allocating memory incorrectly, but I'm not sure if that's what a segmentation fault actually means. The way it is set up, H->hashtable should be an array of hashnodes, right? hashnodes themselves are the pointers to my other structs. Why am I only getting a seg fault at initialize?
#include <stdio.h>
#include <stdlib.h>
typedef struct Position{
char data[12];
struct Hashnode *previous;
struct Position *next;
char letter;
char direction[5];
} *position;
typedef struct Hashnode{
struct Position *INSIDE;
} *hashnode;
typedef struct hash_table{
hashnode *hashtable
} *HTABLE;
HTABLE NewHashtable(){
HTABLE H = (HTABLE) malloc(sizeof(struct hash_table));
if(H == NULL){ printf("Malloc for new hashtable failed."); exit(1);}
return H;
}
void initialize(HTABLE H){
H->hashtable = (hashnode*) malloc(100003*sizeof(hashnode));
int toofer;
for(toofer = 0; toofer<100003; toofer++){
H->hashtable[toofer]->INSIDE = NULL;
}
}
int main(){
HTABLE H = NewHashtable();
initialize(H);
return 0;
}
This:
HTABLE H = (HTABLE) malloc(sizeof(struct hash_table));
is just horrible. It mixes a typedef:ed pointer (why do people still do this?) with the underlying struct name, making it the reader's job to make sure they match. Plus, that cast is a bad idea, too.
It should be:
HTABLE H = malloc(sizeof *H);
if you insist on keeping the typedef.
That said, the code in initialize() is probably failing its malloc() call, which is not checked before being relied on. This is a very bad idea.
Further, there's confusion about what exactly is being allocated. The malloc() code allocates 100003*sizeof(hashnode), but hashnode is (again) typedef:ed as a pointer, not a struct. Then the pointers are dereferenced in the loop, causing mayhem.
H->hashtable = (hashnode*) malloc(100003*sizeof(hashnode));
int toofer;
for(toofer = 0; toofer<100003; toofer++){
H->hashtable[toofer]->INSIDE = NULL;
}
}
The first line allocates a bunch of memory for H->hashtable. It contains random garbage.
Thus, when you enter the loop, H->hashtable[0] is random garbage (because all of H->hashtable is random garbage). But you attempt to follow that random garbage pointer in in your loop. Dereferencing an uninitialized pointer is the fastest way to get a segmentation fault.
Here's a way to help you see it. Say you decided to zero that memory to be safe. Your code would be:
H->hashtable = (hashnode*) malloc(100003*sizeof(hashnode));
memset(H->hashtable, 0, 100003 * sizeof(hashnode));
int toofer;
for(toofer = 0; toofer<100003; toofer++){
H->hashtable[toofer]->INSIDE = NULL;
}
}
Clearly, after that memset, *(H->hashtable) is 0 since that sets all of H->hashtable to 0. So H->hashtable[0] is 0 too and thus H->hashtable[toofer]->INSIDE dereferences a null pointer.
H->hashtable = (hashnode*) malloc(100003*sizeof(hashnode));
should better be
...sizeof(struct Hashnode)...

whats wrong in this program?

Am unable to run this code...
#include<cstdio>
int main()
{
struct a{
int b;
struct a *next;
};
typedef struct a no;
no *n;
n->b = 12;
n->next = NULL;
n->next->b = 12;
n->next->next = NULL;
printf("%d %d", n->b, n->next->b);
getchar();
return 0;
}
When you say:
no *n;
you get an uninitialised pointer. When you use that pointer, you get undefined behaviour.
You allocated space for a pointer to a structure, but you didn't allocate space for the actual structure. This means that you don't have a memory address for the structure you are using.
In addition, the pointer points to some random memory address because you didn't initialize it. As a result, you could be trying to read and write to memory that doesn't belong to you, which can cause your program or even your system to crash because of the undefined behavior that results.
As #Neil Butterworth said, you get an uninitialised pointer. This mean that this pointer could point to anywhere, thus giving an access violation error. The way to fix this is simple, just call malloc() before using that pointer. malloc() gives that pointer a valid and usable address, so no one will complain about that.
You're declaring a struct INSIDE a function.
Declare the struct OUTSIDE of the function.
The typedef should be declared outside the function too.
#include<cstdio>
struct a{
int b;
struct a *next;
};
typedef struct a no;
int main()
{
///... your code...
}
try something like this:
no *n = (no*)malloc(sizeof(no));
#include <cstdio>
/* declaring the struct in the function definition may be possible (I'm not sure,
actually, haha). Unless you have a GOOD reason, it's good practice to declare
structs, globals, typedefs, etc... outside the function */
typedef struct a{
int b;
struct a *next;
} no;
int main()
{
no *n;
/* Here, you have a pointer. Remember these are simply (generally) 32-bit values
defined in your stack space used to store a memory location which points to your
ACTUAL struct a! Depending on what USED to be in the stack space, this could
point ANYWHERE in memory, but usually you will find that it points to the NULL
memory location, which is just address "0". To get this to point to something,
you have to allocate empty space on your heap to store your struct... */
n = malloc(sizeof(no));
/* Now your pointer n points to an allocated 'struct a', and you can use it like
normal */
n->b = 12;
n->next = NULL;
/* You just set n->next, which is another 'no' pointer, to NULL. This means that
n->next points nowhere. So, just like above you have to malloc another instance
of the struct! */
n->next = malloc(sizeof(no));
/* NOW you can use n->next with no ill effects! */
n->next->b = 12;
n->next->next = NULL;
printf("%d %d", n->b, n->next->b);
getchar();
/* After you're done with your structs, you want to free them using the POINTERS
that reference them */
free(n->next);
free(n);
return 0;
}

Seg fault when assigning an instantiated struct?

struct queens_arg {
int board[64];
int focus_idx;
};
struct queens_arg *args;
(*args).focus_idx = 0;
The code is as the following. I get a seg fault (debugged) to occur on setting focus_idx = 0. Why is this happening? How can I fix it? Thanks!
The problem is that you're creating a pointer to a struct queens_arg, but you're not initializing it to point anywhere. Consequently, when you write
(*args).focus_idx = 0;
You're following a garbage pointer, causing the segfault at runtime.
To fix this, make sure that you set up the pointer to point somewhere. Here's how you can have it point to dynamically-allocated memory:
struct queens_arg* args = malloc(sizeof (struct queens_arg));
On a related note, you don't need to write
(*args).focus_idx = 0;
You can instead use this shorthand:
args->focus_idx = 0;
It means exactly the same thing, but is a bit easier to read.
This occurs because you used a pointer, yet you didn't allocate anything. Hence you are writing into memory that is not yours.
You should first allocate args as follows:
struct queens_arg *args = malloc(sizeof(struct queens_arg));
Then you should check if args != NULL. If not, you can write to the memory you just allocated, using:
args->focus_idx = 0;
You've only declared a pointer to a queens_arg struct, no struct actually exists.

Resources