You don't have to use dynamic memory to create a linked list, although you definitely don't want to create separate variables for each node. If you want to store up to N items, then you'd need to declare N distinct variables, which becomes a real pain as N gets large. The whole idea behind using a linked list is that it can grow or shrink as necessary; it's a dynamic data structure, so even if you don't use malloc and free, you're going to wind up doing something very similar.
For example, you can create an array of nodes at file scope like so:
struct node {
  int data;
  struct node *next;
};
/**
 *  use the static keyword to keep the names from being visible 
 *  to other translation units
 */
static struct node store[N];  /* our "heap" */
static struct node *avail;    /* will point to first available node in store */
You the initialize the array so each element points to the next, with the last element pointing to NULL:
void initAvail( void )
{
  for ( size_t i = 0; i < N - 1; i++ )
    store[i].next = &store[i + 1];
  store[N - 1].next = NULL;
  avail = store;
}
To allocate a node for your list, we grab the node avail points to and update avail to point to the next available node (if avail is NULL, then there are no more available nodes).
struct node *getNewNode( void )
{
  struct node *newNode = NULL;
  if ( avail ) /* if the available list isn't empty */
  {
    newNode = avail;       /* grab first available node */
    avail = avail->next;   /* set avail to point to next available node */
    newNode->next = NULL;  /* sever newNode from available list, */
  }                        /* which we do *after* we update avail */
                           /* work it out on paper to understand why */
  return newNode;
}
When you're done with a node, add it back to the head of the available list:
void freeNode( struct node *n )
{
  n->next = avail;
  avail = n;
}
We're not using dynamic memory in the sense that we aren't calling mallic or free; however, we've pretty much recapitulated dynamic memory functionality, with the additional limitation that our "heap" has a fixed upper size.  
Note that some embedded systems don't have a heap as such, so you'd have to do something like this to implement a list on such systems.