I\'m reading some lecture notes of my C++ lecturer and he wrote the following:
- Use Indentation // OK
- Never rely on operator preced
Let's attempt to also modify i
when we increment j
:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
if (i % 2 == 0)
j++;
i++;
Oh no! Coming from Python, this looks ok, but in fact it isn't, as it's equivalent to:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
if (i % 2 == 0)
j++;
i++;
Of course, this is a silly mistake, but one that even an experienced programmer could make.
Another very good reason is pointed out in ta.speot.is's answer.
A third one I can think of is nested if
's:
if (cond1)
if (cond2)
doSomething();
Now, assume you now want to doSomethingElse()
when cond1
is not met (new feature). So:
if (cond1)
if (cond2)
doSomething();
else
doSomethingElse();
which is obviously wrong, since the else
associates with the inner if
.
Edit: Since this is getting some attention, I'll clarify my view. The question I was answering is:
What's the benefit of using the 1st version?
Which I have described. There are some benefits. But, IMO, "always" rules don't always apply. So I don't wholly support
Always use a { } block - even for a single line // not OK, why ???
I'm not saying always use a {}
block. If it's a simple enough condition & behavior, don't. If you suspect someone might come in later & change your code to add functionality, do.
If you are a compiler, it doesn't make any difference. Both are the same.
But for programmers, the first one is more clear, easy to read and less error-prone.
Because when you have two statements without {}
, it's easy to miss an issue. Let's assume that the code looks like this.
int error = 0;
enum hash_type hash = SHA256;
struct hash_value *hash_result = hash_allocate();
if ((err = prepare_hash(hash, &hash_result))) != 0)
goto fail;
if ((err = hash_update(&hash_result, &client_random)) != 0)
goto fail;
if ((err = hash_update(&hash_result, &server_random)) != 0)
goto fail;
if ((err = hash_update(&hash_result, &exchange_params)) != 0)
goto fail;
goto fail;
if ((err = hash_finish(hash)) != 0)
goto fail;
error = do_important_stuff_with(hash);
fail:
hash_free(hash);
return error;
It looks fine. The issue with it is really easy to miss, especially when the function containing the code is way larger. The issue is that goto fail
is ran unconditionally. You can easily imagine how frustrating this is (making you ask why last hash_update
always fails, after all everything looks fine in hash_update
function).
However, that doesn't mean I'm for adding {}
everywhere (in my opinion, seeing {}
everywhere is annoying). While it can cause issues, it never did for my own projects, as my personal coding style forbids conditionals without {}
when they aren't on the same line (yes, I agree that my coding style is unconventional, but I like it, and I use project's code style when contributing to other projects). This makes the following code fine.
if (something) goto fail;
But not the following one.
if (something)
goto fail;
I like Luchian's accepted answer, in fact I learned the hard way that he is right, so I do always use braces, even for single-line blocks. However, personally I make an exception when writing a filter, as you are in your example. This:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
if (i % 2 == 0)
{
j++;
}
}
looks cluttered to me. It separates the for loop and the if statement into separate actions, when really your intent is a single action: to count all of the integers divisible by 2. In a more expressive language, this could be written something like:
j = [1..100].filter(_%2 == 0).Count
In languages which lack closures, the filter cannot be expressed in a single statement, but must be a for loop followed by an if statement. However, it is still one action in the mind of the programmer, and I believe that should be reflected in the code, like so:
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
if (i % 2 == 0)
{
j++;
}
My 2c:
Use Indentation
Obviously
Never rely on operator precedence - Always use parentheses
I wouldn't use words "never and "always", but in general I see this rule being useful. In some languages (Lisp, Smalltalk) this is a non-issue.
Always use a { } block - even for a single line
I never do that and never had a single problem, but I can see how it can be good for students, esp. if they studied Python before.
Const object on left side of comparison
Yoda conditions? No, please. It hurts readability. Just use the maximum warning level when you compile your code.
Use unsigned for variables that are >= 0
OK. Funny enough, I've heard Stroustrup disagree.
Set Pointer to NULL after deletion - Double delete protection
Bad advice! Never have a pointer which points to a deleted or non-existing object.
Always having curly braces is very simple and robust rule. However the code may look inelegant when there are lot of braces. If the rules allow to omit curly braces then there should be more detailed style rules and more sophisticated tools. Otherwise it may easily result with chaotic and confusing (not elegant) code. Therefore looking single style rule separate from rest of style guides and tools used is likely fruitless. I will just bring some important details about that rule #3 that haven't even been mentioned in other answers.
First interesting detail is that most proponents of that rule agree to violate it on case of else
. In other words they do not demand in review such code:
// pedantic rule #3
if ( command == Eat )
{
eat();
}
else
{
if ( command == Sleep )
{
sleep();
}
else
{
if ( command == Drink )
{
drink();
}
else
{
complain_about_unknown_command();
}
}
}
Instead, if they see it they may even suggest to write it like that:
// not fully conforming to rule #3
if ( command == Eat )
{
eat();
}
else if ( command == Sleep )
{
sleep();
}
else if ( command == Drink )
{
drink();
}
else
{
complain_about_unknown_command();
}
That is technically violation of that rule since there are no curly brackets between else
and if
. Such duality of the rule surfaces when to try to apply it to code base automatically with a mindless tool. Indeed, why to argue, just let a tool to apply style automatically.
Second detail (that is also often forgotten by proponents of that rule) is that the errors that may happen are never only because of violations of that rule #3. Actually those almost always involve violations of rule #1 too (that no one argues with). Again from viewpoint of automatic tools, it is not hard to make a tool that immediately complains when rule #1 is violated and so most of the errors can be caught timely.
Third detail (that is often forgotten by opponents of that rule) is the confusing nature of empty statement that is represented by single semicolon. Most developers with some experience became confused sooner or later by sole misplaced semicolon or by empty statement that is written using sole semicolon. Two curly braces instead of single semicolon are visually way easier to spot.