Logical Operators in C: Types, Examples, and Best Practices

Logical Operators in C Types, Examples, and Best Practices

Logical operators in C are used to combine or reverse conditions and are essential when a program needs to make decisions based on more than one expression. They are widely used in if, while, for, and other conditional statements.

In C programming, there are three logical operators:

In C programming, there are three logical operators:

OperatorNamePurpose
&&Logical ANDTrue only when both conditions are true
||Logical ORTrue if at least one condition is true
!Logical NOTReverses the logical result

These operators become especially important in embedded C, where a controller may need to check several conditions before enabling a motor, reading a sensor, transmitting data, or entering a safety state.

Logical operators in C are operators used to evaluate multiple conditions and produce a logical result.

The three logical operators are:

&&    // Logical AND

||    // Logical OR

!     // Logical NOT

For logical AND, both operands must be nonzero. For logical OR, at least one operand must be nonzero. Logical NOT produces 1 when its operand is zero and 0 when its operand is nonzero. The && and || operators also use short-circuit evaluation, meaning the second operand may not be evaluated when the result is already known.

Table of Contents

What Are Logical Operators in C?

Logical operators in C combine or invert conditions and return a single true/false result. C doesn’t have separate “true” and “false” values by default — it uses numbers instead. Any nonzero value counts as true; zero is false. Whatever a logical operator evaluates to comes back as an int: exactly 1 for true, exactly 0 for false. Nothing else.

That last part catches people who’ve coded in Python or JavaScript first. In those languages, 5 && 3 can hand you back 3. In C, it hands you back 1, every time.

C has exactly three logical operators:

OperatorNameWhat It Does
&&Logical ANDTrue only if both conditions are true
||Logical ORTrue if at least one condition is true
!Logical NOTReverses true to false and false to true

They’re easy to mix up with two other operator families:

  • Relational operators (==, !=, <, >) compare two values against each other.
  • Bitwise operators (&, |, ^) work bit-by-bit on a number’s binary representation.

That last mix-up — bitwise vs. logical — causes more bugs than almost anything else on this page. We’ll get to exactly why further down.

registor_now_P

What Is the Role of Logical Operators in C?

Logical operators help a program make decisions using conditions.

Consider an embedded temperature controller. The heater should turn on only when:

  • Temperature is below the required limit.
  • The system is enabled.

Instead of using multiple nested statements, the conditions can be combined:

 
c
if (temperature < 25 && system_enabled)
{
    heater_on();
}

This makes the decision easier to read and keeps related conditions together.

Logical operators are commonly used for:

  • Checking multiple conditions
  • Controlling program flow
  • Validating input
  • Monitoring sensor values
  • Implementing safety conditions
  • Controlling peripherals
  • Checking communication status
  • Building firmware state conditions

Types of Logical Operators in C

C provides three logical operators: &&, ||, and !.

1. Logical AND (&&)

The logical AND operator returns 1 only when both operands are nonzero. Otherwise, it returns 0.

Syntax

 
c
condition1 && condition2

Example

 
c
#include 

int main(void)
{
    int age = 25;
    int has_id = 1;

    if (age >= 18 && has_id)
    {
        printf("Access granted");
    }

    return 0;
}

Here, both conditions must be satisfied:

 
age >= 18     → true
has_id        → true

true && true  → true

Embedded C Example

Suppose a motor should start only when the temperature is safe and the start command is active:

 
c
if (temperature < 80 && start_command)
{
    motor_start();
}

The motor will start only when both conditions are satisfied.

2. Logical OR (||)

The logical OR operator returns 1 when either operand or both operands are nonzero. It returns 0 only when both operands are zero.

Syntax

 
c
condition1 || condition2

Example

 
c
#include 

int main(void)
{
    int button = 0;
    int remote_signal = 1;

    if (button || remote_signal)
    {
        printf("Device activated");
    }

    return 0;
}

The condition is true because remote_signal is nonzero.

Embedded C Example

