Skip to content

C Logical Operators

Updated 7 min read Aimerscrew

Real decisions rarely depend on one thing. You can log in if your password is right and your account isn't locked. You get free shipping if you're a member or your cart is over $100. C logical operators let you combine several conditions into one answer. In this lesson, you'll learn &&, || and !, their truth tables, and a clever behavior called short-circuit evaluation.

🌍 Real-life example

A theme park ride has a sign: "Riders must be at least 120 cm tall and 10 years old, or ride with an adult." The staff member checks several yes/no facts and combines them into one final yes or no. That's exactly what logical operators do with the 1s and 0s from comparison operators.

What are logical operators in C?

A logical operator works on true/false values and produces a true/false result. C has three:

OperatorNameTrue when...Example
&&ANDboth sides are trueage >= 18 && has_id
||ORat least one side is trueis_member || total >= 100
!NOTthe value is false!is_locked

Like comparisons, logical operators always produce an int: 1 for true and 0 for false.

#include <stdio.h>

int main(void) {
    int has_ticket = 1;
    int has_id = 0;

    printf("has_ticket && has_id = %d\n", has_ticket && has_id);
    printf("has_ticket || has_id = %d\n", has_ticket || has_id);
    printf("!has_id = %d\n", !has_id);

    return 0;
}
has_ticket && has_id = 0
has_ticket || has_id = 1
!has_id = 1

Truth tables for &&, || and !

A truth table lists every possible input and the result. Since each side can only be true (1) or false (0), there are just four cases for && and ||, and two for !.

Truth tables. a && b is 1 only when a and b are both 1. a || b is 0 only when a and b are both 0. !a turns 0 into 1 and 1 into 0
AND needs both, OR needs at least one, NOT flips the value.

An easy way to remember: && is strict (one false ruins it), || is generous (one true is enough).

Using && (AND): all conditions must be true

A login check is a classic AND: every requirement must pass.

#include <stdio.h>

int main(void) {
    int username_ok = 1;
    int password_ok = 1;
    int account_locked = 0;

    int can_log_in = username_ok && password_ok && !account_locked;
    printf("Can log in: %d\n", can_log_in);

    password_ok = 0;    /* wrong password this time */
    can_log_in = username_ok && password_ok && !account_locked;
    printf("Can log in: %d\n", can_log_in);

    return 0;
}
Can log in: 1
Can log in: 0

AND is also how you check that a number is inside a range. C has no 13 <= age <= 19 shortcut; you write two comparisons joined by &&.

Using || (OR): at least one condition must be true

#include <stdio.h>

int main(void) {
    int is_member = 0;
    double cart_total = 120.00;
    int free_shipping = is_member || cart_total >= 100.0;
    printf("Free shipping: %d\n", free_shipping);

    int age = 15;
    int is_teen = age >= 13 && age <= 19;
    printf("Teen: %d\n", is_teen);

    int temperature = 38;
    int extreme_weather = temperature < 0 || temperature > 35;
    printf("Extreme weather: %d\n", extreme_weather);

    return 0;
}
Free shipping: 1
Teen: 1
Extreme weather: 1

The shopper isn't a member, but the cart is over $100, so one true side is enough. Notice the pattern: "inside a range" uses &&, "outside a range" uses ||.

Using ! (NOT): flipping a condition

! works on a single value and reverses it. It's great for readable names: !account_locked reads as "account not locked". Put it right before the value, with no space needed: !is_open.

In C, any non-zero value is true

Logical operators don't require their inputs to be exactly 1 or 0. C treats 0 as false and every other value as true, including negative numbers. The result, however, is always a clean 1 or 0.

#include <stdio.h>

int main(void) {
    int a = 5;
    int b = -3;
    int c = 0;

    printf("a && b = %d\n", a && b);
    printf("a && c = %d\n", a && c);
    printf("!a = %d\n", !a);
    printf("!c = %d\n", !c);
    printf("!!a = %d\n", !!a);

    return 0;
}
a && b = 1
a && c = 0
!a = 0
!c = 1
!!a = 1

The double NOT !!a is a neat trick: it converts any value into exactly 1 (if non-zero) or 0.

What is short-circuit evaluation?

C evaluates && and || from left to right and stops as soon as the answer is known. This is called short-circuit evaluation:

  • For A && B: if A is false, the result must be false, so B is never evaluated.
  • For A || B: if A is true, the result must be true, so B is never evaluated.

