I am programming a project in C and have a problem: I've got a lot of if
conditions and it might get really hard for other people to read. I haven't yet found a similar question on the internet.
Do you have an idea or example how to make my code more readable?
Here is the C Code:
if( ((g_cycle_cnt == uartTxSecondaryMsg[3][msgPos[3]].sliceNo) || //correct slicenumber...
(uartTxSecondaryMsg[3][msgPos[3]].sliceNo == -1) || // or as fast as possible...
( (uartTxSecondaryMsg[3][msgPos[3]].sliceNo == -2) &&
((uartTxSecondaryMsg[3][msgPos[3]].timeFrameBegin>=g_uptime_cnt) &&
(uartTxSecondaryMsg[3][msgPos[3]].timeFrameEnd<=g_uptime_cnt)))) &&
((dataProcessingFlag & SECONDARY_MSG_ANNOUNCED_CH4) == SECONDARY_MSG_ANNOUNCED_CH4) )
Nested if statements mean an if statement inside another if statement. Yes, both C and C++ allow us to nested if statements within if statements, i.e, we can place an if statement inside another if statement.
Long if statements may be split onto several lines when the character/line limit would be exceeded. The conditions have to be positioned onto the following line, and indented 4 characters. The logical operators ( && , || , etc.)
Make 4 space indent, make sure no use tabs . Always use curly braces, even if the body is only one sentence. The if , else and if else keywords belong on separate lines by itself, no curly.
The if-else-if statement is used to execute one code from multiple conditions. It is also called a multipath decision statement.
Create functions that have indicative names that check the requirements and represent their meaning e.g:
if( is_correct_slice_number(/*... params here ... */) ||
is_asap(/*... params here ... */) ||
is_other_condition(/*... params here ... */))
Or as suggested macros that follow the same logic e.g:
if( IS_CORRECT_SLICE_NUMBER(/*... params here ... */) ||
IS_ASAP(/*... params here ... */) ||
IS_OTHER_CONDITION(/*... params here ... */))
I think that this might make your intentions clearer.
If you want to stick with your existing code (as opposed to factor things out into inline functions), and just modify the indentation, I'm a big fan of using indent
consistently. This means you can fix any source file.
With its default options it gives you GNU indentation, i.e.:
if (((g_cycle_cnt == uartTxSecondaryMsg[3][msgPos[3]].sliceNo) || //correct slicenumber...
(uartTxSecondaryMsg[3][msgPos[3]].sliceNo == -1) || // or as fast as possible...
((uartTxSecondaryMsg[3][msgPos[3]].sliceNo == -2) &&
((uartTxSecondaryMsg[3][msgPos[3]].timeFrameBegin >= g_uptime_cnt) &&
(uartTxSecondaryMsg[3][msgPos[3]].timeFrameEnd <= g_uptime_cnt)))) &&
((dataProcessingFlag & SECONDARY_MSG_ANNOUNCED_CH4) ==
SECONDARY_MSG_ANNOUNCED_CH4))
{
/* do something */
}
I'd say that the problem here is in fact that you are poking about illegibly in arrays. At the very least, factor out:
uartTxSecondaryMsg[3][msgPos[3]]
into a separate variable, e.g.:
whatever *foo = &uartTxSecondaryMsg[3][msgPos[3]];
if (((g_cycle_cnt == foo->sliceNo) || //correct slicenumber...
(foo->sliceNo == -1) || // or as fast as possible...
((foo->sliceNo == -2) &&
((foo->timeFrameBegin >= g_uptime_cnt) &&
(foo->timeFrameEnd <= g_uptime_cnt)))) &&
((dataProcessingFlag & SECONDARY_MSG_ANNOUNCED_CH4) ==
SECONDARY_MSG_ANNOUNCED_CH4))
{
/* do something */
}
Obviously choose an appropriate type and variable name for foo
.
You could then separate out the limbs of the if
statement into separate functions each taking foo
as a parameter.
[this is very subjective]
switch
, or using early return
s, or even goto
First step (cleanup and alignment):
if ( ( uartTxSecondaryMsg[3][msgPos[3]].sliceNo == g_cycle_cnt //correct slicenumber...
|| uartTxSecondaryMsg[3][msgPos[3]].sliceNo == -1 // or as fast as possible...
||(uartTxSecondaryMsg[3][msgPos[3]].sliceNo == -2
&& uartTxSecondaryMsg[3][msgPos[3]].timeFrameBegin >= g_uptime_cnt
&& uartTxSecondaryMsg[3][msgPos[3]].timeFrameEnd <= g_uptime_cnt
&& (dataProcessingFlag & SECONDARY_MSG_ANNOUNCED_CH4) == SECONDARY_MSG_ANNOUNCED_CH4 ) )
{
// do something useful here
}
Second step, using switch
(and goto
!) [this could be a bit too much for some readers ...]
switch (uartTxSecondaryMsg[3][msgPos[3]].sliceNo) {
default:
if (uartTxSecondaryMsg[3][msgPos[3]].sliceNo == g_cycle_cnt) goto process;
break;
case -2:
if (uartTxSecondaryMsg[3][msgPos[3]].timeFrameBegin < g_uptime_cnt) break;
if (uartTxSecondaryMsg[3][msgPos[3]].timeFrameEnd > g_uptime_cnt) break;
if ((dataProcessingFlag & SECONDARY_MSG_ANNOUNCED_CH4) != SECONDARY_MSG_ANNOUNCED_CH4) break;
case -1:
process:
// do something useful here
}
The advantage of the switch()
construct is that it immediately clear that uartTxSecondaryMsg[3][msgPos[3]].sliceNo
is the master condition. Another advantage is that cases and conditions can be added without having to interfere with the other cases or to struggle with the parentheses.
Now I hope I got the parentheses-removal right...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With