21
int main()
{
    int var = 0;; // Typo which compiles just fine
}

In both C and C++, this is allowed because an expression-statement may be just a ;, which makes it a "null statement". Why is this allowed?

5
  • get your hands on a microcontroller with a low count of timers, and soon you'll want to make use of NOP instructions... implemented with ; on C (C++ in this scenario is unlikely)
    – jpinto3912
    Commented May 5, 2009 at 13:37
  • @R.A. a ;; is not a blank before a compiler optimization: it's a NOP.
    – jpinto3912
    Commented May 5, 2009 at 13:38
  • @jpinto: Think you misread my comment.
    – sharkin
    Commented May 5, 2009 at 13:44
  • Technically, this is not an empty expression (which C/C++ does not allow), but the second semicolon is a null-expression. If for instance you want to do nothing in one of the branches of a conditional (ternary) expression (in void context), you are forced to write something useless (that moreover must match what is in the other branch). Commented Jul 11, 2021 at 12:55
  • 2
    I don't see any reason to not allow it. Any reasonable optimizing compiler will just discard the stray ; and generate no code at all for it, so it does no harm and there are situations (like macros and auto generated code) where avoiding stray semicolons would be a pain. Commented Oct 8, 2023 at 18:28

11 Answers 11

35

How else could assert(foo == bar); compile down to nothing when NDEBUG is defined?

2
  • 5
    Well, it occurs to me now that letting it compile down to something like 0 or ((void) 0) would probably be fine too, but anyway... Commented Nov 14, 2011 at 20:04
  • assert() could be a macro and in NDEBUG reverts to an expression with no side effects, .e.g (void)0;
    – Nick
    Commented Dec 5, 2017 at 17:04
13

You want to be able to do things like

while (fnorble(the_smurf) == FAILED)
    ;

and not

while (fnorble(the_smurf) == FAILED)
    do_nothing_just_because_you_have_to_write_something_here();

But! Please do not write the empty statement on the same line, like this:

while (fnorble(the_smurf) == FAILED);

That’s a very good way to confuse the reader, since it is easy to miss the semicolon, and therefore think that the next row is the body of the loop. Remember: Programming is really about communication — not with the compiler, but with other people, who will read your code. (Or with yourself, three years later!)

2
  • In my opinion, a much cleaner way to express such intent is to use continue: while (fnorble(the_smurf) == FAILED) continue; (preferably with the continue; statement on its own line — here it is not possible due to the formatting constraints of Stack Overflow comments). Another option is do; while (fnorble(the_smurf) == FAILED);, but I personally like it less.
    – ib.
    Commented Feb 29 at 15:49
  • That's the reason I first run code through an indenter before trying to figure it out.
    – vonbrand
    Commented May 30 at 12:26
11

This is the way C and C++ express NOP.

6
  • Which is of prime importance on tight-budget microcontroller systems.
    – jpinto3912
    Commented May 5, 2009 at 13:34
  • 32
    @jpinto3912: In fact ; will almost certainly compile down to no instructions at all, rather than to the target platform's NOP instruction. Commented Aug 14, 2010 at 4:00
  • 4
    I doubt it. More likely to be an intrinsic, or asm. No instructions != NOP.
    – Nick
    Commented Dec 5, 2017 at 17:02
  • There is no way in C or C++ to express NOP and ; certainly isn't it. You will need intrinsics or inline ASM for that. A stray ; will just be optimized away to no instructions at all. This answer is incorrect. Commented Oct 8, 2023 at 18:32
  • 2
    Then it's misleading. The primary meaning of NOP is an assembly or machine instruction, which is not relevant to this question. Despite the wikipedia usage, using it for C++ is non-standard and rare; I've never come across it before in many discussions about C++ and other languages. Note every single comment on your answer thought you meant some relation to assembly/machine code.
    – aschepler
    Commented Oct 10, 2023 at 17:12
11

OK, I’ll add this to the worst case scenario that you may actually use:

for (int yy = 0; yy < nHeight; ++yy) {
    for (int xx = 0; xx < nWidth; ++xx) {
        for (int vv = yy - 3; vv <= yy + 3; ++vv) {
            for (int uu = xx - 3; uu <= xx + 3; ++uu) {
                if (test(uu, vv)) {
                    goto Next;
                }
            }
        }
    Next:;
    }
}   
1
  • 5
    +1 for the useful example: since goto labels must have a statement after them, but on the rare occasion you need a goto the label is often just needed at the bottom of a loop, so you need a NOP statement. You may rail against gotos, but it is occasionally preferable (AND more readable) to use a goto to exit deeply nested loops, rather than having extra variables and if() statements to exit each loop individually.
    – Darren
    Commented Nov 6, 2014 at 17:02
10

I'm no language designer, but the answer I'd give is "why not?" From the language design perspective, one wants the rules (i.e. the grammar) to be as simple as possible.

Not to mention that "empty expressions" have uses, i.e.

for (i = 0; i < INSANE_NUMBER; i++);

Will dead-wait (not a good use, but a use nonetheless).

EDIT: As pointed out in a comment to this answer, any compiler worth its salt would probably not busy wait at this loop, and optimize it away. However, if there were something more useful in the for head itself (other than i++), which I've seen done (strangely) with data structure traversal, then I imagine you could still construct a loop with an empty body (by using/abusing the "for" construct).