A warning can be generated when either a temperature limit or voltage limit is exceeded:

 
c
if (temperature > 100 || voltage > 5)
{
    trigger_alarm();
}

Only one condition needs to become true for the alarm to activate.

3. Logical NOT (!)

The logical NOT operator reverses a logical condition.

  • If the operand is zero, ! produces 1.
  • If the operand is nonzero, ! produces 0.

Syntax

 
c
!condition

Example

 
c
int system_ready = 0;

if (!system_ready)
{
    printf("System is not ready");
}

Since system_ready is 0:

 
!0 → 1

The condition therefore becomes true.

Embedded C Example

A controller can check whether a sensor is not ready:

 
c
if (!sensor_ready)
{
    initialize_sensor();
}

This is often clearer than writing:

 
c
if (sensor_ready == 0)

Both approaches can be valid, but !sensor_ready directly expresses the logical meaning.

Truth Table for Logical Operators in C

Understanding the truth table makes the behavior of logical operators easier to predict.

AND Truth Table

ABA && B
000
010
100
111

OR Truth Table

ABA || B
000
011
101
111

NOT Truth Table

A!A
01
10

In C, these rules also apply when the operands are values other than 0 and 1. Any nonzero scalar value is treated as logically true, while zero is treated as false. The logical operators produce an int result of 0 or 1.

Examples of Logical Operators in C

Example 1: Checking a Valid Range

 
c
int temperature = 50;

if (temperature >= 20 && temperature <= 80)
{
    printf("Temperature is within range");
}

The expression checks both the lower and upper limits.

Example 2: Checking Multiple Inputs

 
c
int password_correct = 1;
int user_active = 1;

if (password_correct && user_active)
{
    printf("Login successful");
}

The login succeeds only when both conditions are true.

Example 3: Triggering an Alarm

 
c
int high_temperature = 0;
int low_battery = 1;

if (high_temperature || low_battery)
{
    printf("Warning!");
}

The warning is generated because at least one condition is true.

Example 4: Checking a Sensor Pointer Safely

Logical AND is frequently useful when checking a pointer before dereferencing it:

 
c
if (sensor != NULL && sensor->status == READY)
{
    read_sensor(sensor);
}

Because && evaluates the right-hand operand only when the left-hand operand does not compare equal to zero, the member access is not attempted when sensor is NULL.

This short-circuit behavior is particularly useful when writing defensive C code.

Example 5: Combining Conditions with NOT

 
c
if (!error && system_enabled)
{
    start_operation();
}

The operation starts when there is no error and the system is enabled.

Short-Circuit Evaluation in C

One of the most important characteristics of logical operators in C is short-circuit evaluation.

For logical AND:

 
c
A && B

If A is false, C does not evaluate B because the complete expression cannot become true.

For logical OR:

 
c
A || B

If A is true, C does not evaluate B because the complete expression is already true.

Example: Avoiding a Divide-by-Zero Error

 
c
int x = 0;

if (x != 0 && 100 / x > 5)
{
    printf("Result is valid");
}

Here, x != 0 is false, so:

 
c
100 / x > 5

is not evaluated.

This is important because evaluating 100 / x when x is zero would create an invalid division operation.

Example: Checking a Device Pointer

 
c
if (device != NULL && device->enabled)
{
    start_device(device);
}

The second condition is checked only when device != NULL.

Logical Operators vs Bitwise Operators

A common source of confusion in C is the difference between logical and bitwise operators.

LogicalBitwise
&&&
|||
!~
Works with logical conditionsWorks on individual bits
Produces a logical 0 or 1 resultProduces a value based on bit operations

For example:

 
c
int a = 5;
int b = 3;

if (a && b)
{
    printf("Logical AND");
}

Here, both 5 and 3 are nonzero, so the logical result is true.

By contrast:

 
c
int result = a & b;

performs a bitwise AND operation on the binary representation of the values.

This distinction is particularly important in embedded C because bitwise operators are commonly used for register manipulation, while logical operators are commonly used for decision-making.

Explore Courses - Learn More

