Logical Expressions in C
Logical Expressions, also known as Boolean Expressions, are expressions that can take a true or false value.
These expressions can be built using several types of operators: relational operators, equality operators, and logical operators.
In this lesson, we will study how to create and combine logical expressions in C using operators, what the precedence rules are, and how to combine logical and mathematical expressions.
- C represents boolean values as integers:
1
represents true,0
represents false; - Logical expressions return an integer result;
- Any non-zero value is considered true;
- To build a logical expression you can use relational, equality, and logical operators.
Boolean Values and Logical Expressions
In the C programming language, it is possible to write expressions whose result can be either true or false, i.e., boolean values. These expressions are called Logical Expressions or Boolean Expressions.
In many programming languages, there is a specific type to represent boolean values, capable of taking only two values. In this sense, the C language is different because it does not have a specific type.
In fact, C represents boolean values as integers. Specifically, any non-zero value is considered true.
Representation of Boolean Values in C
In the C language, boolean values are represented as integers. Specifically:
- Any value equal to zero is considered false;
- Any non-zero value is considered true, including negative integers.
The fact that the result of a logical expression is an integer means that a logical expression is in all respects a mathematical expression in C, and so we can combine them. For example, using the greater-than operator >
, which we will study in this lesson, we can write a line of code like:
x = (5 > 2) * 3;
In this example, the value 5 is greater than 2, so the result in parentheses is true. The C compiler will use the value 1
in place of the parentheses, so the variable x
will be assigned the value 3.
Logical Expressions in C
In the C language, logical expressions return an integer, so they are mathematical expressions in all respects and can be combined with them.
To build a logical expression in C, you can use three types of operators:
- Relational operators;
- Equality operators;
- Logical operators.
Relational Operators
The first logical operators we will study, used to build boolean expressions, are the relational operators. These are:
Operator | Mathematical Operator | Meaning |
---|---|---|
< |
"Less than" | |
> |
"Greater than" | |
<= |
"Less than or equal to" | |
>= |
"Greater than or equal to" |
These operators can be used to compare either integer values/variables or floating-point values/variables. The result of the comparison, as with all boolean expressions in C, produces an integer value: 0 if the expression is false, and 1 if the expression is true.
For this reason, an expression like:
10 < 20;
returns the value 1, whereas the following expression returns 0:
30 > 40;
As long as the operands are numeric types—integers and floating-point numbers—these operators can also be used in mixed cases. That is, you can compare integers and floating-point numbers. For example, the following expression returns 1:
5.23 < 10;
while this one returns 0:
40 > 21.2;
Regarding precedence, relational operators have lower precedence than arithmetic operators. Therefore, the following expression:
x + 1 < y * 2;
is equivalent to:
(x + 1) < (y * 2);
In any case, it is always good programming practice to use parentheses in such cases, even if they are redundant. This makes the code more readable and avoids mistakes when you don’t remember the exact operator precedence.
Finally, relational operators are left-associative. However, in the case of relational operators, this property is of limited practical use. To understand this better, let’s consider the following example:
x < y < z;
At first glance, it might seem that this boolean expression checks whether the variable y
is between x
and z
. But this is incorrect! In fact, the above expression is equivalent to:
(x < y) < z;
Therefore, the C compiler will first evaluate the expression x < y
. However, since this is a boolean expression, its result will be 0 or 1. So if x
is less than y
, the expression becomes:
1 < z;
If x
is greater than or equal to y
, the equivalent expression becomes:
0 < z;
In both cases, we end up with a completely different expression than expected.
In the C language, an expression like:
x < y < z
is legal, but it does not have the same meaning as it does in mathematics. Since relational operators are left-associative, the expression is equivalent to:
(x < y) < z
The result will be a comparison between the variable z
and the result 0
or 1
from the comparison between x
and y
.
To summarize:
Relational Operators in C
The relational operators in the C language allow you to relate, that is, compare, two expressions. The result is an integer equal to 0 if the result is false, and 1 if it is true.
-
The relational operators in C are:
< > <= >=
-
Relational operators are left-associative:
x < y < z
is equivalent to:
(x < y) < z
-
Relational operators have lower precedence than arithmetic operators:
x + y < z * w
is equivalent to:
(x + y) < (z * w)
Equality Operators
The equality operators in the C language are used to check whether two variables or values are equal or different from each other. Unlike their mathematical counterparts, however, equality operators have a particular appearance in C:
Operator | Mathematical Operator | Meaning |
---|---|---|
== |
"Equal to" | |
!= |
"Not equal to" |
In particular, the equality operator consists of two equal signs in a row ==
, while the inequality operator consists of an exclamation mark followed by an equal sign: !=
.
Because of these peculiarities, many beginners in the C language make the mistake of confusing the equality operator ==
with the assignment operator =
. As a result, they end up with expressions like x = 2
, which do not check whether x
equals 2 but rather assign the value 2 to the variable x
.
Be careful not to confuse the assignment operator with the equality operator. The two expressions below are not equivalent:
x == y; /* Compares x and y */
x = y; /* Assigns the value of y to x */
As with relational operators, the result is 1
if the expression is true and 0
if it is false. Thus, expressions like the following:
5 == 5;
4 != 5;
return the value 1
, while expressions like the following:
2 == 3;
4 != 4;
return the value 0
.
Equality operators are also left-associative and have lower precedence than arithmetic operators. However, they also have lower precedence than relational operators, so an expression like:
x < y == z > w;
is equivalent to:
(x < y) == (z > w);
To summarize:
Equality Operators in C
The equality operators in the C language allow you to check whether two expressions are equal, i.e., whether they return the same value, or not. The result is an integer equal to 0
if false, and 1
if true.
-
The equality operators in C are:
== !=
-
Equality operators are left-associative:
x == y != z
is equivalent to:
(x == y) != z
-
Equality operators have lower precedence than arithmetic operators and lower precedence than relational operators:
x + y < z * w == x * y > z + w
is equivalent to:
((x + y) < (z * w)) == ((x * y) > (z + w))
Logical Operators
In the C language, it is possible to construct more complex logical expressions starting from simpler ones. This is made possible by the use of Logical Operators:
Operator | Name | Meaning |
---|---|---|
! |
Not | Logical negation |
&& |
And | Logical conjunction |
|| |
Or | Logical inclusive disjunction |
The Not operator, !
, unlike the others, is a unary operator, meaning it takes only one operand. In general, when placed in front of a logical expression, it inverts its value:
!(expression)
The result of the expression above will be 0
if expression
equals 1
, and 1
otherwise.
For example, the following expression returns 1
:
!(5 > 10)
Whereas the following expression returns 0
:
!(2 == 2)
As for the And operator, &&
, it returns 1
if and only if both of its operands are 1
. Otherwise, it returns 0
.
For example, the following expression is 1
since both sub-expressions are true:
(3 > 2) && (5 < 10)
The next expression is false because the second operand is 0
. Therefore, the result will be 0
:
(3 > 2) && (10 < 2)
Finally, the Or operator, ||
, returns 1
if at least one of its operands is 1
; otherwise, it returns 0
. It is called an inclusive logical disjunction because the result is 1
even when both operands are 1
.
For example, the following expression is 1
because both operands are true:
(3 > 2) || (5 < 10)
This expression is also 1
because the first operand is true:
(3 > 2) || (10 < 2)
To summarize:
Logical Operators in C
The logical operators in the C language allow the combination of logical expressions and return 0
or 1
.
The logical operators are:
-
Not operator:
!
. It is a unary operator (takes one operand) and inverts the result of the logical expression to which it is applied:!(expression)
-
And operator:
&&
. Returns1
if both operands are1
. Returns0
otherwise:(expression_1) && (expression_2)
-
Or operator:
||
. Returns1
if at least one of the operands is1
. Returns0
otherwise:(expression_1) || (expression_2)
Precedence of Logical Operators
In the C language, the logical operators &&
and ||
have the lowest precedence among all operators. For example, the following expression:
x < y && z > w
is equivalent to:
(x < y) && (z > w)
Additionally, these two operators are left-associative. Thus, the following expression:
(x < y) && (z > 0) && (w < 0)
is equivalent to:
((x < y) && (z > 0)) && (w < 0)
The Not operator, on the other hand, has the same precedence as the unary minus.
Characteristics of And and Or Operators
The logical operators &&
and ||
have the following properties:
-
They have lower precedence than all other operators:
x < y && z > w
is equivalent to:
(x < y) && (z > w)
-
They are left-associative:
(x < y) && (z > 0) && (w < 0)
is equivalent to:
((x < y) && (z > 0)) && (w < 0)
Logical Operators and Mathematical Expressions
Logical operators work by combining logical expressions, i.e., expressions whose result is 0
or 1
. However, this is not a strict requirement. In fact, logical operators treat any value different from 0
as 1
.
Therefore, you can write an expression like:
(x * 2) && (y < 5)
In this case, if the first operand (x * 2)
has a value different from zero, it will be treated as 1
by the &&
operator.
This way, it is possible to combine mathematical expressions and logical expressions. For example, the following expression:
(x != 0) && (y != 0)
can be rewritten as:
x && y
Indeed, the expression x != 0
is equivalent to the simple expression x
since it is true if and only if x
is not equal to 0
.
Note that even negative values are considered true. For example, the following expression is true:
(-1) && 1
However, it is better to avoid writing logical expressions this way, as it may reduce code readability.
Mathematical Expressions and Logical Operators in C
In the C language, any value different from zero is considered true by logical operators, equivalent to 1
.
Zero is always considered false.
Short-Circuit Evaluation of Logical Operators
Another important characteristic of logical operators in the C language is that they evaluate their operands using short-circuit evaluation.
To understand what this means, let’s look at an example:
(x != 0) && (y > 0)
This is a logical And (&&
) between two expressions. This operator will return 1
, i.e., true, if and only if both expressions are true. However, if the first expression is false — that is, if x
is equal to zero — then the result can never be 1
regardless of the second expression.
In other words, even if the variable y
is greater than zero, the result would still be 0
, i.e., false.
For this reason, the C compiler takes advantage of this and generates code that effectively does not evaluate the second operand if the first operand is false. That is, the compiler skips checking whether the variable y
is positive when x
equals zero.
Using an electronic metaphor, it is said that the &&
operator has short-circuited the second operand.
The same thing applies to the Or operator ||
. In this case, if the first operand is 1
, evaluating the second operand is unnecessary.
Short-Circuit Evaluation of Logical Operators
In the C language, the binary logical operators &&
and ||
evaluate their operands using short-circuit evaluation.
- The And operator
&&
does not evaluate the second operand if the first is0
, and returns0
in that case. - The Or operator
||
does not evaluate the second operand if the first is1
, and returns1
in that case.
The fact that the logical operators &&
and ||
use short-circuit evaluation brings some advantages. For example, suppose we want to evaluate whether the division between two variables x
and y
is greater than zero. However, we must be cautious if y
is zero, as a division by zero could crash our program.
We could first check that y
is not zero (with an if
statement, which we’ll cover in the next lessons) and then check whether the division is greater than zero.
Or, we can use short-circuit evaluation like this:
(y != 0) && (x / y > 0)
In this way, if the variable y
equals zero, the division will not be evaluated thanks to the short-circuit behavior, thus avoiding a division by zero.
Short-Circuit Evaluation and Side Effects
The fact that logical operators evaluate operands using short-circuit evaluation may lead to unwanted side effects.
For example, consider the following expression:
x > 0 && ++y > 0
Based on operator precedence, the second operand consists of incrementing the variable y
and then checking if it's greater than zero.
However, if x
is less than or equal to zero, the second operand will not be evaluated, which means the variable y
might not be incremented.
To fix this, you can either reverse the operand order (the And operator is commutative):
++y > 0 && x > 0
Or increment the variable y
separately and then perform the check.
In Summary
In this lesson, we introduced logical expressions in the C language. We saw that there is no boolean type in C, but the values for true and false are represented as integers: 1
and 0
.
Logical expressions return an integer and can be built using various operators. They can also be combined with mathematical expressions.
In the next lessons, we’ll see how to use logical expressions in selection statements and loop statements.