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.

Key Takeaways
  • 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.

Definition

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.

Definition

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
< &lt; "Less than"
> &gt; "Greater than"
<= \leq "Less than or equal to"
>= \geq "Greater than or equal to"
Table 1: Relational operators in the C language

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.

Note

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:

Definition

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"
!= \neq "Not equal to"
Table 2: Equality operators in the C language

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.

Note

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:

Definition

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
Table 3: Logical operators in the C language

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:

Definition

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: &&. Returns 1 if both operands are 1. Returns 0 otherwise:

    (expression_1) && (expression_2)
    
  • Or operator: ||. Returns 1 if at least one of the operands is 1. Returns 0 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.

Definition

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.

Definition

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.

Definition

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 is 0, and returns 0 in that case.
  • The Or operator || does not evaluate the second operand if the first is 1, and returns 1 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.

Note

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.