I believe this question can be interpreted in two different ways. The first is the most literal: "Why does a C compiler allow this syntax?" The second is probably more vauge: "Why was C designed to allow such syntax to be legal?"
The answer to the first can be found in The C Programming Language (a highly recommend book if you do not already have it) and comes down too "because the language says so. It's just the way it is defined.
In the book you can refer to Appendix A to find a description of how the grammar is broken down. Specifically A7. Expressions, and A9. Statements.
A9.4 Selection Statements states:
selection-statement:
if ( expression ) statement
if ( expression ) statement else statement
switch ( expression ) statement
Meaning that any valid expression, of which assignment applies, is legal as the 'argument' to the selection with a minor cavet (emphasis is my own):
In both forms of the if statement, the expression, which must have arithmetic or pointer type, is evaluated, including all side effects, and if it compares unequal to 0, the first substatement is executed.
This might seem odd if you are coming from a language like Java, that requires the result of an expression used in a conditional to be expressly 'boolean' in nature, that attempts to lower runtime errors that are the results of typographical issues (i.e. using =
instead of ==
).
As for why C's syntax is like this I am not sure. A quick Google search returns nothing immediately but I offer this conjection (in which I stess I have found nothing to back up my claim and my experience with assembly languages is minimal):
C was designed to be a low level language that mapped closely to assembly level mechanisms; making it easier to implement a compiler for, and to translate assembly to.
In assembly level languages branches are the results of instructions that look at registers and decided to do. The work previously placed in the register is of no concern. Decrementing a counter is not a boolean operation but testing the resulting value in the register is. Allowing a general expression possibly made implementations of C easier to write. The original compiler written by Dennis Ritche simply spat our assembly files that needed to be assembled manually.