This isn't just a speed trick. It lets you put a safety check first and the risky part second:

#include <stdio.h>

int main(void) {
    int orders = 0;
    int revenue = 500;

    int big_average = orders != 0 && revenue / orders > 100;
    printf("Big average order: %d\n", big_average);

    return 0;
}
Big average order: 0

Because orders != 0 is false, C never runs revenue / orders, so there's no division by zero. If you swapped the two sides, the program could crash.

💡 Tip

Put the cheapest or the "guard" condition on the left. You'll use this pattern a lot later, for example checking that an index is inside an array before reading from it in C arrays.

Combining && and || with parentheses

! binds tightest, then &&, then ||. So a || b && c means a || (b && c). That's easy to misread, and GCC even warns "suggest parentheses around '&&' within '||'". Always add parentheses when you mix them:

int can_ride = height_cm >= 120 && (age >= 10 || with_adult);

The full ranking of all operators is in the operator precedence lesson.

Common mistakes with logical operators

1. Using & instead of &&

A single & is the bitwise AND, a different operator (covered in bitwise operators). It can give a different answer:

#include <stdio.h>

int main(void) {
    int a = 1;
    int b = 2;

    printf("a && b = %d\n", a && b);
    printf("a & b = %d\n", a & b);

    return 0;
}
a && b = 1
a & b = 0

Both 1 and 2 are "true", so && gives 1. But bitwise & compares individual bits, and 1 and 2 share none, so it gives 0. The same goes for | versus ||.

2. Writing x == 1 || 2

int is_small = x == 1 || 2;   /* always 1! */

This reads as (x == 1) || 2, and 2 is non-zero, so the result is always true. Repeat the variable: x == 1 || x == 2.

3. Expecting both sides to always run

Because of short-circuiting, the right side of && or || may be skipped. Don't put something there that must happen every time, like updating a counter.

4. Writing !x == 5

! applies first, so this means (!x) == 5, which compares 0 or 1 with 5 and is always false. You probably meant x != 5 or !(x == 5). GCC warns about this with "logical not is only applied to the left hand side of comparison".

Practice exercise

🏋️ Try it yourself

Write the theme park rule from the start of this lesson: a rider may ride if they are at least 120 cm tall and either at least 10 years old or riding with an adult. Test it with a rider who is 125 cm tall, 8 years old, and with an adult.

Show solution
#include <stdio.h>

int main(void) {
    int height_cm = 125;
    int age = 8;
    int with_adult = 1;

    int can_ride = height_cm >= 120 && (age >= 10 || with_adult);
    printf("Can ride: %d\n", can_ride);

    return 0;
}
Can ride: 1

Height passes (1). Inside the parentheses, age fails (0) but with_adult is 1, so the OR gives 1. Then 1 && 1 is 1.

What's next?

Next, meet two tiny but very popular operators that add or subtract 1 in a single step: C increment and decrement operators.

Key takeaways

  • C has three logical operators: && (AND), || (OR) and ! (NOT).
  • && is true only if both sides are true; || is true if at least one side is true; ! flips the value.
  • C treats 0 as false and any non-zero value as true, but logical operators always return 1 or 0.
  • Short-circuit evaluation: && skips the right side when the left is false, || skips it when the left is true.
  • Put guard checks first, such as orders != 0 && revenue / orders > 100.
  • Use parentheses when mixing && and ||, and do not confuse && with the bitwise &.

Frequently asked questions

What is the difference between & and && in C?

&& is the logical AND: it treats each side as true or false and returns 1 or 0. & is the bitwise AND: it compares the individual bits of two integers. 1 && 2 is 1, but 1 & 2 is 0.

What is short-circuit evaluation in C?

C stops evaluating && and || as soon as the result is known. In A && B, if A is false, B is never evaluated. In A || B, if A is true, B is never evaluated.

What values are true in C?

Zero is false and every non-zero value is true, including negative numbers. Comparison and logical operators always produce exactly 1 for true and 0 for false.

Which has higher precedence in C, && or ||?

&& has higher precedence than ||, so a || b && c means a || (b && c). Add parentheses when mixing them to make your intent clear.

How do I check if a number is between two values in C?

Combine two comparisons with &&, for example age >= 13 && age <= 19. C does not support the math-style chain 13 <= age <= 19.