Logical Operator Precedence in C

When several operators appear in one expression, C uses operator precedence to determine how the expression is grouped.

Among the logical operators:

 
!     higher precedence
&&
||

Therefore:

 
c
A || B && C

is interpreted as:

 
c
A || (B && C)

not:

 
c
(A || B) && C

C’s operator precedence places logical NOT above logical AND, and logical AND above logical OR.

Use Parentheses for Clarity

Even when you know the precedence rules, parentheses often make the intended logic easier to understand:

 
c
if ((temperature > 20 && temperature < 80) || emergency_mode)
{
    enable_system();
}

This is especially valuable in firmware, where incorrect condition grouping can lead to unexpected device behavior.

Importance of Logical Operators in C

Logical operators are fundamental to decision-making in C programs.

1. Combining Conditions

Instead of writing several separate if statements, related conditions can be combined:

 
c
if (voltage >= 3.0 && voltage <= 3.6)
{
    battery_ok = 1;
}

2. Implementing Safety Conditions

Embedded systems frequently depend on multiple safety checks:

 
c
if (temperature_ok && pressure_ok && door_closed)
{
    enable_motor();
}

3. Simplifying Program Logic

Logical operators make conditions more compact without necessarily making them harder to understand.

4. Working with Sensors and Peripherals

Firmware often needs to check several status flags before performing an operation:

 
c
if (uart_ready && data_available)
{
    read_uart();
}

5. Controlling Program Flow

They are used throughout:

  • if
  • while
  • for

and other expressions where conditional logic is required.

Best Practices Using Logical Operators in C

Writing the expression correctly is only part of the job. Good logical expressions should also be easy to review and maintain.

Keep Complex Conditions Readable

Avoid packing too many checks into a single line:

 
c
if (a && b && c && d && e && f)

When logic becomes difficult to review, separate it into meaningful variables:

 
c
int safe_to_start = temperature_ok &&
                    pressure_ok &&
                    power_ok;

if (safe_to_start)
{
    start_system();
}

Use Parentheses When the Logic Is Important

Even when precedence is known, parentheses can make the intended grouping obvious:

 
c
if ((sensor_ok && signal_valid) || manual_override)
{
    process_data();
}

This reduces the chance of misunderstandings during maintenance or code review.

Take Advantage of Short-Circuit Evaluation

Place a quick or safety-related condition first when appropriate:

 
c
if (pointer != NULL && pointer->value > 10)
{
    process(pointer);
}

This can prevent the second expression from being evaluated when it is unnecessary or unsafe.

Be Careful with Side Effects

Avoid relying on complicated side effects inside logical expressions:

 
c
if (x++ && y++)
{
    /* ... */
}

Although such code may be valid in some situations, it can make program behavior harder to understand because short-circuit evaluation determines whether the second operand executes.

For maintainable embedded firmware, explicit statements are often clearer when an expression changes program state.

Do Not Confuse && with &

Remember:

 
c
&&    // logical AND
&     // bitwise AND

Likewise:

 
c
||    // logical OR
|     // bitwise OR

Using the wrong operator can completely change the result of an expression.

Common Mistakes to Avoid

Using & Instead of &&

Incorrect:

 
c
if (temperature > 20 & temperature < 80)

For logical conditions, the intended operator is normally:

 
c
if (temperature > 20 && temperature < 80)

Using | Instead of ||

Incorrect:

 
c
if (button_pressed | sensor_active)

Logical OR should normally be:

 
c
if (button_pressed || sensor_active)

Forgetting Operator Precedence

Consider:

 
c
if (a || b && c)

This is interpreted as:

 
c
if (a || (b && c))

When the intended logic is different, use parentheses explicitly.

Assuming Only 1 Means True

In C, logical tests do not require a value to be exactly 1.

For example:

 
c
int value = 25;

if (value)
{
    printf("True");
}

The condition is true because 25 is nonzero. In logical expressions, scalar zero is false and nonzero is true.

Overcomplicating Simple Conditions

