The Search For Self-Documenting Code

By Craig S. Kaplan

Historical Note:

This report has been getting a lot more attention recently as a result of it being discovered by Yahoo!. In light of that, I figured I would provide just a little context for it.

As a COOP student at the University of Waterloo, I was required to write no fewer than four work term reports over the course of six internships. This was the fourth. It's important to realize that the reports are graded almost exclusively for form rather than content. An English major is assigned to read the report for grammar, spelling and punctuation. As far as they're concerned, I could be writing about crabgrass. Knowing my intended audience, I made some concessions towards a reader without extensive programming experience by including, for example, a glossary.

What you're about to read was written circa 1994. I re-read it recently. The writing is pretty rough (the bar was low for work-term reports, a fact that I certainly didn't call into question) but the ideas are sound. Hope you enjoy it.

I'm not exactly sure why Yahoo! put this page under its "jokes and humour" category. I guess it's not funny ha ha, just funny peculiar...


Note 2: (by Günter Rote)

The C programs no longer conform to the current standard of the C language (as of 2025), for two reasons:

Nevertheless, the gcc compiler compiles the programs, but it issues warnings. The clang compiler does it if the option -std=c89 is provided.

Abstract

This report examines the problem of writing a self-documenting program: a program that, when run, produces itself as output. The problem is examined from the point of view of self-reference, the property a self-documenting program must exhibit. The report proceeds from early programs that fail to work correctly, through successively sophisticated programs which approach a solution, to working self-documenting programs. Then it steps back a bit and shows how some programs can seem to cheat and still fit the definition of a self-documenting program, suggesting improvements to that definition. At each step, the report addresses how the given programs demonstrate the subtle relationship between computer programming and self-reference.


Table of Contents

ii. Abstract
iii. Table of Contents

  1. Introduction
  2. Self-Reference
  3. Self-Documenting Programs
  4. Early Attempts That Fail
  5. A Sophisticated Attempt That Fails
  6. Self-Documenting Programs That Work
  7. Self-Documenting Programs That Cheat
  8. Conclusions
  9. Glossary

Appendices
A. Source Code

1. Introduction

This report presents a novel intellectual challenge: the problem of writing a self-documenting program. It then attempts to solve this problem, bringing to light along the way a series of pitfalls and curiosities that underlie our understanding of context and meaning within formal systems such as computer languages. It demonstrates that the problem of self-documenting code probes at the core of questions about self-reference and the use/mention distinction.

As this report deals with issues relating to programming languages in general and C in particular, the reader is assumed to have at least a basic understanding of computer programming. Some experience with various languages, especially C, would certainly be an asset. Since the report examines the problem from the point of view of computer languages, knowledge of specific hardware platforms and operating systems is not required. However, it should be possible for the novice programmer or non-programmer to follow the main arguments presented here.

The body of this report contains six main sections. The first and second sections introduce the concepts of self-reference and self-documenting programs. They set the stage for the challenge by defining the key concepts and showing why the problem of writing a self-documenting program is non-trivial. The next three sections build up from initial attempts at self-documenting programs that fail to more sophisticated attempts that still fail, to actual working programs. At each step, a discussion of the attempt shows what needs to be revised or improved to proceed onward. The final section shows how we can stretch our initial definition of a self-documenting program to produce solutions that seem unsatisfying, and how we can appropriately modify the definition.

Throughout the report, the reader will find terms or phrases which are italicized and underlined, like this. This indicates that the word could be unfamiliar to the reader, and that a short explanation is given in the glossary, which appears at the end of the report. Clicking on that word will take the reader directly to that explanation.

Pieces of C source code appear throughout the report. In some cases, lines of source were too long to fit on a single line. In these cases, a bold ampersand '&' has been used to indicate that in the source code, the line containing the ampersand and the line immediately following it should be concatenated and treated as a single line.


2. Self-Reference

What is self-reference?

Self-reference occurs whenever a system loops back on itself and describes some aspect of its own form or structure. This mysterious phenomenon happens all the time. Whenever someone says 'I' or 'me', whenever a movie talks about movies or a television program talks about television programs, self-reference is being demonstrated.