4
  • 3
    Will that actually wait? Can the compiler optimize away the loop since nothing happens?
    – Tom
    Commented May 5, 2009 at 11:53
  • Good question. Now that you mention it, I don't know. All I know is that it should. :)
    – Dan Fego
    Commented May 5, 2009 at 11:55
  • @Tom: Good question. In C++ at least, according to the standard, execution time is not explicitly part of the "observable behaviour" of the system, so in theory I suppose a compiler could optimise this out. However, if i is volatile then this optimisation is forbidden. Commented May 5, 2009 at 11:58
  • " From the language design perspective, one wants the rules (i.e. the grammar) to be as simple as possible" - C++ lost that battle decades ago. But, I get what you mean ;) Commented Oct 15, 2023 at 0:01
8

I honestly don't know if this is the real reason, but I think something that makes more sense is to think about it from the standpoint of a compiler implementer.

Large portions of compilers are built by automated tools that analyze special classes of grammars. It seems very natural that useful grammars would allow for empty statements. It seems like unnecessary work to detect such an "error" when it doesn't change the semantics of your code. The empty statement won't do anything, as the compiler won't generate code for those statements.

It seems to me that this is just a result of "Don't fix something that isn't broken"...

3
  • 1
    DMR's original C compiler (which is where this syntax comes from) was a recursive descent compiler - such compilers are normally hand written, so I don't think the toolds were an issue here.
    – anon
    Commented May 5, 2009 at 12:04
  • That almost makes it seem more likely that this sort of thing would happen. If you are handcoding a r.d.p., then you are writing a function for every production in your grammar whose job is to match the next stretch of input... If expressions and other stuff make up statements, and both expressions and other stuff can end up consuming no input (except for the ultimate semicolon to follow), then the parser would recognize such statements. Idk if this is the real reason, but it seems like a reasonable way to reason it out. (I never wrote a huge r.d.p., but I have written one before).
    – Tom
    Commented May 5, 2009 at 18:38
  • 1
    I am looking now at one particular Yacc grammar for a C-like language, and the empty statement requires an extra rule, so the opposite seems to be true. At least with that grammar, for that language, for that tool. Commented May 6, 2009 at 3:53
6

Obviously, it is so that we can say things like

for (;;) {
  // stuff
}

Who could live without that?

6
  • 13
    That's not a good example, because, for example, the second semicolon is not used to end a statement. The second clause of the for is an expression; and leaving that empty is treated as a special case making it equivalent to true.
    – newacct
    Commented May 5, 2009 at 18:06
  • 5
    That's actually a poor and irrelevant example. The ; inside the header of for have nothing in common with the ; in the OP's question. What you have inside for header are not statements at all. And the ; in the above for are not "empty statements". The ; in for are simply hardcoded characters of for syntax with no special meaning. Only the first section of for header is referred as "statement" in the language spec, but even that one is a special kind of for-specific pseudo-statement with its own dedicated grammar. Commented Oct 24, 2013 at 23:13
  • 5
    ... In other words, the fact that for(;;) is legal has absolutely nothing to do with the fact that empty statements are legal. The two are described by their own, absolutely unrelated parts of the grammar and language spec. Commented Oct 24, 2013 at 23:14
  • 1
    And the original question is actually about statements, not expressions. The OP misused the term "expression" apparently because they haven't learned the difference between expressions and statements. Commented Oct 24, 2013 at 23:15
  • 1
    I can't believe 13 people +1ed this. Commented Oct 3, 2020 at 5:37
4

There are already many good answers but have not seen the productive-environment sample.

Here is FreeBSD's implementation of strlen:

size_t
strlen(const char *str)
{
    const char *s;

    for (s = str; *s; ++s)
        ;
    return (s - str);
}
2

When using ;, please also be aware about one thing. This is ok:

a ? b() : c();

However this won't compile:

a ? b() : ; ;
1
  • 4
    That's because ; cannot substitute for an expression; only for a statement.
    – namezero
    Commented Sep 7, 2015 at 11:24
1

The most common case is probably

int i = 0;
for (/* empty */; i != 10; ++i) {
    if (x[i].bad) break;
}
if (i != 10) {
    /* panic */
}
2
  • The ";;" in for aren't statement ending semicolons.
    – vonbrand
    Commented Jun 3 at 14:44
  • @vonbrand: You might want to check the actual C++ grammar. The first one is.
    – MSalters
    Commented Jun 4 at 7:47
1
while (1) {
    ;  /* do nothing */
}

There are times when you want to sit and do nothing. An event/interrupt driven embedded application or when you don't want a function to exit such as when setting up threads and waiting for the first context switch.

example: http://lxr.linux.no/linux+v2.6.29/arch/m68k/mac/misc.c#L523

3
  • 9
    You don't need the semicolon there! Commented May 5, 2009 at 12:27
  • You could write while(1); And true, in some cases you do not need the ';' however that is not true with all compilers. A fair amount of embedded compilers will yell at you if you are missing it. However you are correct that in the ANSI C Grammar it is not required.
    – user101511
    Commented May 5, 2009 at 12:32
  • 1
    @user101511 Please name an actual compiler that will fail this code. Handwaving is not sufficient. Any compiler that does so is incorrect, and in a way that is trivial to fix.
    – user207421
    Commented May 27 at 15:24

Not the answer you're looking for? Browse other questions tagged or ask your own question.