Chapter 05 of 24
5. Expressions and operators
How expressions produce answers
An expression combines values and operators to produce another value. For example, 3 + 4 produces 7; the plus sign is the operator and the numbers are its operands.
- Start with arithmetic such as
price * quantity. - Add comparisons such as
count > 0, which produce a true-or-false result for a decision. - Combine comparisons with logical operators, and use parentheses to make the intended grouping easy to see.
When an answer surprises you, write down the value and type of each part and check the grouping. The later examples expand from everyday arithmetic to bit-level operations.
An expression computes a value. Operators include:
- Arithmetic:
+,-,*,/,%(remainder for integers). - Comparison:
==,!=,<,<=,>,>=. - Logical:
&&(and),||(or),!(not). - Assignment:
=, and compound forms such as+=and*=. - Increment/decrement:
++and--. - Conditional:
condition ? value_if_true : value_if_false. - Bitwise:
&,|,^,~,<<,>>. - Address and indirection:
&objectand*pointer.
Do not confuse assignment (=) with equality comparison (==). Logical
operators produce either zero or one and short-circuit: the right operand of
&& or || may not be evaluated.
Parentheses make intent clear. Avoid relying on subtle precedence or on the
order of evaluation of expressions with side effects. For example, do not
modify the same variable multiple times in one expression.
Arithmetic, comparisons, and logic
Integer division truncates toward zero. The remainder operator % is useful
for divisibility tests, such as number % 2 == 0, but its operands must be
integers and the divisor must not be zero. Integer overflow is not a safe way
to detect a value that is too large; signed overflow has undefined behavior.
Check the bounds before performing a potentially overflowing calculation.
Comparisons produce an integer truth value: zero means false and nonzero means
true. In conditions, C treats zero as false and nonzero as true. Combine
conditions with && and ||, and negate with !. These operators
short-circuit, so this is a useful safe pattern:
#include <stdio.h>
int main(void)
{
int divisor = 3;
int numerator = 10;
if (divisor != 0 && numerator / divisor > 2) {
puts("The quotient is greater than two.");
}
return 0;
}
The division is not evaluated when divisor is zero. Keep each side effect
in its own statement; expressions such as i++ + i++ do not provide a
reliable, portable order of updates.
Bitwise operations
Bitwise operators work on the individual bits of integer values, unlike
logical operators which combine truth values. They are useful for flags and
encoded fields, but should be introduced only when a problem calls for them.
For example, flags & mask tests whether any masked bits are set; flags | mask sets those bits. Use unsigned integer types for bit manipulation and
check shift counts and widths carefully.
#include <stdbool.h>
#include <stdio.h>
int main(void)
{
unsigned int flags = 0u;
const unsigned int READABLE = 1u << 0;
const unsigned int WRITABLE = 1u << 1;
flags |= READABLE; // set a flag
bool can_read = (flags & READABLE) != 0u;
flags &= ~READABLE; // clear a flag
flags ^= WRITABLE; // toggle a flag
return can_read && flags == WRITABLE ? 0 : 1;
}
Each shift count must be smaller than the width of the left operand.
Checkpoint: Explain why && is different from &, and identify the
condition that prevents division by zero in the example.
Exercises: write a test for whether a year is a leap year; use % to test
even/odd; use a conditional expression to choose the larger of two values.