It gives another version that separates code and data. The challenge is that in x86 assembler, there is no distinction between code and data at the cpu-acting-on-memory level - either way you're reading memory and indeed reading some of the same byte values of member.
So in 16-bit and 32-bit x86 CPUs, starting with 80286, it was possible to distinguish code memory and data memory, without allowing any mixing between them. The distinction was possible even in the assembly source text, for some of the more sophisticated assemblers, because you could declare the segments used, with all their attributes, and the assembler could check if the segments were used correctly, based on ASSUME directives that specified the content of the segment registers.
In the more recent 64-bit x86 CPUs, it is possible to prohibit instruction fetches from a memory page, but it is not possible to prohibit data reads from a memory page with code.
A C program that reverse engineered itself by reading its own machine code is also not a quibe, to be clear.
#include <stdio.h>
int main(int c,char** v){char*s="#include <stdio.h>%cint main(int c,char** v){char*s=%c%s%c;printf(s,10,34,s,34,10);}%c";printf(s,10,34,s,34,10);}
Surely this should count as a non-cheated quine? Still, parts of the code (namely, the contents of string s) are embedded directly in the binary: % clang -std=c99 -pedantic quine.c -o quine; strings quine | grep include
#include <stdio.h>%cint main(int c,char** v){char*s=%c%s%c;printf(s,10,34,s,34,10);}%c
Even though parts of the original source survive in the binary and are passed as pointers to a print function, the source code itself doesn't get read at compile or runtime (aside from being read once into the compiler).If it's OK to read parts of the loaded binary to use as strings, I don't see why it wouldn't be OK to read the whole loaded binary, as long as you touch the source code file. I'd simply accept that the platform allows for some fairly trivial quines.