why and when is a double-pointer required? - c

I have been a sysadmin most of my life, but recently decided to practice some my dev knowledge and attempt a devops position. I have as such been practicing some C and Python skills and wrote some code for inserting a number into a linked list.
void list_insert(struct list *h, int d)
{
struct list *elem = malloc(sizeof(struct list));
elem->data = d;
elem->next = NULL;
if (!h) {
h = elem;
} else {
elem->next = h;
h = elem;
}
}
I noticed that this function doesn't seem to alter the outside view of the variable h (i.e whatever is passed to list_insert) and I noticed that printing at the end of the insert function seems to work. So having tried to look for answers online, I couldn't find anything, obvious but I found most list implementations would have double-pointers. I changed my function to use double-pointers and then it suddenly started working. Could someone help me understand what's happening here as I know pointer management is an important concept and I think I understand what a pointer is and how it relates to memory, but I don't think I get why a single pointer does not get changed, whereas a double-pointer does.
Thanks!

In C, arguments to function are passed by values. Even pointers are passed by values.
For example:
#include<malloc.h>
#include<stdio.h>
int allocatingMemory(int* ptr)
{
ptr = malloc(sizeof(int));
if(ptr==NULL)
return -1;
else
return 0;
}// We are not returning the pointer to allocated memory
int main(void)
{
int* ptr;
int allocated = allocatingMemory(ptr);
if(allocated == 0)
{
*ptr = 999;// Boom!!!
free(ptr);
}
return 0;
}
To overcome this issue, we use
int allocatingMemory(int** ptr)
{
*ptr = malloc(sizeof(int));
if(*ptr == NULL)
return -1;
else
return 0;
}
int main(void)
{
int* ptr;
int isAllocated = allocatingMemory(&ptr);
if(isAllocated == 0)
{
*ptr = 999;
free(ptr);
}
return 0;
}
If you are working with linked lists and say for example, you want to modify the head. You will pass a pointer to pointer (Note that, it is not called as double pointer) to head node.

To change memory in the caller's context, a function needs to have a pointer to that memory.
If the caller of your function has an empty list in a variable, and does an insert on that list like so:
struct list *numbers = NULL;
list_insert(numbers, 4711);
then of course inside list_insert() all we have is the NULL pointer, so we can't change the value of the variable numbers in the caller's context.
If, on the other hand, we're given a pointer to the variable, we can change the variable.
That said, it's much cleaner (in my opinion) to return the new head of the list, i.e. make the function's signature be struct list * list_insert(struct list *head, int x);.

h is actually a copy of the original pointer, so your original pointer doesnot get modified. That is why you should use a double pointer.
There are numerous questions related to that on SO. for example Using single versus double pointers in Linked lists implemented in C

Related

Pointers to structs in C vs pointers to arrays

