Main Content

CERT C: Rec. EXP02-C

Be aware of the short-circuit behavior of the logical AND and OR operators

Since R2026a

Description

Be aware of the short-circuit behavior of the logical AND and OR operators1

Polyspace Implementation

The rule checker checks for the issue Right hand operand of a logical && or || operator contains persistent side effects.

Examples

expand all

Issue

This issue occurs when the right side of a logical || or && operator has persistent side effects. For instance, if the right side contains a function call and the function modifies a global variable, Polyspace® reports a violation.

Polyspace does not report a violation if the right side contains a call to a pure function, that is, a function without side effects. The checker considers a function as pure if the function performs only simple operations such as:

  • Reading a nonvolatile parameter or global variable

  • Writing to a local variable

In addition to simple operations, if the function contains a call to another function, Polyspace attempts to determine if the callee is a pure function. If Polyspace determines that the callee is a pure function, then the calling function is considered a pure function (as long as the other operations in the calling function are simple operations).

Polyspace does not consider a function as pure if the function does one of the following:

  • Writes to a global variable or the dereference of a parameter

  • Reads or writes to a volatile variable, or contains an asm block

To determine if a function is pure, Polyspace searches for the function definition within the same translation unit as the function call (a translation unit is a source file plus all headers included in the source). If a function definition is not found in the current translation unit, the checker does not report a violation of this rule. The checker also does not analyze functions called through function pointers.

If the right operand of the logical || or && operator invokes a function by using a function pointer, Polyspace cannot determine whether the invoked function has side effects. Polyspace does not report a violation in this case.

Risk

The right operand of the || operator is not evaluated if the left operand is true. The right operand of the && operator is not evaluated if the left operand is false. In these cases, if the right operand modifies the value of a variable, the modification does not take place.

Fix

Avoid relying on side effects in logical operations.

Example — Short-circuit behavior of logical AND operator

In this example, the operand (accounts[index].role = ROLE_USER) is the right operand of a logical operation and modifies an array. If the left operand --index evaluates to false, the right operand is not evaluated and the assignment does not happen, which can lead to an unexpected result. Polyspace reports a violation.

#include <stdio.h>
#include <stdlib.h>

#define ROLE_ADMIN 0
#define ROLE_USER 1

typedef struct {
    int role;
    int identifier;
} account_t;

account_t *Create_User_Accounts(int total_accounts) {
    account_t *accounts = (account_t *)calloc(total_accounts, sizeof(account_t));
    int index = total_accounts;
    while (--index && (accounts[index].role = ROLE_USER)) {  // Noncompliant
        accounts[index].identifier = index;
    }
    return accounts;
}

int main() {
    account_t *sample;
    int j;
    sample = Create_User_Accounts(25);
    for (j = 0; j < 25; j++)
        printf("Account %d has role level %d\n", sample[j].identifier, sample[j].role);
    free(sample);
    return 0;
}

Correction — Remove Reliance on Side Effects

To fix this violation, refactor your code to remove the assignment operation from the right side of the logical && operator. One possible solution is to change the while loop condition to a pure comparison and move all assignments within the body of the loop.

#include <stdio.h>
#include <stdlib.h>

#define ROLE_ADMIN 0
#define ROLE_USER 1

typedef struct {
    int role;
    int identifier;
} account_t;

account_t *Create_User_Accounts(int total_accounts) {
    account_t *accounts = (account_t *)calloc(total_accounts, sizeof(account_t));
    int index = total_accounts;
    while (index!=0) {  // Compliant
        accounts[index].role = ROLE_USER;
        accounts[index].identifier = index;
        index--;
    }
    return accounts;
}

int main() {
    account_t *sample;
    int j;
    sample = Create_User_Accounts(25);
    for (j = 0; j < 25; j++)
        printf("Account %d has role level %d\n", sample[j].identifier, sample[j].role);
    free(sample);
    return 0;
}

Check Information

Group: Rec. 03. Expressions (EXP)
PQL Name: std.cert.EXP02_C

Version History

Introduced in R2026a


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.