Self-reference has some important applications, too. The proof of Gödel's Incompleteness Theorem, one of the most far-reaching concepts in mathematics and logic, relies on a formal system being able to describe its own structure. Turing used the idea of a self-referential computer program to provide a proof to the Halting Problem, an important question of computer science.

The use/mention distinction

An important aspect of self-reference is the differentiation between 'use' and 'mention'. To use something is to actually employ its meaning, to get at its deeper content. To mention something, on the other hand, is merely to refer to its form, its outer appearance. For instance, consider the following two sentences:

This sentence has five words.
"This sentence" has two words.

Intuitively, both sentences assert true propositions. Yet despite the nearly identical choice of words, they make these assertions in entirely different ways. The distinction is in the words "this sentence". In the first sentence, "this sentence" is being used. It is actually being employed, and the reader forms a mental association between the words and their meaning, namely the sentence as a whole. On the other hand, in the second sentence "this sentence" is being mentioned. The content of the two words is unimportant, because there is no attempt to derive meaning from them. All that matters is that the phrase "this sentence" does indeed have two words.

Note that people are able to see this distinction automatically, without thought. Although this sentence does not occur at all in the previous paragraph, "this sentence" occurs many times. And each time, the reader has no problem understanding that "this sentence" is being mentioned rather than used. As another example, when reading this sentence, the reader has no trouble distinguishing between the two contexts of "this sentence".

Computers and self-reference

Computer programming is full of self-reference. It is becoming nearly impossible to avoid playing with these kinds of loops in modern programming languages. For example, one of the most fundamental structures used by programmers is the linked list. A typical declaration of a linked list looks like this:
    struct link {
        int          data;
        struct link *next;
    };
This fragment of code declares a new type - a template from which to construct objects later. In this case, the type is declared as an aggregate which contains two subobjects: some piece of numerical information, called data, and a pointer to another link structure, called next.

This simple structure embodies the use/mention distinction. If I declare an object of type struct link, the computer will set aside memory to hold my new object. This block of memory will behave according to its definition, and allow access to two subobjects of the appropriate types. This new object is a use of a link. But notice how the second subobject is defined as a pointer to another link. A pointer is the encoding of a location in memory. It does not make sense for next to be of type struct link; that would make the type infinitely large! So next is not a link itself, but indicates where another such object can be found. It is a mention of a link.


3. Self-Documenting Programs

Defining self-documenting programs

Self-documenting programs are not used nearly as frequently as structures like linked lists. This is primarily because they do not have as strong a practical value - the concept serves much better as a form of recreational programming. And in that regard, they provide an interesting intellectual challenge.

The idea is simple: write a computer program that, when run, produces itself as output. For now, we will use this as the definition of a self-documenting program, although later we will see how we can stretch this definition to produce examples that seem unsatisfactory.

The complexities

Because the idea is so simply stated, one might think that there would be a simple solution, that one could just sit down at a computer and start writing a program to perform this task. In most cases, however, actually implementing a self-documenting program is far from simple. In fact, in a certain sense, it is impossible to ever start writing one! Let us examine why.

Because we are not dealing with complex data or abstractions, we would traditionally employ a top-down design to write this program. The idea behind top-down design is to begin with a very high level description of the solution, and iteratively refine this description until you arrive at the level of actual program statements. So, let us attempt this with the given problem. We begin with a simple (if somewhat ambitious) program definition, with the section to be refined written in italics:

    main() {
        print myself out.
    }
The next step would be to refine the "print myself out" section of the program. But with what? There is no straightforward way to insert code into this section of the program, because whatever code gets used in the end, it has to perform a double duty. It has to not only function properly, that is to say actually print itself out, it also has to specify what needs to be printed out, namely itself! In a sense, the code that gets inserted here needs to anticipate its own structure before it even gets written. It has to be the program and the description of the program. It has to be the use and the mention. It has to form a closed loop on itself. In this sense, you could never just start writing such a program. You would have to compose the entire program at once in a conceptual leap.

4. Early Attempts That Fail

