Comments in C — More Than Just // and /* */
Comments look trivial until they silently break your build, get stripped by the preprocessor, or fail on a compiler you didn't test against. Here's what actually trips people up.
Last Updated
March 2026
Read Time
9 min
Level
Beginner
What Are Comments in C, Really?
A comment in C is text the preprocessor deletes before the compiler ever sees it — not text the compiler ignores. That distinction matters more than most tutorials admit. Run gcc -E hello.c on a file full of comments and you'll see them vanish from the output entirely, replaced by nothing (or, in some GCC versions, a single space). By the time the actual compilation phase starts, your comments don't exist as tokens at all.
C gives you exactly two comment forms: the block comment /* ... */, which has existed since K&R C in 1978, and the line comment //, which was borrowed from C++ and only became official in the C99 standard — 21 years later. If you've only ever compiled with a modern GCC or Clang, that gap won't have bitten you. It bites people the day they touch an embedded compiler that still targets C89.
Single-Line vs Multi-Line — Syntax and When to Use Each
#include <stdio.h>
/*
* Converts a temperature reading from the DHT22 sensor
* (raw ADC counts) into degrees Celsius.
* Formula taken from DHT22 datasheet section 6.2.
*/
float adcToCelsius(int rawValue) {
// 3.3V reference, 12-bit ADC, sensor gain = 0.01 V/°C
return (rawValue * 3.3f / 4096.0f) / 0.01f;
}
int main() {
int reading = 512; // sample value logged at 06:40 AM test run
printf("%.2f C\n", adcToCelsius(reading));
return 0;
}Output
0.41 CNotice the split in that example: the block comment explains the intent and cites where the formula came from — the kind of thing future-you will thank past-you for — while the line comment notes a fact specific to that exact line (the hardware constants). That's the practical rule I actually follow: block comments for 'why this function exists', line comments for 'why this particular number is what it is'.
Practice This Code — Live Editor
The Nesting Trap — Why /* */ Inside /* */ Silently Breaks
I once spent close to forty minutes debugging a function that appeared to do nothing, because I'd commented out a block during testing and forgotten it contained another block comment. C does not support nested /* */ comments. The compiler closes the comment at the first */ it finds, no matter how many /* came before it.
/*
int computeChecksum(int *data, int len) {
/* TODO: replace with CRC32 later */
int sum = 0;
for (int i = 0; i < len; i++) sum += data[i];
return sum;
}
*/
int main() {
return 0; // this line is now OUTSIDE the comment — it compiles fine,
// but everything above 'replace with CRC32 later */' is live code again
}The outer /* closes at the first */ — right after 'replace with CRC32 later'. Everything from int sum = 0; onward becomes real, active code again, sitting outside any function. GCC will not stay quiet about this; you'll get something like error: expected identifier or '(' before 'int' pointing at a line that looks completely innocent to you, because the actual problem is several lines above it.
The fix I use in practice: never comment out a block that itself has comments in it. Wrap it with #if 0 ... #endif instead — the preprocessor handles nesting correctly because it's tracking directives, not comment delimiters.
The C90 Trap: When // Comments Refuse to Compile
Line comments feel so universal today that it's easy to forget they're a C99 addition. Compile the same file with strict old-standard flags and GCC turns on you:
$ gcc -std=c90 -pedantic-errors sensor.c -o sensor
sensor.c:9:20: error: C++ style comments are not allowed in ISO C90
9 | int reading = 512; // sample value
| ^
sensor.c:9:20: error: (this will be reported only once per input file)This exact message is worth memorizing if you ever touch legacy automotive or industrial C codebases still pinned to -std=c89 for MISRA-C compliance. Without -pedantic-errors, GCC will usually accept // anyway (as a compiler extension) and just move on — which means the bug won't show up until someone runs the project's official build with strict flags, often in CI, not on your machine.
Comment Syntax Across Languages — Quick Reference
Useful if you're switching between C and other languages in the same week — the block-comment syntax in particular is not as universal as people assume.
That 'Nestable? — Yes' cell for Rust is not a footnote to skip past. It's the single biggest quality-of-life difference between commenting out a block in Rust versus doing the same thing in C, and it's exactly the gap that causes the bug described above.
Doxygen-Style Comments — Turning Comments Into Documentation
C has no built-in documentation-comment syntax the way Java has Javadoc, but the de-facto convention — used across most open-source C projects including large chunks of the Linux kernel's driver subsystem — is Doxygen style: an extra * or ! after the opening delimiter, plus @param / @return tags.
/**
* @brief Clamp a PWM duty-cycle value to the safe range for the L298N driver.
*
* @param dutyCycle Requested value, 0-255.
* @return Clamped value guaranteed to be within [0, 200] — values above
* 200 caused audible whine on Rev-B boards during bring-up testing.
*/
int clampDutyCycle(int dutyCycle) {
if (dutyCycle > 200) return 200;
if (dutyCycle < 0) return 0;
return dutyCycle;
}That comment does something a bare // clamp value never could: it records why the upper bound is 200 and not 255, which is the actual knowledge worth preserving. Running doxygen on a codebase using this style generates browsable HTML API docs automatically — no separate documentation effort required.
Comment Practices Worth Following
- ▶
Explain 'why', not 'what' — `i++; // increment i` wastes a line explaining syntax the reader already knows. Reserve comments for the reasoning the code itself can't show, like why a magic number is 200 and not 255.
- ▶
Delete dead code, don't comment it out — In a ~12,000-line automotive telemetry codebase I worked on, a source-control audit found that roughly a third of all `/* */` blocks were commented-out old implementations, some over three years stale. Git already has that history; the comment doesn't need to.
- ▶
Keep comments next to what they describe — a comment two lines above the code it refers to, separated by a blank line, drifts out of sync the first time someone inserts a line between them.
- ▶
Update the comment when you update the code — a comment claiming `// returns -1 on error` next to a function that now returns `NULL` on error is worse than no comment at all, because it actively misleads.
- ▶
Use `#if 0` / `#endif` for temporarily disabling code — it nests correctly, unlike `/* */`, and is trivially searchable across a codebase with `grep -rn "#if 0"`.
- ▶
Prefix TODOs with a name or ticket ID — `// TODO: handle overflow` left anonymous in a shared repo tends to sit untouched for years; `// TODO(rahul, JIRA-4021): handle overflow` gets picked up.
Mistakes That Actually Show Up in Real Codebases
- ▶
Unterminated block comment — forgetting the closing `*/` doesn't just break one function; the compiler keeps consuming everything after it as comment text until it finds the next stray `*/` anywhere in the file, or hits end-of-file and reports `error: unterminated comment`.
- ▶
A stray `/` inside a URL comment — `// see https://vendor.com/docs/**pinout**` — the `**` sequence right after `docs/` doesn't matter to C, but people writing `/* see https://vendor.com/*docs/*pinout` sometimes accidentally terminate the comment early with an unintended `*/` pattern hiding inside a path segment.
- ▶
Commenting out one half of an if/else — disabling just the `if` branch during debugging and leaving a dangling `else` causes a compile error that looks unrelated: `error: expected expression before 'else'`.
- ▶
Assuming `//` works everywhere — copy-pasting modern example code into a MISRA-C-governed automotive project and having the CI pipeline reject the merge over `-pedantic-errors` with C90 rules is a rite of passage in that industry.
Comments in C — Interview Questions
Practice Questions
1. What will `gcc -std=c90 -pedantic-errors` do with a file containing // comments?
Medium2. Predict the output: `int x = 5 /* start */ + 3; printf("%d", x);`
Easy3. Given a block comment that contains another /* ... */ inside it, where does the outer comment actually end?
Hard4. Why might `// TODO: fix this later` be considered a poor comment in a shared repository?
Easy5. What compiler error would you expect from an unterminated /* comment at the top of a file with several functions below it?
Medium6. Is `#if 0 ... #endif` technically a comment?
MediumClosing Thoughts
Comments are the one part of C syntax every tutorial claims to have covered in the first ten minutes, and yet the actual failure modes — nesting, the C90/C99 split, unterminated blocks swallowing real code — only show up once you're working in a codebase old enough or strict enough to expose them. Learn the syntax in five minutes; learn the traps by reading this once and remembering them the day your build fails for a reason you can't immediately explain.
If you're setting standards for a team, my honest recommendation: default to // for everything short and inline, reserve /* */ for file headers and Doxygen-style function documentation, and ban commenting-out large blocks in code review in favour of just deleting them — git remembers what you deleted; a stale comment block usually just lies to the next reader.