Print strings in order with dynamic arrays
When allocating an array to hold arbitrary numbers of things, consider using calloc and reallocarray (reallocarray is not standard C, but there isn't a standard analog to calloc).

The reason is that calloc will fail if you try to allocate more memory than size_t can hold. If, for example on a 32-bit system, you decide you want to allocate 500 million of some struct, you could do this:

typedef struct { char a; } little_struct_t;
...
ptr = malloc(500000000 * sizeof(little_struct_t));


and it might work. But later, when you decide that your struct actually needs more stuff, you'll end up with something like this:

typedef struct { char a; int b; bool c } little_struct_t;
...
ptr = malloc(500000000 * sizeof(little_struct_t));


and boom, you've just caused a multiplication overflow without thinking about it. If instead you had used calloc, you'd either see the error when you checked that ptr was not NULL (see my next comment) or a happy segfault when you try to dereference ptr. Instead, now you have a 1705032704-byte ((sizeof(little_struct_t) * half a billion) &amp; 0xffffffff)) chunk of memory, and if you're lucky you'll get a segfault when you write off the end of it. If you're not lucky, you'll corrupt a bunch of other memory before your program crashes.

My second comment is that you should always always check the return value of malloc. If it's not NULL, no harm done. If it is, either you want to crash right now or you want to take a different path. Whatever happens, you don't want your program to happily keep chugging along as though nothing's wrong.

Third, and this isn't super relevant to your example (though I would apply it in deallocate_words after the free call inside the loop), but it's good practice to null out your pointers after you free them. I think I've spent too many words already, so I'll just say that it will simplify debugging and get rid of double free issues in the future.