When first faced with the problem of writing a self-documenting program, the initial attempts at solutions tend to lead to dead ends. Still, they provide insight into the structure of a working solution. Two such ideas are examined here.

Brute Force

In the brute force approach, we simply take the "print myself out" idea literally. We examine the code and start generating statements to print out the program. The first line of any C program reads:
    main() {
So obviously, the second line will have to read:
    printf( "main() {\n" );
Namely, a line that prints out the first line. Now, we have to add a line that prints out the second line. That will look like this:
    printf( "printf( \"main() {\\n\" );\n" );
And, of course, we need to include a line to print that line out:
    printf( "printf( \"printf( \\\"main() {\\\\n\\\" );\\n\" );\n" );
There is an obvious difficulty here. Any program that attempts to solve the problem this way will have to be infinitely long. As soon as the program contains any instructions at all, it will have to contain instructions to print those instructions, and a loop will begin. Brute force fails because we continuously need to refer to previously done work; we can never catch up.

Name That Program

We need to bring out the use/mention distinction to overcome this infinite loop problem. In particular, we need to separate the use and mention aspects of the program to achieve our goal. In brute force, the use and mention were wrapped up in each other. The use of each line of code contained the mention of the line before it. Perhaps we can get around this by specifying the mention first, and then using it later.

To accomplish this, we first define a string f which contains a mention of the whole program. Then we can print the program from within its own code by printing f - a mention, rather than a use, of the code. Based on this idea, we get the following program:

    char*f="main(){printf(f);}";
    main(){printf(f);}
When run, this program produces the following output:
    main(){printf(f);}
Unfortunately, we do not yet have a solution, because this is not the complete program! This idea failed to take into account the extra line that was added at the start to define the mention of the program. Our solution would have to output a copy of this line as well. But where do we obtain a mention of this line? We almost have one already! Except for the initial 'char*f="' and the closing '";', the line is a mention of itself. So we may be very close to a solution indeed. The critical step is to print out the same string twice: once as the mention, and once as the use.

5. A Sophisticated Attempt That Fails

We now proceed to a proto-attempt at a self-documenting program which is very close to a solution. This example is correct except for the fine details of the C language.

Making use of the ideas from the Name That Program example, we obtain the following program:

Failure I

    char*f="main(){printf(f);printf(f);}";main(){printf(f);printf(f);}
The output from this program is:
    main(){printf(f);printf(f);}main(){printf(f);printf(f);}
which is very nearly correct. All that is missing is the code that defines the string f. The intuitive way to put the finishing touches on this program is to rewrite it as follows:

Failure II

    char*f="main(){printf("char*f="");printf(f);printf("";\n");printf(f);}";
    main(){printf("char*f="");printf(f);printf("";\n");printf(f);}
Namely, to add the necessary code to print out the text which delimits the mention of the program and reformat the output so it runs over two lines. In theory, there is nothing wrong with this solution. In practice, it fails to even compile! As it turns out, the problem reduces to the way the C language handles the use/mention distinction internally.

In C, certain characters are treated differently depending on whether they are being used (as part of a programming instruction) or merely mentioned (as part of a string literal). Firstly, the double quote character " cannot be directly inserted into a string, because C uses it to delimit strings. There is no easy way of knowing whether a given double quote was meant as a use or a mention. C resolves this conflict by forcing the programmer to state which sense of " is desired. To mention a double quote from within a string, you must escape it by preceding it with a backslash. C understands backslashes as a special means of mentioning characters under any context. (Incidentally, to avoid taking the backslash character out of context when it is being used explicitly, one places a second backslash in front of it). Also, one cannot insert a newline character directly into a string. That, too, must be escaped, and appears in strings as \n.

Based on this discussion, one might think that the next level of refinement would simply be to escape all the double quotes and newlines inside strings. If we were to perform this transformation on Failure II above, we would obtain a new program, Failure III (the code for Failure III can be found in Appendix A). Unfortunately, when Failure III is compiled and run, its output is not itself, but Failure II!

The reason for this is that escaping a character in C introduces no new information in the final program; it is a lexical convenience, a way for the compiler to know how to treat special characters in its input. The backslashes are thrown away internally, so Failure III outputs a copy of itself without escaped characters - namely, Failure II.


6. Self-Documenting Programs That Work

The C language itself is beginning to complicate the creation of a self-documenting program with its use of escaped characters. Of course, this should not be taken as a flaw in the language; in any other application, the approach taken by C is suitable and even desirable. There simply is no way to discern a use of a double quote from a mention without additional information. That information comes in the form of the optional backslash. In the application of self-documenting code, however, it looks like another conceptual leap is in order.

That conceptual leap comes in the form of finding an alternate way to express these escaped characters. Obviously, the program is going to have to find some way to output a double quote - they will, by necessity, appear in the source code, after all! But there can be no double quotes in the actual instructions, because if a double quote appears in the program proper, it will also have to appear in the description of the program, in which case it will have to be escaped. As we have seen, this is forbidden.

Fortunately, there are other ways to output this character. The easiest is the statement putchar(34);. 34 is the standard ASCII code for the double quote. The putchar function translates the code into the correct character and outputs the double quote to the screen. Similarly, putchar(10); will output a newline character.

This leaves one more step. We still need to output 'char*f=' and ';' on either side of the string. We could use a series of statements of the form:

    putchar('c');putchar('h');putchar('a');putchar('r');...
and so on (the resulting code, Self 0, can be found in Appendix A), but a slightly more elegant solution is to incorporate these extra characters into the string used to describe the program. We can, at runtime, selectively print sections of that string corresponding to different parts of the program, by printing from an offset into the string and temporarily placing an end-of-string marker at the end of the section we wish to print.

Combining these techniques generates the first working self-documenting program:

Self I

    char f[]="char f[]=;main(){f[9]=0;printf(f);putchar(34);f[9]=';';printf(f);&
        putchar(34);f[10]=0;printf(&f[9]);f[10]='m';putchar(10);printf(&f[10]);putchar(10);}";
    main(){f[9]=0;printf(f);putchar(34);f[9]=';';printf(f);putchar(34);&
        f[10]=0;printf(&f[9]);f[10]='m';putchar(10);printf(&f[10]);putchar(10);}

