💬 C Basics

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

🖥️ Ccomments.c
#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 C

Notice 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.

🖥️ Cnesting-bug.c
/*
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:

🐚 Shellterminal
$ 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.

LanguageSingle-LineMulti-LineNestable?First Available
C89/C90❌ Not standard/* ... */❌ No1989
C99+// .../* ... */❌ No1999
C++// .../* ... */❌ No1979 (CFront era)
Python# ...''' ... ''' (string, not a true comment)N/A1991
Rust// .../* ... */✅ Yes2010
Java// .../* ... */ and /** ... */ (Javadoc)❌ No1995

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.

🖥️ Cdoxygen-example.c
/**
 * @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?

Medium

2. Predict the output: `int x = 5 /* start */ + 3; printf("%d", x);`

Easy

3. Given a block comment that contains another /* ... */ inside it, where does the outer comment actually end?

Hard

4. Why might `// TODO: fix this later` be considered a poor comment in a shared repository?

Easy

5. What compiler error would you expect from an unterminated /* comment at the top of a file with several functions below it?

Medium

6. Is `#if 0 ... #endif` technically a comment?

Medium

Closing 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.

Frequently Asked Questions (FAQ)