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:
| Operator | Name | True when... | Example |
|---|---|---|---|
&& | AND | both sides are true | age >= 18 && has_id |
|| | OR | at least one side is true | is_member || total >= 100 |
! | NOT | the 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 !.
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: ifAis false, the result must be false, soBis never evaluated. - For
A || B: ifAis true, the result must be true, soBis 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.