When Self I is compiled and run, it outputs an exact duplicate of itself, and is therefore a successful self-documenting program. Unfortunately, Self I is cryptic and unreadable, even by C standards! The lines are long, and spacing is minimal. Furthermore, there is no practical way to reformat this program while preserving its self-reference. We cannot break the definition of string f over several lines, because as we have seen, newlines must be escaped within a string. If we inserted a newline directly into f, the program would fail to compile.

That is not to say there is no such thing as an elegant self-documenting program. It is in fact possible to write a much shorter program. The trick lies in careful use of the printf function and the fact that it allows the user to include format specifiers to reformat text before it gets printed out. Furthermore, the format string passed to printf is just a normal C string like any other. In fact, it is possible to achieve self-reference by letting the string which describes the program also serve as the format specifier for its own output! This idea gives us the following program:

Self II

    char*f="char*f=%c%s%c;%cmain(){printf(f,34,f,34,10,10);}%c";
    main(){printf(f,34,f,34,10,10);}
Here, we have overcome the problem of outputting the characters which delimit the string by making the string represent not just the use of the program, but rather the program in its entirety. The string f represents everything that needs to be printed out, with a few critical sections left out - namely, the characters that need to be escaped, and the contents of the string. That is the crucial idea behind Self II. The string f can avoid the infinite recursion of including a copy of own its contents within itself by specifying that some unknown string will later be substituted for the '%s', and then substituting f itself! This is why the printf statement contains f twice. The first time is as the format specifier, and the second is as the replacement text for the '%s' in f. And Self II has the added advantage of being relatively easy to read and understand.

There are, of course, many more possibilities. There are numerous ways to rephrase these working programs, and even programs that attack the problem from a different angle (for an example, see Self IV in Appendix A). Unfortunately, there is no room here to discuss the strategies used by those programs. At the very least, the examples given above provide a good starting point for moving forward towards exploring those other ideas.


7. Self-Documenting Programs That Cheat

