There are special combinations of inline and multiline comments in C that you
abuse exploit take advantage of utilize to comment out one of two
mutually-exclusive code blocks, and then easily swap between them.
In this example, the first main
function is compiled, while the second is
commented out.
//*/
int main(int argc, char **argv) {
printf("Hello world!\n");
}
/*/
int main(int argc, char **argv) {
printf("Goodbye world!\n");
}
//*/
Just remove the first forward slash (/
) to reverse it! That will comment out
the first code area while compiling the second.
/*/
int main(int argc, char **argv) {
printf("Hello world!\n");
}
/*/
int main(int argc, char **argv) {
printf("Goodbye world!\n");
}
//*/
This works because C will ignore the start of a multiline comment if it appears
on a commented line, and will end a multiline comment on the first closing mark
(*/
) it sees. When C encounters the text //*/
it can treat it as the start
of a single-line comment or the end of a multiline comment. Additionally, when
C encounters /*/
, it can interpret it as the start of a multiline comment or
the end of a multiline comment.
So, when we give C the following example, it will create a single-line comment, compile the next line, and then start a multiline comment until it finds the end marker on the last line.
//*/
printf("The answer is 42\n");
/*/
printf("88 miles per hour\n");
//*/
// */ |
single-line comment |
printf("The answer is 42\n"); |
compiled code |
/* |
begin multiline comment |
/ printf("88 miles per hour\n"); // |
multiline comment |
*/ |
end multiline comment |
Then, when we remove the first forward slash (/
), it then interprets the first
line as the beginning of a multiline comment until it finds the end marker on
the middle line, and will interpret the last line as a single-line comment.
/*/
printf("The answer is 42\n");
/*/
printf("88 miles per hour\n");
//*/
/* |
begin multiline comment |
/ printf("The answer is 42\n"); / |
multiline comment |
*/ |
end multiline comment |
printf("88 miles per hour\n"); |
compiled code |
// */ |
single-line comment |
Toggling multiline comments like that is cool and all, but it's not something
you couldn't already do with #if
and #else
preprocessor directives, and
honestly that's what you really should be doing in this situation.
#if 1 // 1 to include the first section, and 0 to include the second.
int main(int argc, char **argv) {
printf("Hello world!\n");
}
#else
int main(int argc, char **argv) {
printf("Goodbye world!\n");
}
#endif
However, there is a variation of this comment trick that you can't do with preprocessor directives: toggling sections of code on a single line.
// Prints "The magic number is: 42"
printf("The magic number is: %d\n", /*/*/ 42 /*/ 88 /*//**/);
In our first example, we begin our toggling statement with /*/*/
, which C
interprets as the beginning of an inline comment, then a single forward slash,
followed by the end of an inline comment. It then compiles in the number 42
before encountering /*/
, which is interprets as the beginning of an inline
comment followed by a forward slash, which is comments out along with the 88
.
It then encounters /*/
again, which it interprets this time as a commented-out
forward slash followed by the end of an inline comment. Finally, it sees /**/
as an empty inline comment.
/* |
/ |
*/ |
42 |
/* |
/ 88 / |
*/ |
/* |
*/ |
---|---|---|---|---|---|---|---|---|
begin | comment | end | compile | begin | comment | end | begin | end |
To toggle our comment to print 69
instead, just add a space after the second
asterisk (*
).
// Prints "The magic number is: 88"
printf("The magic number is: %d\n", /*/* / 42 /*/ 88 /*//**/);
Now C will see /*/*
as the beginning of a single inline comment. It will then
proceed to comment out 42
until reaches /*/
, which it interprets as the end
of the inline comment. C will carry on to compile in 88
before it again
encounters /*/
, which this time it reads as the beginning of an inline
comment. C will comment out the following two forward slashes and asterisk until
it finds */
to end the inline comment.
/* |
/* / 42 / |
*/ |
88 |
/* |
//* |
*/ |
---|---|---|---|---|---|---|
begin | comment | end | compile | begin | comment | end |
This is all possible because C's inline/multiline comment syntax contains what's arguably a design flaw: you can't nest them within each other. In this regard the C preprocessor is a simple beast. It blindly reads these inline/multiline comments until it finds the first match for their end. It doesn't try to keep track of how many markers it finds to start comments. It's yet another part of C you can shoot yourself in the foot with. If you try to comment out a large chunk of code, you may not actually comment it all out if it already contained an inline or multiline comment.
It's a product of the times. The original C compilers didn't have much memory available to them, and wasn't really worth it to keep track of comment nesting. Nowadays with our comparably limitless working memory, there's no reason a modern language shouldn't keep track of nested comments.
But I'll give C a pass for this. I just think it's neat.