C Operators

Operators are the symbols C uses to compute, compare, combine, and update values. They are small pieces of syntax, but they control most of what a program actually does: arithmetic, decisions, loops, flags, and memory-related expressions all depend on them.

Learning operators well means learning both their visible result and their rules: operand types, integer division, precedence, associativity, and side effects. A program can compile and still be wrong if an operator is used with the wrong expectation.

Overview: How C Operators Work

An operator acts on one, two, or three operands and produces a result. In a + b, + is a binary operator because it uses two operands. In !ready, ! is unary because it uses one operand. In condition ? yes : no, the conditional operator is ternary because it uses three operands.

C operators are strongly connected to types. If both operands of / are integers, C performs integer division and discards the fractional part. If at least one operand has a floating-point type after the usual conversions, C performs floating-point division. Similarly, comparison operators such as < and == produce an int result: 1 for true and 0 for false.

Before many binary operations, C applies the usual arithmetic conversions. Smaller integer types such as char and short are promoted to int when possible, and mixed numeric expressions are converted to a common type. This is why 3 / 2 gives 1, while 3.0 / 2 gives 1.5. The operator is the same, but the operand types change the operation.

Some operators also have side effects. Assignment changes the stored value of the left operand. Increment and decrement change an object. Compound assignment such as x += 5 both computes and stores. Logical && and || short-circuit, which means the right operand may not even be evaluated. This can be useful, but it also means expressions with function calls or increments inside them must be written carefully.

Syntax

The exact syntax depends on the operator category, but the common forms are:

Category Form Meaning
Arithmetic a + b, a - b, a * b, a / b, a % b Compute numeric results. % works only with integer operands.
Assignment x = value, x += value Store a new value in a modifiable object.
Comparison a == b, a != b, a < b, a >= b Produce 1 if true or 0 if false.
Logical !a, a && b, a || b Combine truth values. Zero is false; nonzero is true.
Increment ++x, x++, --x, x-- Add or subtract one, either before or after producing the expression value.
Bitwise a & b, a | b, a ^ b, ~a, a << n, a >> n Operate on individual bits of integer values.
Conditional condition ? value_if_true : value_if_false Select one of two expressions.

Operators also have precedence and associativity. Precedence decides which operation groups first; associativity decides how operators of the same precedence group. For example, multiplication has higher precedence than addition, so 2 + 3 * 4 is 14. Assignment associates right to left, so a = b = 10 stores 10 in b, then stores that result in a.

Examples

Arithmetic and Integer Division

#include <stdio.h>

int main(void) {
    int apples = 17;
    int boxes = 5;

    printf("apples + boxes = %d\n", apples + boxes);
    printf("apples - boxes = %d\n", apples - boxes);
    printf("apples * boxes = %d\n", apples * boxes);
    printf("apples / boxes = %d\n", apples / boxes);
    printf("apples %% boxes = %d\n", apples % boxes);

    double exact = (double) apples / boxes;
    printf("exact division = %.2f\n", exact);

    return 0;
}

Output:

apples + boxes = 22
apples - boxes = 12
apples * boxes = 85
apples / boxes = 3
apples % boxes = 2
exact division = 3.40

The first division uses two int operands, so C gives an integer quotient. The remainder operator % gives what is left after integer division. The cast (double) converts apples before division, so the final calculation keeps the fractional part.

Comparison and Logical Operators

#include <stdio.h>

int main(void) {
    int score = 86;
    int attendance = 92;

    int passed_exam = score >= 70;
    int attended_enough = attendance >= 80;
    int passed_course = passed_exam && attended_enough;

    printf("passed_exam = %d\n", passed_exam);
    printf("attended_enough = %d\n", attended_enough);
    printf("passed_course = %d\n", passed_course);
    printf("needs_review = %d\n", !passed_course);

    return 0;
}

Output:

passed_exam = 1
attended_enough = 1
passed_course = 1
needs_review = 0

Relational operators produce integer truth values. The logical AND operator && returns true only when both operands are true. The logical NOT operator ! flips true to false and false to true, so !passed_course is 0 here.

Bitwise Operators for Flags

#include <stdio.h>