Hi! A lot of people seem to locate this page by doing a Web search on "cheat programs". If you're one of those people, welcome. I don't think this is the kind of information you had in mind. Nevertheless, I welcome you to check out my home page.

Standards and Libraries

Based on the previous section, it would seem that the problem has been solved. We now have a program which is truly self-documenting. It clearly satisfies the definition of a self-documenting program given in section 2. But is Self II a satisfying solution, definition aside? For one thing, it relies quite heavily on ASCII to map 34 and 10 to double quote and newline, respectively. ASCII is not part of the C language per se. The mapping of codes to characters is specific to each machine on which the program is executed. Although ASCII is an accepted worldwide standard, it is not the only mapping that exists. Some computers use another code, called EBCDIC. Still others are adopting newer standards like Unicode or JIS which allow for characters which do not exist in ASCII, like kanji ideograms and hiragana characters. Of course, each of these standards provides some code for double quote and another for newline, but the program will have to be recoded each time. So Self II is not truly universal.

All is not lost, though. Self II can be recoded to eliminate the dependencies on specific character codes (that solution, called Self III, can be found in Appendix A). Once that is out of the way, is there anything left that might be unsatisfying? One important element remains: the printf function.

Printf is included by the committee responsible for defining the C language as a part of the standard C libraries. Note that printf is not part of C itself - C on its own provides no mechanism for output whatsoever! We are only justified in using printf (and putchar, for that matter) based on its recognition as a standard C function.

But what if C also had a standard function called print_a_prog, which was defined as follows:

    void print_a_prog() {
        printf( "extern void print_a_prog();\n" );
        printf( "main(){printf_a_prog();}\n" );
    }
The implementation of this function would be hidden away in a library. The programmer need only declare the function's existence and then use it. In this case it becomes trivial to write a self-documenting program:

Cheat I

    extern void print_a_prog();
    main(){print_a_prog();}
This program is not very satisfying, because it seems as if we are making use of a function tailor-made to our needs, one which just happened to be in the library. Perhaps one should feel compelled by one's conscience to require that Cheat I also output the definition of print_a_prog. That means that the print_a_prog function has to be self-documenting, which naturally begs the question of writing a self-documenting program. Furthermore, we certainly cannot require that Self II output the definition of printf - we do not know what it is! And even if the definition of printf was known to us, it would be a much more difficult task to include it in the output of our program. So it looks as if we must be satisfied with allowing standard library functions.

What is 'itself' to a program?

Cheat I implicitly raises another important question concerning self-documenting programs. Our definition states that such a program must be able to produce itself as output. But what exactly is meant by 'itself'? Obviously 'itself' is not meant in the same sense as it would were it applied to something like a chair. The only thing that can fill the role of 'itself' for a given chair is that chair and that chair alone. A program can never hope to truly produce 'itself' as output, and must instead settle on an exact duplicate of itself.

Even in this case, at what level should the program duplicate itself? Over its lifetime, a computer program exists in many different forms. Depending on what form we identify as the program's 'itself', we obtain different solutions to the original problem. For instance, we can identify a computer program with the file that contains its source code. In this case, we obtain the following:

Cheat II

    #include <stdio.h>
    main(){
        int c;
        FILE *f;
        f = fopen( __FILE__, "r" );
        c = fgetc( f );
        while( c != EOF ) {
            putchar( c );
            c = fgetc( f );
        }
        fclose( f );
    }
This program literaly opens its source file, reads each character in it, and echoes that character as output. The self-reference occurs in the __FILE__ macro, which gets replaced at compile time by the name of the file which contains the given program.

This example is unsatisfying because it relies on the existence of its source code in a fixed location. It seems as if a self-documenting program should exist independently of the files that created it. So, intuitively, we are making the identification between a program and itself at the wrong level. Our definition of a self-documenting program should be revised to include the clause that the program be able to run in isolation of its source.

We could take the opposite viewpoint, and identify a program with the pattern of bits that make up its representation in the computer's memory as it runs. A high level description of the resulting program would look like this:

Cheat III

    main(){
        Find myself in the computer's memory.
        Output all the bits that make me up.
    }
