Record transaction log I am trying to resolve following problem to improve my DS knowledge. Please advice which part of the code can be improved.

You run an e-commerce website and want to record the last N order ids
in a log. Implement a data structure to accomplish this, with the
following API:
record(order_id): adds the order_id to the log get_last(i): gets the
ith last element from the log. i is guaranteed to be smaller than or
equal to N. You should be as efficient with time and space as
possible.

Code explanation:
I choose linked list with single pointer (prev) to keep transaction logs. I suppose it would be called Stack Linked List.
stProduct -&gt; linked list node
stProdStack -&gt; struct to keep database information, namely ll tail and size (N)
record() examines db information struct tail pointer and order_id within range, then calls actual method to add node to the list: add_product().
I did not implement unit testing due to limited time to seat on.
PARDON MY ENGLISH xD
Code:
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdbool.h&gt;

//backward/stack linked list for storing transaction action log
typedef struct stProduct
{
    struct stProduct * prev;
    unsigned int order_id;
}
stProduct;

typedef struct stProdStack
{
    struct stProduct * tail;
    unsigned int size;
}
stProdStack;

stProduct* add_product(stProduct *tail, unsigned int order_id)
{
    stProduct *product = malloc(sizeof *product);
    if (!product) return NULL;
    product-&gt;order_id = order_id;
    product-&gt;prev = tail;
    return product;
}

bool record(stProdStack *stListInfo, unsigned int order_id)
{
    if (!stListInfo)
        return false;
    stProduct *tail = add_product(stListInfo-&gt;tail, order_id);
    if (tail)
    {
        stListInfo-&gt;tail = tail;
        return true;
    }
    else
    {
        return false;
    }
}

unsigned int get_last(const stProdStack *stListInfo, unsigned int i)
{
    if (!stListInfo) return 0;
    if (i &gt; stListInfo-&gt;size) return 0;

    stProduct *current = stListInfo-&gt;tail;
    for (int j = 0; j &lt; i - 1; j++)
    {
        current = current-&gt;prev;
    }

    return current-&gt;order_id;
}

int main(void)
{
    unsigned int N = 100;
    stProdStack product_stack = { NULL, N  };

    for (unsigned int i = 0; i &lt; 300; i++)
    {
        if (!record(&amp;product_stack, 1000 + i))
        {
            printf(&quot;transaction log record failed\n&quot;);
            break;
        }
    }

    printf(&quot;10th last product ID: %u\n&quot;, get_last(&amp;product_stack, 10));

}

Result:
10th last product ID: 1290
The code doesn't fulfill the requirements. You are supposed to log the last N orders. Instead you log all of them. The old orders shall be removed from the list as it grows beyond N.

Bug. The list is initialized with the size being set to N. However, at the beginning it may contain less than N elements. In the scenario
  List is empty
  An order is added
  get_last(2) is called

the loop
  for (int j = 0; j &lt; i - 1; j++)
  {
      current = current-&gt;prev;
  }

is guaranteed to access a null pointer.

The above loop complexity is \$O(N)\$.


All that said, I recommend to use not a list, but a circular buffer. No need to malloc anything, and get_last is executed in \$O(1)\$.