int main(void) {
    int permissions = 0;
    const int CAN_READ = 1 << 0;
    const int CAN_WRITE = 1 << 1;
    const int CAN_EXECUTE = 1 << 2;

    permissions |= CAN_READ;
    permissions |= CAN_WRITE;

    printf("permissions = %d\n", permissions);
    printf("can read = %d\n", (permissions & CAN_READ) != 0);
    printf("can execute = %d\n", (permissions & CAN_EXECUTE) != 0);

    permissions &= ~CAN_WRITE;
    printf("after removing write = %d\n", permissions);

    return 0;
}

Output:

permissions = 3
can read = 1
can execute = 0
after removing write = 1

Bitwise operators work on the binary representation of integers. 1 << 0, 1 << 1, and 1 << 2 create values with one bit set. |= turns a flag on, & tests a flag, and &= ~flag clears a flag.

Conditional Operator and Compound Assignment

#include <stdio.h>

int main(void) {
    int stock = 3;
    int requested = 5;

    int shipped = requested <= stock ? requested : stock;
    stock -= shipped;

    printf("requested = %d\n", requested);
    printf("shipped = %d\n", shipped);
    printf("remaining stock = %d\n", stock);

    return 0;
}

Output:

requested = 5
shipped = 3
remaining stock = 0

The conditional operator chooses the smaller usable amount without an if statement. The expression before ? is tested first. Because the request is greater than the stock, the expression evaluates to stock. Then stock -= shipped subtracts and stores in one step.

How It Works Step by Step

When C evaluates an expression, it first groups operators by precedence and associativity. Parentheses override the default grouping and are often worth using even when you know the precedence. After grouping, C evaluates operands as required by the operator rules. Arithmetic operands may be promoted or converted to a common type before the actual operation happens.

For int result = a + b * c;, multiplication groups first, so C computes b * c and then adds a. For left && right, C evaluates left first. If left is zero, the result is already false, so right is skipped. For left || right, if left is nonzero, right is skipped because the result is already true.

Assignment is different from ordinary arithmetic because it requires a modifiable left operand, often called an lvalue. You can write x = 10, but not 5 = x or x + y = 10. The assignment expression itself has a value: the value stored. That is why chained assignment works, although it should be used only when it remains readable.

Common Mistakes

Confusing Assignment and Equality

= stores a value; == compares two values. In a condition, if (x = 5) assigns 5 to x and then tests the assigned value, which is true. To compare, write if (x == 5). Many compilers warn about this when warnings are enabled.

Expecting Floating-Point Results from Integer Division

5 / 2 is 2, not 2.5, because both operands are integers. Convert before dividing: (double)5 / 2. Converting after division, as in (double)(5 / 2), is too late because the fractional part has already been discarded.

Using Bitwise Operators Instead of Logical Operators

& and | operate on bits and always evaluate both operands. && and || operate on truth values and short-circuit. For conditions, usually use && and ||. Use bitwise operators for masks, flags, and low-level integer manipulation.

Relying Too Much on Precedence

Some precedence rules are easy, such as multiplication before addition. Others are easy to misread, especially when bitwise, comparison, and logical operators appear together. Prefer (permissions & CAN_READ) != 0 over permissions & CAN_READ != 0, because the first version states the intended grouping clearly.

Best Practices

  • Use parentheses when they make intent clearer, especially around comparisons mixed with bitwise or logical operators.
  • Compile with warnings enabled, such as -Wall -Wextra, so assignment-in-condition and precedence mistakes are easier to catch.
  • Cast before division when you need a floating-point result.
  • Avoid modifying the same variable more than once in one expression, such as combining i++ with another use of i.
  • Use && and || for conditions; use &, |, ^, and shifts for bit-level work.
  • Use named constants for bit flags instead of unexplained numbers.
  • Keep conditional operator expressions short. If either branch becomes complex, an if statement is usually clearer.

Practice Exercises

  1. Write a program that stores minutes in an int, then prints the number of whole hours and leftover minutes using / and %.
  2. Create three score variables and print whether a student passed only if all three scores are at least 60. Use comparison and logical operators.
  3. Define three bit flags named CAN_READ, CAN_WRITE, and CAN_DELETE. Turn two flags on, test each flag, then clear one flag.

Summary

  • Operators are the building blocks of C expressions.
  • Operand types matter: integer arithmetic and floating-point arithmetic can produce different results.
  • Comparison and logical operators produce 1 or 0.
  • && and || short-circuit, so the right operand may be skipped.
  • Assignment and increment operators have side effects because they change stored values.
  • Bitwise operators are for integer bits, masks, and flags.
  • Parentheses and compiler warnings help prevent subtle operator mistakes.