In some sense, this program is indeed self-documenting. But this result, too, is unsatisfying, because the output is more or less meaningless. The representation of the program in memory changes from compiler to compiler, from computer to computer, even between executions! The output from this program breaks the chain of self-documentation, because it cannot be interpreted in some way as a self-documenting program, which suggests it is not identical to the program that generated it. This suggests another addition to the definition of a self-documenting program. Not only should the program produce itself as output, but the output should be a self-documenting program!

The implementation language presents even more difficulties. So far, we have simply been using C for all our attempts at self-documentation, without addressing the possibility of such programs in other languages. The problem of defining self-documenting programs becomes overwhelming when applied to more than one language. For instance, suppose there was a language called CAT which had very simple semantics: given a file containing source code, CAT generates an executable which outputs the source code. In this case, any program will suffice! They all print out their own source. (The name CAT comes from the UNIX command 'cat' which echoes its input). This example suggests that we revise our definition yet again, to specify that the implementation language be sufficiently complex. Of course, 'sufficiently complex' is another definition altogether.

So, after reviewing the different ways a program can cheat the initial definition, a new, stronger, definition emerges:

A self-documenting program is a program written in a sufficiently complex language which, when run in isolation from its source, produces output which can be identified with itself, and which is also a self-documenting program.

8. Conclusions

Self-reference occurs whenever a system loops back on itself and describes some aspect of its own form or structure. It occurs frequently in the world, and has practical applications in domains such as mathematics and theoretical computer science.

The use/mention distinction plays an important role in self-reference. To use something is to exploit its content and meaning. To mention something is to refer to its form and appearance.

Self-reference occurs frequently in the domain of computer programming. Structures such as linked lists embody the use/mention distinction.

Self-documenting programs are a simple idea - a program that, when run, produces itself as output. The task of writing a self-documenting program is not nearly as easy as it sounds. In a certain sense, it is impossible to ever begin.

The brute force approach at a self-documenting program fails because we get caught in an infinite loop of adding an instruction to output a previous instruction.

Merely taking the description of the program out of the code fails bacause the resulting program outputs only a use, and not a mention.

The problem is further complicated by the way C handles the use/mention distinction internally: one cannot mention double quotes or newlines in a string without escaping them.

By finding an alternate expression for these special characters, such as an encoding like ASCII, one can write true self-documenting programs. Such programs tend to be cryptic and difficult to read.

These self-documenting programs can be made more elegant by using the printf function's capacity for format specifiers.

ASCII is not the only encoding used in the world; this foils some self-documenting programs, causing them to give incorrect output on some computers.

Printf is not part of the C language, but defined as a part of its standard libraries. Although this introduces a dilemma as to what functions one should be allowed to use as helpers in ones program, functions in standard libraries should be permitted in self-documenting programs anyway.

Whether or not to consider a program self-documenting also depends on what we identify as 'itself' when referring to a program. The file that contains the program's source seems to exist at too high a level. The representation of the program in the computer's memory at runtime seems too low.

Our definition of a self-documenting program should be revised to specify that the program be implemented in a sufficiently complex language, that it be able to run in isolation from its source, and that its output also be a self-documenting program.


9. Glossary

ASCII
Americal Standard Code for Information Exchange, the most widely-used alphanumeric code for data processing. ASCII represents a standard encoding of letters, numbers and punctuation.

Compiler
A tool for translating a high level description of a computer program into a lower-level description. The high level description is typically a piece of source code written in a language such as C or Pascal. The lower level description is almost always instructions the computer can understand directly: machine language.

EBCDIC
Extended Binary-Coded Decimal Interchange Code, another alphanumeric code which was popular on old IBM computers .

Escape
To escape a character in C is to precede it with a backslash, indicating to the compiler that it should be treated literally rather than as a syntactical element. In other words, an escaped character is always mentioned rather than used.

Format Specifier
A small code passed to the printf function to allow it to reformat the supplied format string. For example, when %s appears in the format string, it is replaced by a string supplied as another argument to printf.

Format String
Always the first argument to printf, the format string contains plain text, which is copied directly to the output stream, and format specifiers, which instruct printf to insert the value of an argument and insert it at the location of the specifier in the output stream.

