Stack Exchange
pointer functions does not return anything but returns something?
#include <stdio.h>
#include <stdlib.h>

struct node {
    int data;
    struct node * prev;
    struct node * next;
};


struct node * addtoempty(struct node * x,int value)
{
    x=malloc(sizeof(struct node));
    x->data=value;
    x->prev=NULL;
    x->next=NULL;
    
   
}


struct node *addatbeginning(struct node * x, int value){
    struct node * ptr=malloc(sizeof(struct node));
    ptr->data=value;
    ptr->next=x;
    ptr->prev=NULL;
    x->prev=ptr;
    x->next=NULL;
    x=ptr;
}
int main()
{
    struct node * head=NULL;
    head=addtoempty(head,2);
    head=addatbeginning(head,5);
    printf("%d",head->data);
    
    return 0;
}

I was implementing linked lists and I don't understand how this code still prints 5, I've tried calling the functions and not assigning them (since they are not returning anything ) but it gives me an error.

Since you are directly modifying the variable using its pointer, u don't need to return the pointer to update the new head. Here you're passing the node as a pointer in both the functions and directly modifying the pointer, so after calling the function if u print the head in main it will be updated.

@riprandy, UB is UB.  Enable all warnings to see troubles.

"and I don't understand how this code still prints 5" What did you expect it to print instead?

It depends on compiler/calling convention. If return value is in register, you get the last value of this register. Try to change 5 in addatbeginning(head,5); to different number and see whether the new value is printed. Anyway, you have to add return in functions.

It looks like you don't fully understand how parameter passing in C works. Take addtoempty for example. It doesn't use the value of x that is passed in, instead it immediately assigns it a new value. Why is this a parameter at all? Do you expect the changes that the function makes to x to be reflected back to the call site?

@n.m.couldbeanAI what I say is code shouldn't print the node values, not only i can print the first node's value without an error, but also I can print the second node's value with head-> next -> data without an error, which prints 2.

Do you expect correct programs to print correct answers, and incorrect programs to print incorrect answers? If that was the case we wouldn't need testing.

@n.m.couldbeanAI I think he wonder why incorrect program seems working correctly.

@n.m.couldbeanAI I was curious about how this code manages to work fine without adding return statements.I realize the code isn't correct, and I could have just searched for 'linked list implementation in C instead of posting here.

"How this code manages to work fine" Required reading.

@riprandy Re: "I was curious about how this code manages to work fine without adding return statement" - I tried to cover that part in my answer. If you want me to clarify anything further, please just comment under the answer and I'll try to make it clearer.

Your program has undefined behavior since addatbeginning and addtoempty don't return a struct node* which they are declared to return and your assignment to head actually expects them to return a struct node*.
With undefined behavior, the program could do just about anything, like printing 5 ... or 6 ... or nothing ... or crash.
A correction for addatbeginning could look like this:
struct node *addatbeginning(struct node *x, int value) {
    struct node *ptr = malloc(sizeof *ptr);
    if (ptr == NULL) exit(1);
    *ptr = (struct node) {value, NULL, x};
    if(x) x->prev = ptr;
    return ptr;
}

This function can be used on an empty list too, so addtoempty isn't really needed.
Demo

If you don't want it to return the new head, you'll have to make sure it's set inside the function:
void addatbeginning(struct node **x, int value) {
    struct node *ptr = malloc(sizeof *ptr);
    if (ptr == NULL) exit(1);
    *ptr = (struct node) {value, NULL, *x};
    if (*x) (*x)->prev = ptr;
    *x = ptr;
}

Which you then call by sending in a pointer to head:
addatbeginning(&head, 5);

Demo

The issue here lies in how the addtoempty and addatbeginning functions are implemented.
addtoempty Function:
You're creating a new node and assigning values to it, but you're not returning this new node from the function. This means that when you call head=addtoempty(head,2);, the head pointer in main doesn't actually get updated with the new node's address. It remains NULL.
Similar to addtoempty, this function creates a new node but does not return it. Therefore, the head pointer in main doesn't get updated here either.
#include <stdio.h>
#include <stdlib.h>

struct node {
    int data;
    struct node * prev;
    struct node * next;
};

struct node * addtoempty(struct node * x, int value) {
    x = malloc(sizeof(struct node));
    x->data = value;
    x->prev = NULL;
    x->next = NULL;
    return x;  // Return the new node
}

struct node * addatbeginning(struct node * x, int value) {
    struct node * ptr = malloc(sizeof(struct node));
    ptr->data = value;
    ptr->next = x;
    ptr->prev = NULL;
    if (x != NULL) {  // Check if the list is not empty
        x->prev = ptr;
    }
    return ptr;  // Return the new node
}

int main() {
    struct node * head = NULL;
    head = addtoempty(head, 2);
    head = addatbeginning(head, 5);
    printf("%d", head->data);

    // Add code to free the allocated memory

    return 0;
}


" the head pointer in main doesn't actually get updated with the new node's address. It remains NULL" - There's no telling what value head will have. It may hold the new node's address or the address of something completely different since the program has undefined behavior.