Do pointers to structures in C work differently than other pointers?
For example in this code:
typedef struct node {
int data;
struct node *next;
} node;
void insert(node **head, int data) {
node *new_node = malloc(sizeof(node));
new_node->data = data;
new_node->next = *head;
*head = new_node;
}
int main() {
node *head = NULL;
insert(&head, 6);
Why do I have to use a pointer to a pointer and can't use the variable head in the insert function like in this example with arrays:
void moidify(int *arr) {
*arr = 3;
}
int main() {
int *array = malloc(8);
*array = 1;
*(array + 1) = 2;
moidify(array);
}
Here I don't have to pass &array to the function.
There is no difference. If you want to change the value of the variable you send in to function in such a way that the change is visible in the function that called function, you need to supply its address to function, which is what you do when taking the address of head.
In moidify(array) you send in a pointer to the first element in array which is why modifying the array data works. If you would like to modify the array variable itself (by making it potentially point somewhere else), you would have to take its address too. Example:
void moidify(int **arr) {
*arr = realloc(*arr, 128);
if(*arr == NULL) {
perror(__func__);
exit(1);
}
}
int main() {
int *array = malloc(8);
*array = 1;
*(array + 1) = 2;
moidify(&array);
}
You must understand how pointers works to get this one.
Here, the variable array is not properly speaking, an array. It's a pointer toward a memory space, of size 8 * sizeof(int). It contains only an address. From this address you can access the values of the array, you move using this address, to the rightfully memory space you want to fill or read.
Once that understood, when you call the moidify function, you are not passing the array. Nor the memory space. You are passing, the address of the memory space. The function gets a copy of the given address, in the argument variable int *arr.
Hence, you can use it the same way you use it from the main function.
If you wanted to change the address toward which the array variable would go, you would need to specify &array to the receiving function, which would then use an int ** argument variable.
Your example with struct is similar to this last part I just described, you want to change toward which address head is pointing, so, you need to give &head to the function. To get the address of head, and be able to modify the contained address.
You use an address, to access the memory space called head, to modify the address inside the memory space called head, which point toward another memory space, where your struct truly belongs.

C linked list won't change when I push data onto an initialised stack

I'm trying to implement a stack using a linked list in C but I am getting segfaults whenever I try to call the value after I push a new one onto the stack. I know this is happening because the program still says the stack is null even if I add to it. For some reason the changes that I make in the push don't stay when the function terminates and I can't figure out why.
Here's my code for the stack struct, initialization, and push:
typedef struct stack
{
int value;
struct stack * next;
} * stack_T;
stack_T
new_stack()
{
return NULL;
}
int
push_stack(stack_T s, int data)
{
stack_T new = malloc(sizeof(stack_T));
new = s;
if (s == NULL)
{
s = malloc(sizeof(stack_T));
if (s == NULL)
return 1;
}
s->value = data;
s->next = new;
return 0;
}
EDIT: Thanks for the help but I forgot to mention that the push has to be done with exactly those parameters, it's part of an assignment. I'm not looking for how to do it but rather what I'm doing wrong. I know that I can emulate pass by reference but as I said it has to be:
int push_stack(stack_T s, int data)
I've made structs before with this style and had functions that just take them as a parameter and changes stay but they won't in this case and I have no idea why.
It looks like you haven't labeled s as a double pointer. It looks like you're attempting to push into the front of the linked list. What you need is to pass in double pointer of the reference to the head of the stack in order to change the address value stored at the memory. This way you would always have reference the top of the stack.
int
push_stack(stack_T **s, int data)
{
stack_T *new = malloc(sizeof(stack_T));
if (new == NULL)
return (1);
if (s == NULL)
{
free(new);
return (1);
}
new->value = data;
new->next = *s;
*s = new;
return 0;
}
I would have to see how your main function is being called that would result in the segfault. However, it does seem that you don't have the proper reference to the top of the stack. Also, I don't quite understand why you are malloc out space for two nodes. Malloc returns a pointer that is being claimed by the size that you've designated.
what you wrote here:
stack_T new = malloc(sizeof(stack_T));
new = s;
if (s == NULL)
{
s = malloc(sizeof(stack_T));
if (s == NULL)
return 1;
}
Will never be met with NULL is a a type of void pointer and s as you assigned it is not of a pointer type, so you are attempting to store a variable of the 12 bytes when malloc returns a pointer of 8 bytes.
Also a tip is that you can compile with gcc -g and use valgrind to determine the line in which the segfault is occuring

How is this statically allocated struct data available outside the function?

Here is a quick test C program that I've coded to see how struct memory allocation works...
#include <stdio.h>
#include <stdlib.h>
typedef struct _node {
int kk;
int zz;
} node;
node ** createNode(){
node** res = (node**) malloc(sizeof(node*)*10);
int i,j;
for(i= 0;i<10;i++){
res[i] = (node*) malloc(sizeof(node)*10);
for(j=0;j<10;j++){
res[i][j].kk=33;
}
}
return res;
}
int main(void) {
node ** g = createNode();
printf("%d",g[0][0].kk);
return 0;
}
This program prints the value "33". Now this has become obvious to me, but reflecting on it, I don't understand why...
Now that I think about it, shouldn't the variable g be of type node *** ?
And the print statement look something like printf("%d",g[0][0]->kk); ?
Where in the second version, I've essentially done the same thing as my original code, but I have a pointer to a node instead of the actual node.
What is the difference between the two in terms of the first being statically allocated (I think) and the second being dynamically allocated... and shouldn't the node values I created in my createNode() function be destroyed once outside the scope of that function?
Just a little confused is all :S I need someone to clarify this for me, what is the difference between node** and node***
Let's use a simpler function to make samples. For example we want to create a function which will be allocate memory for integer and assign some value. The function does the same thing as your one.
int* createInt() {
int *res = malloc(sizeof(int));
*res = 5;
return res;
}
This function creates a pointer and allocate dynamic memory for int. After this the function returns an address of memory where the int is but the res pointer won't exist. By the way look at this answer to similar question, it's worth reading.
As you understand in main function you use
int *myInt = createInt();
Because you want to assign an address of allocated memory to pointer to deal with that and free after all.
But you could do this
int** createInt() {
static int *res;
res = malloc(sizeof(int));
*res = 5;
return &res;
}
And in main
int **myInt = createInt();
Here you do the same things as above but create static pointer so it is retained from one call of the function to another. So you can return an address of this pointer. It will point to actual data after function ends.
As mentioned in comments my code isn't safe because if you call this function twice it leads to memory leak. You could do
int* createSingletonInt() {
static int *res;
if (res != NULL) {
return res;
}
res = malloc(sizeof(int));
*res = 5;
return res;
}
That can be useful if you want to deal with the same int. And more useful if you deal with something but not int :>
I think it can be applied to your example.

Values in array of structs turning into garbage values

I have an issue that's really confusing me... Below I am calling an initialize function:
void Initialize (List *L) {
char* initialize = "initialize";
int i;
for (i=0; i<MAXLISTSIZE; i++) {
strncpy(L->items[i].name,initialize,MAXNAMESIZE);
L->items[i].name[MAXNAMESIZE - 1] = '\0';
L->items[i].grade = 0;
printf("L->items[i].name = %s\n", L->items[i].name);
printf("L->items[i].grade = %d\n", L->items[i].grade);
}
L->count = 0;
}
And it seems to work, I print the values in the loop and it's fine. If I also print inside an identical loop in main to double check it works as well but If I just print the values in main after the initialize function (no print statements in Initialize) I get complete garbage.
It seems the memory I'm storing my values in isn't staying consistent and I can't figure out why.
Do I need to malloc memory for the structs? Since I don't need a variable amount of storage I thought it was not necessary... I am unsure of how to go about that.
My Structs:
typedef Student Item;
#define MAXLISTSIZE 4
typedef struct {
Item items[MAXLISTSIZE];
int count;
} List;
#define MAXNAMESIZE 20
typedef struct {
char name[MAXNAMESIZE];
int grade;
} Student;
I am simply calling Initialize from main:
int main () {
List *newList;
/*call initialize function*/
newList = callInitialize();
return 0;
}
callInitialize:
List *callInitialize () {
List *L;
List studentList;
L = &studentList;
Initialize(L);
return L;
}
Now that you posted the function that causes the actual problem, we see what's wrong: You are returning the address of a local variable that goes out of scope! This is not valid.
List * foo()
{
List x; // <--- x comes to life
return &x; // <--- x dies here...
}
int main()
{
List * p = foo(); // ... but its address is used here!
p->name ... // dereferencing an invalid address!!
}
Your situation calls for dynamic (or "manual") allocation, which means memory allocation whose lifetime is controlled only by you, and not by the local scope.
List * initList()
{
return malloc(sizeof(List)); // this memory is permanent
}
Any manual allocation needs to come with a clean-up routine:
void freeList(List * p)
{
free(p);
}
Now that you've posted the remainder of your code, the problem is obvious:
List *callInitialize () {
List *L;
List studentList;
L = &studentList;
Initialize(L);
return L;
}
Within this function you create a local variable studentList which you the pass to Initialize to set it up.
Unfortunately, the scope of the studentList variable ends when you exit the callInitialize function. That's why it may contain rubbish when you attempt to use it later on.
Despite your comment about not needing malloc because the structure is small, you do need it if you want the scope of the data to exist beyond the function it's created in.
Probably the "minimal-change" solution would be:
List *callInitialize (void) {
List *L = malloc (sizeof (List));
if (L != NULL)
Initialize(&L);
return L;
}
and then remember that it needs to be freed at some point by the caller, unless the malloc fails and this function therefore returns NULL.

C - Can't initate a pointer that is passed as an argument

#include <stdio.h>
#include <stdlib.h>
typedef struct {
unsigned length;
} List;
void init(List *l) {
l = (List *) malloc(sizeof(List));
l->length = 3;
}
int main(void) {
List *list = NULL;
init(list);
if(list != NULL) {
printf("length final %d \n", list->length);
return 0;
}
return 1;
}
This is a simplified version of the code that is giving me problems. I am trying to construct the pointer *list from a method where *list is passed as an parameter.
I know I can make void init(List *l) work by changing it to void init(List **l) but this is for a class tutorial. I can't change the method arguments. I have spent four hours working on this.
I want to ensure that there is no way to make void init(List *l) work before I confront my professor.
Thanks in advance
You're passing a copy of the pointer to init, which is allocating memory, storing it in its local copy, and promptly leaking it when init returns. You cannot pass data back to the calling function this way. You need to either return the allocated data, or pass in a pointer to the pointer you want to modify, both of which involve modifying the function signature.
void init(List **l) {
*l = (List *) malloc(sizeof(List));
(*l)->length = 3;
}
init(&list);
Did the assignment specify that you have to allocate the List from within init? If not, you could always pass a pointer to an already allocated List object, and perform whatever initialization length = 3 is a place-holder for:
void init(List *l) {
l->length = 3;
}
List list;
init(&list);
printf("length final %d \n", list.length);
The problem is that the pointer is passed by value, so you're changes are discarded. You really need a pointer to a pointer to do this correctly. As in you would do:
void init(List** l) {
*l = (List*) malloc(sizeof(List));
// ...
}
And when you call it, you would use init(&list) instead of init(list). Of course, in this case, it makes sense to just go ahead and return the result instead of using a pointer to a pointer:
List* init() {
List* result = (List *) malloc(sizeof(List));
result->length = 3;
return result;
}
And then, with the above, you could simply use list = init();.
Note that in C++, you can use references instead of pointers, but mixing references and pointers is incredibly messy. Here, using a return-type is really the neatest thing to do.
If you absolutely have to use the existing signature, you can be sneaky and initialize the list, then in the init() function you can make the passed-in list's next pointer point to the list you actually want to create. Then, after init() has been called, you can take the next pointer and dispose of the original list object you created. Or you could always just have the first element by some dummy element.
this assignment is probably a good way to teach in a class the pass by value and pass by reference. If you want to maintain the constructor's signature you need to modify the main function.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
unsigned length;
} List;
void init(List *l) {
l->length = 3;
}
int main(void) {
List list;// x = NULL;
memset(&list,0,sizeof(List));
init(&list);
printf("length final %d \n", list.length);
return 1;
}
Now here list is of type List and not address to List. the init() method passed the address of list and inside init you can change the value of the structure contents.
./a.out
length final 3
init needs to be passed a pointer to an existing List. I suspect that the real problem here is with your data structure. You have something called a List, that contains a length, but there's no list to be seen anywhere. List should probably contain a pointer to an array of the given length, and init should malloc that array and set the pointer. You will probably find this out when you ask the professor to correct his requirements which aren't broken -- if they were, he probably would have heard about it from past students and corrected them by now.

Resources