Library
A external repository for functions used within a computer program. Libararies are written and compiled outside the scope of a given program, but the program is able to make use of the functions in a library by including the compiled library code into its own code at compile time.

Linked List
A simple structure frequently used in computer programming, consisting of a sequence of blocks of data, loosely connected through the computer's memory. Each link in a list is an independent unit. Linked lists are useful in cases where the amount of data to be manipulated is unknown in advance.

String
A basic data type in computer languages. A string is simply a block of printable characters, such as "Hello, World!". In C, strings are terminated with an end-of-string marker (zero).

Appendix A: Source Code

This appendix contains all the individual programs which appear in the report, along with the ones which are referred to but which to not appear in the body of the report.

Failure I

    char*f="main(){printf(f);printf(f);}";main(){printf(f);printf(f);}

Failure II

    char*f="main(){printf("char*f="");printf(f);printf("";\n");printf(f);}";
    main(){printf("char*f="");printf(f);printf("";\n");printf(f);}

Failure III

    char*f="main(){printf(\"char*f=\"\");printf(f);printf(\"\";\\n\");printf(f);}";
    main(){printf("char*f=\"");printf(f);printf("\";\n");printf(f);}

Self 0

    char*f="main(){putchar('c');putchar('h');putchar('a');putchar('r');putchar('*');&
        putchar('f');putchar('=');putchar(34);printf(f);putchar(34);putchar(';');&
        putchar(10);printf(f);putchar(10);}";
    main(){putchar('c');putchar('h');putchar('a');putchar('r');putchar('*');putchar('f');&
        putchar('=');putchar(34);printf(f);putchar(34);putchar(';');putchar(10);&
        printf(f);putchar(10);}

Self I

    char f[]="char f[]=;main(){f[9]=0;printf(f);putchar(34);f[9]=';';printf(f);&
        putchar(34);f[10]=0;printf(&f[9]);f[10]='m';putchar(10);printf(&f[10]);putchar(10);}";
    main(){f[9]=0;printf(f);putchar(34);f[9]=';';printf(f);putchar(34);&
        f[10]=0;printf(&f[9]);f[10]='m';putchar(10);printf(&f[10]);putchar(10);}

Self II

    char*f="char*f=%c%s%c;%cmain(){printf(f,34,f,34,10,10);}%c";
    main(){printf(f,34,f,34,10,10);}

Self III

    char a='"';char b='\n';char c='\\';
    char*f="char a='%c';char b='%cn';char c='%c%c';%cchar*f=%c%s%c;%cmain(){&
        printf(f,a,c,c,c,b,a,f,a,b,b);}%c";
    main(){printf(f,a,c,c,c,b,a,f,a,b,b);}

Self IV

    char*lines[]={
    "char*lines[]={",
    "%c%s%c%c%c",
    "0};",
    "main(){",
    "int idx;",
    "puts(lines[0]);",
    "for(idx=0;lines[idx]!=0;idx++){",
    "printf(lines[1],34,lines[idx],34,',',10);",
    "}",
    "puts(lines[2]);",
    "for(idx=3;lines[idx]!=0;idx++){",
    "puts(lines[idx]);",
    "}",
    "}",
    0};
    main(){
    int idx;
    puts(lines[0]);
    for(idx=0;lines[idx]!=0;idx++){
    printf(lines[1],34,lines[idx],34,',',10);
    }
    puts(lines[2]);
    for(idx=3;lines[idx]!=0;idx++){
    puts(lines[idx]);
    }
    }
(Self IV assumes in advance that it will be iterating across an array of strings rather than a single string. It iterates across the array the first time to output the mention, namely the definition of the array. The second iteration provides outputs the use).

Cheat I

    extern void print_a_prog();
    main(){print_a_prog();}

Cheat II

    #include <stdio.h>
    main(){
        int c;
        FILE *f;
        f = fopen( __FILE__, "r" );
        c = fgetc( f );
        while( c != EOF ) {
            putchar( c );
            c = fgetc( f );
        }
        fclose( f );
    }

Cheat III

    main(){
        Find myself in the computer's memory.
        Output all the bits that make me up.
    }