Everyone has their code formatting pet peeves. Whether your favorite argument is tabs-vs-spaces, indentation and brace rules, camelCase-vs-PascalCase, you probably have some anecdotal situation that you staked your position on. Now I’m going to throw in my opinion on a particular formatting practice that I’ve never liked much:
A conditional control structure and its body sharing the same line of code.
Please, don’t do this.
Especially if you’re writing in a language that allows nesting scoped blocks.
Especially, especially if that language you’re writing in is C.
Especially, especially, especially if you’re porting code you don’t understand well into C.
What makes that last line so special that it deserves all those ‘especially’s? Basically, you can end up with code like this:
if (someVariableA == doSomeCheckOn(someVariableB) && !someConditionC) blah = 0;
{
int x1, x2, x3, x4;
int y1, y2, y3, y4;
// A bunch of work
}
Most programmers scanning down a long block of code that see this will keep their focus to the left, and automatically assume that the big block of work they see will only be executed when the conditional is true. The reality is exactly the opposite.
All that’s required for this incongruity to occur is a language that lets you write a statement on the same line as a conditional and create independent nested scoped blocks. But almost no one would ever write code like that. How often do you even write a nested scoped block without some control structure attached to the front of it?
That’s where C comes in when you’re restricted to pure C90, and the inability to declare your variables as you need them. For methods that grow very long with lots of variables needing to be declared all over the place, it can be tempting to create arbitrary inner scopes. It helps compartmentalize all of that data and complexity. But I think you’re still not likely to encounter this situation when writing virgin code. You’re more aware of what’s going on.
And that’s where porting other higher-level code to C comes in. Porting is a very mechanical and tedious process, and it’s easy to lose awareness of what you’re doing. Especially if you didn’t write the code in the first place, or don’t understand what the code is supposed to do. You’re just translating syntax. If you end up porting a bunch of one-line conditional-and-statements, and then go back over to try and shore up your variable declarations with extra inner scopes, you may create that above example and have no idea. Woe be the developer that needs to work with this code later.
This is something I personally experienced while porting a large amount of C# code that I didn’t understand into C. To help set up the perfect storm, the C# code was written with Allman-style braces, except for single-statements which were mostly compressed into a single line. The syntax was ported faithfully and in some cases extra scopes were injected to prevent relocating large numbers of variable declarations. It wasn’t until I wasted a bunch of time trying to figure out why a break point wasn’t being hit that I noticed the deceptive syntax.
The only good news is that your code is still correct, unlike a certain JavaScript brace style issue. But it can certainly ruin a few hours of your day if you need to debug and your assumptions of control flow are incorrect.
This especially becomes true in C++, where the creation (and destruction) of variables can have semantic side effects, so you might actually need those scopes:
// this code is unsynchronized
{
std::unique_lock lock(some_mutex); // constructing the lock will also lock the mutex
// do something synchronized with other threads
} // at this point the lock is destructed and will unlock the mutex in the process
// this code is unsynchronized again
dkcfm9
5t4l99
8ii9fu
g69nxw
it3ier