char FAR *p;
  char FAR *mem = farmalloc(65536);

  for (p = &mem[65535]; p >= &mem[0]; p--) {
    dostuff(p);
  }
Nice one.

To be fair to Windows, good C courses should still teach this, but I'm not sure if they do :-)

It's UB to set a pointer to before the first element of an array, or after the last element plus one. So, if it knows the call to farmalloc/malloc returns the start of an object, a modern C compiler on a modern architecture may, in principle, optimise the above to an infinite loop.

I've seen something similar on architectures (long ago) where a zero-bit-pattern pointer was a valid memory address you might actually access. Of course p-1 is not less than p when p is zero.

None of my college CS courses used programming languages that featured FAR pointers.

The above example would cause an infinite loop on Win16's seg:off far memory model, but compiling on Win32 would not cause an infinite loop.

Problem is that far pointers only affect the offset, not the segment. So decrementing a 0 value offset would just wrap around to 0xFFFF and the segment would stay the same, so you're going from mem[0] to mem[65535] not mem[-1].