Instead of:

 
c
if ((status == 0) == 1)
{
    /* ... */
}

a much clearer expression is:

 
c
if (status == 0)
{
    /* ... */
}

Readable logic is particularly important in embedded software, where conditions may directly control physical hardware.

Logical Operators in Embedded C

Logical operators have a direct role in embedded firmware.

Consider a microcontroller controlling a cooling fan:

 
c
if (temperature > 70 && system_active)
{
    fan_on();
}

The fan operates only when the temperature exceeds the threshold and the system is active.

Another example is a fault condition:

 
c
if (over_voltage || over_temperature || sensor_fault)
{
    shutdown_system();
}

Any one of the three fault conditions can initiate the shutdown.

A more complete firmware decision might look like:

 
c
if (power_ok &&
    sensor_ok &&
    communication_ready &&
    !critical_fault)
{
    start_operation();
}

This type of condition is common in control-oriented firmware because several system states may need to be valid before an operation is permitted.

Logical Operators and stdbool.h

C also provides the Boolean type through:

 
c
#include 

This allows code such as:

 
c
#include 

bool sensor_ok = true;
bool system_enabled = true;

if (sensor_ok && system_enabled)
{
    start_system();
}

Using bool, true, and false can make application-level logic more expressive.

However, the built-in logical operators themselves work with scalar operands, and their result in C has type int with value 0 or 1.

A Practical Example: Sensor Monitoring System

Consider a simple embedded monitoring system.

The system should activate an alarm when either:

  • The temperature becomes too high.
  • The battery voltage becomes too low.
 
c
#include 

int main(void)
{
    int temperature = 85;
    int battery_voltage = 3;

    if (temperature > 80 || battery_voltage < 3)
    {
        printf("Alarm activated");
    }

    return 0;
}

Here:

 
temperature > 80
       OR
battery_voltage < 3

Because the temperature condition is true, the complete expression is true.

Now consider a motor:

 
c
if (temperature < 80 && battery_voltage >= 3 &&
    system_enabled)
{
    motor_start();
}
else
{
    motor_stop();
}

The motor starts only when every required condition is satisfied.

This demonstrates why logical operators are so important in embedded programming: they allow software to translate multiple hardware and system states into a single decision.

Logical Operators in C: Key Takeaways

The most important points to remember are:

  • C has three logical operators: &&, ||, and !.
  • && requires both conditions to be true.
  • || requires at least one condition to be true.
  • ! reverses a logical result.
  • Zero represents false; a nonzero scalar value represents true in logical testing.
  • The logical operators produce 0 or 1.
  • && and || use short-circuit evaluation.
  • ! has higher precedence than &&, and && has higher precedence than ||.
  • Logical operators are different from bitwise operators.
  • Parentheses improve clarity when conditions become complex.
  • Logical operators are heavily used in embedded systems for sensor checks, safety logic, peripheral control, and state validation.

Talk to Academic Advisor

Final Thought

Logical operators in C may look simple, but they form the foundation of much of the decision-making found in real software. From validating a condition in a small C program to deciding whether an embedded controller should start a motor, transmit data, or enter a safe state, &&, ||, and ! turn individual conditions into meaningful program logic.

The most effective approach is not simply to know what each operator does, but to understand when conditions are evaluated, how operators are grouped, and how the same logic behaves in real firmware.

FAQs

&& is the logical AND operator. It returns 1 when both operands are nonzero; otherwise, it returns 0.

Short-circuit evaluation means C may skip evaluating the second operand of && or || when the final result is already determined.

They allow firmware to combine multiple conditions such as sensor status, voltage levels, system states, communication readiness, and fault conditions before controlling hardware.

Yes. In a logical expression, zero is treated as false and any nonzero scalar value is treated as true.

! is the logical NOT operator. It changes a zero value to 1 and a nonzero value to 0.

Author

Embedded Systems trainer – IIES

Updated On: 03-09-26


10+ years of hands-on experience delivering practical training in Embedded Systems and it's design