Skip to content

Instantly share code, notes, and snippets.

@stevebrun
Created May 12, 2024 21:40
Show Gist options
  • Save stevebrun/55947b9c92d00790d7cb6b8484756db8 to your computer and use it in GitHub Desktop.
Save stevebrun/55947b9c92d00790d7cb6b8484756db8 to your computer and use it in GitHub Desktop.
Toggle Comments in C

Toggle Comments in C

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.

Toggling Multiline Comments

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 Inline Comments

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

Conclusion

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment