It's unclear exactly what you mean by "backwards" (and some interpretations like "print UTF-8 characters in reverse order" are infamously literally impossible[0]), but here's a straightforward/naive version (bytes in each line):
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sysexits.h>
#define die(E,...) ({ \
fprintf(stderr,__VA_ARGS__); \
exit(E); })
void reverse(char* a,size_t z) {
for(; z>=2 ;a++,z-=2) {
char t=*a; *a=a[z-1]; a[z-1]=t; } }
int main(void) {
size_t z=16,i=0;
char *a=malloc(z), *p=0;
for(;;) {
for(;;) {
ssize_t n = read(0,a+i,z-i);
if(n<0) die(EX_IOERR,"input error: %m");
if(n+i==0) exit(0);
if(n==0) die(EX_DATAERR,"error: %zu bytes of garbage at end of file",i);
p = memchr(a,'\n', (i += n) );
if(p) break;
if(i > z/2) {
a = realloc(a, (z = z+z/2) );
if(!a) die(EX_OSERR,"realloc(%zu bytes): %m",z); } }
while(p) {
reverse(a,p-a);
fwrite(a,p-a+1,1,stdout);
memmove(a,p+1, (i -= p-a+1) );
p = memchr(a,'\n',i); } } }
This produces:
$ ./a.out
cat
tac
warning: incompatible implicit declaration of built-in function
noitcnuf ni-tliub fo noitaralced ticilpmi elbitapmocni :gninraw
naïve resumé
�muser ev�an
> i guarantee the c++ code will be easier to write and understand than any c code.
Put up or shut up, as the saying goes. At the very least it ought to be easy to abstract out some of the memory allocation and file handling to use C++ builtins, and I'd be interested to see what other improvements you can manage.
Edit: 0: Exercise for the reader: consider the input "abXc", where 'X' is some as-yet-undefined codepoint that will, at some point in the future, be defined as either a combining diacritic, or a standalone character. If 'X' is a character, the output should be "cXba", but if it's a diacritic, the output should be "cbXa". The information about which (mutually-exclusive) output is correct does not exist yet.