C - Learning Linked Lists, Pointer Manipulation - Store some ints, print and free memory I'm learning C, and wrote this program outside of my assignments in order to practice pointer manipulation and data structures, step by step. I want to ensure that this program follows well known conventions, has excellent style and readability, and uses pointers as efficiently as possible, before I move on to more complex structs. 

I am aware I am not doing any error checking or type checking. I don't intend to export this code as-is, my intention is to practice with pointers. I found it easiest for now to keep the code as simple as possible and remove checking. This is also why I created the additional functions, just to help isolate parts of the code. 

If appropriate, I'm also interested in modifying the freelist() function to work recursively, freeing the last node first. Currently, if the node order is {1,2,3}, 1 is freed first. I haven't been able to successfully call the function recursively to instead free the nodes from 3 then 2 then 1. 

// This program stores N integers from user input into a 
// linked list, prints the list, and then frees each node. 

#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#define N 5

typedef struct _node {
    int value;
    struct _node *next;
} NODE;

int getinteger(void);
void printlist(NODE **head);
void freelist(NODE *head);

int main(void)
{
    NODE *start = NULL;

    for (int i = 0, n = 0; i &lt; N; i++)
    {
        n = getinteger();

        NODE *newnode = (NODE *) malloc(sizeof(NODE));
        newnode-&gt;value = n;
        newnode-&gt;next = NULL;

        if (start == NULL)
        {
            start = newnode;
        }
        else
        {
            NODE *p = NULL;
            for (p = start; p-&gt;next != NULL; p = p-&gt;next)
                ;
            p-&gt;next = newnode;
        }
    }

    printlist(&amp;start);
    freelist(start);
}

int getinteger(void)
{
    int n = 0;
    printf("Enter integer: ");
    scanf("%i", &amp;n);
    return n;
}

void printlist(NODE **head)
{
    NODE **tracer = head;
    while ((*tracer) != NULL)
    {
        printf("%i\n",(*tracer)-&gt;value);
        tracer = &amp;(*tracer)-&gt;next;
    }
}

void freelist(NODE *head)
{
    NODE *p = head;
    while (p != NULL)
    {
        NODE *next = p-&gt;next;
        free(p);
        p = next;
    }
}
Naming
It's unconventional to name a type with all-uppercase - we normally reserve those names for preprocessor macros, to warn readers that they need treating with care.  Avoid such names for ordinary identifiers.
Avoid using identifiers that begin with an underscore - in many situations, those names are reserved for use by the implementation, which could conflict with your own uses.
Allocation
Let's look at this section:

    NODE *newnode = (NODE *) malloc(sizeof(NODE));
    newnode-&gt;value = n;
    newnode-&gt;next = NULL;


There's a serious bug here.  Remember that malloc() can return a null pointer.  It's essential to check that newnode is not null before we dereference it using * or -&gt;.
Because malloc() returns a void*, it's not necessary or desirable to cast the result - we can assign it directly to newnode.  Also, when computing the size, we can avoid repeating the type of the allocation by writing sizeof *newnode for the size (this has more value when the allocation is far from the declaration, but it's a good habit to be in).
That gives this replacement:
    Node *newnode = malloc(sizeof *newnode);
    if (!newnode) {
        fputs(&quot;Allocation failed\n&quot;, stderr);
        return EXIT_FAILURE;
    }

    newnode-&gt;value = n;
    newnode-&gt;next = NULL;

Reading input
Another function whose return value must be checked is scanf().  If I enter something that's not a number, or if I close the input stream, then we don't write to n here:

scanf(&quot;%i&quot;, &amp;n);


We need to check the return value and test whether the expected number of items were successfully converted.  Something like this:
int getinteger(void)
{
    int n = 0;
    printf(&quot;Enter integer: &quot;);
    fflush(stdout);
    while (scanf(&quot;%i&quot;, &amp;n) != 1) {
        if (feof(stdin)) {
            /* no point retrying! */
            fputs(&quot;Read failure\n&quot;, stderr);
            exit(EXIT_FAILURE);
        }
        printf(&quot;Invalid input! Enter integer: &quot;);
        fflush(stdout);
        scanf(&quot;%*[^\n]&quot;);       /* discard rest of line */
        /* newline remains, but will be discarded by scanf(&quot;%i&quot;) */
    }
    return n;
}

Notice that there's a lot more code for dealing with the unexpected than with the &quot;happy path&quot; - that's a common experience when reading input using C.
Memory management
Apart from the lack of checking when malloc() is used, the memory management is great.  No leaks, double-frees, or use of uninitialised, unallocated or released memory.
Efficiency
Notice that every time we add a node, we have to start from the head, and traverse the whole length of the list to get to the tail.  This becomes more and more work as the list gets longer - sometimes likened to Schlemiel the Painter.  We can make this more efficient, though at the cost of some extra storage, by maintaining a pointer to the tail of the list as well as the head.
Other improvements
We don't need to pass a pointer-to-pointer into printlist(); a plain Node * is fine:
void printlist(Node *p)
{
    for (;  p;  p = p-&gt;next) {
        printf(&quot;%i\n&quot;, p-&gt;value);
    }
}

When we're looking for the list tail, we can simplify by starting p at head rather than first assigning NULL:
        Node *p = start;
        while (p-&gt;next) {
            p = p-&gt;next;
        }
        p-&gt;next = newnode;

In the main loop in main(), n shouldn't be declared in the for initializer, as it's not part of the loop control.
If we define main() last, the definitions of the other functions can act as their declarations, so no need to forward-declare them.

Improved code
Note: I haven't addressed the issue mentioned above in &quot;Efficiency&quot;.
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

/* number of nodes to create in list */
#define N 5

typedef struct node {
    int value;
    struct node *next;
} Node;

/* Prompt on stdout, and read a number from stdin */
int getinteger(void)
{
    printf(&quot;Enter integer: &quot;);
    fflush(stdout);

    int n;
    while (scanf(&quot;%i&quot;, &amp;n) != 1) {
        if (feof(stdin)) {
            /* no point retrying! */
            fputs(&quot;Read failure\n&quot;, stderr);
            exit(EXIT_FAILURE);
        }
        printf(&quot;Invalid input! Enter integer: &quot;);
        fflush(stdout);
        scanf(&quot;%*[^\n]&quot;);       /* discard rest of line */
        /* newline remains, but will be discarded by scanf(&quot;%i&quot;) */
    }

    return n;
}

void printlist(Node *p)
{
    for (;  p;  p = p-&gt;next) {
        printf(&quot;%i\n&quot;, p-&gt;value);
    }
}

void freelist(Node *p)
{
    while (p) {
        Node *next = p-&gt;next;
        free(p);
        p = next;
    }
}


int main(void)
{
    Node *start = NULL;

    for (int i = 0;  i &lt; N;  ++i) {
        int n = getinteger();

        Node *newnode = malloc(sizeof *newnode);
        if (!newnode) {
            fputs(&quot;Allocation failure\n&quot;, stderr);
            return EXIT_FAILURE;
        }

        newnode-&gt;value = n;
        newnode-&gt;next = NULL;

        if (!start) {
            start = newnode;
        } else {
            Node *p = start;
            while (p-&gt;next) {
                p = p-&gt;next;
            }
            p-&gt;next = newnode;
        }
    }

    printlist(start);
    freelist(start);
}