V2661. MISRA. A 'for' loop should be well-formed.
This diagnostic rule is based on the MISRA (Motor Industry Software Reliability Association) software development guidelines.
This diagnostic rule is relevant only for C.
A for
loop should be formatted according to the following rules:
The first loop statement (Init-statement
) should:
- either be empty;
- or have no persistent side effects;
- or define and initialize the loop counter.
The second loop statement (Condition
) should:
- have no persistent side effects;
- use the loop counter, either alone or with a loop control flag;
- not use objects that are modified within the loop body.
The third loop statement (Expression
) should:
- have no persistent side effects except for the loop counter modification;
- not use objects that are modified within the loop body.
Adhering to these for
loop formatting rules reduces the likelihood of errors and simplifies the processes of debugging and code review. This approach allows the entire loop logic to be immediately visible in its header, eliminating unpredictable behavior.
Note. Using a loop with three empty statements for( ; ; )
is permissible.
The example of incorrect code:
for (size_t i = 0;
i < users_size && retry_count < MAX_RETRIES; // <=
i += (retry_count > 0 ? 0 : 1)) // <=
{
if (!checkUser(users[i]))
{
retry_count += 1; // <=
}
else
{
retry_count = 0; // <=
}
// ....
}
The danger of this code lies in the non-obvious hanging of the loop on a problematic user. If the checkUser
function consistently returns false
while processing a specific user, the loop becomes stuck at one position until all retry attempts are exhausted. This causes all subsequent users to be skipped, which can be difficult to detect during a code review.
In this case, correct formatting prohibits the use of retry_count
in the second and third statements of the for
loop, since this variable is modified within the loop body. To fix the issue, the validation mechanism can be extracted into a separate function. This ensures the loop operates predictably and processes all users.
The corrected code:
bool checkUserWithRetry(struct User user) {
for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {
if (checkUser(user)) return true;
}
return false;
}
// ....
for (size_t i = 0; i < users_size; i++) {
if (!checkUserWithRetry(users[i])) {
logError("Failed to process user: ", users[i]);
}
}
// ....