C Operators — Arithmetic, Relational, Logical, Bitwise, and Assignment
In this tutorial, you will learn about C Operators. We cover key concepts, practical examples, and best practices to help you master this topic.
C operators are symbols that perform operations on operands, including arithmetic for calculations, relational for comparisons, logical for Boolean conditions, bitwise for direct bit manipulation, and assignment for storing values.
Why It Matters
Operators are the building blocks of all computation in C. Every program calculates values, compares data, makes decisions, and manipulates memory. C provides a rich set of operators that map directly to CPU instructions, giving you fine-grained control over performance. Understanding operator precedence and associativity prevents subtle bugs that are hard to find.
Real-World Use
Device drivers use bitwise operators to set and clear individual bits in hardware registers. Network code uses shift operators to pack and unpack protocol headers. Security scanning tools like Durga Antivirus Pro use bitwise operations for fast pattern matching and checksum calculations. Database systems use relational operators in query filters.
What You Will Learn
- Arithmetic operators for basic math operations
- Relational operators for comparing values
- Logical operators for combining conditions
- Bitwise operators for bit-level manipulation
- Assignment operators and compound assignments
- Operator precedence and associativity rules
Learning Path
flowchart LR A[Constants] --> B[Operators
You are here] B --> C[Control Flow] C --> D[Loops] D --> E[Arrays] style B fill:#f90,color:#fff
Arithmetic Operators
C provides the standard arithmetic operators:
#include <stdio.h>
int main() {
int a = 10, b = 3;
printf("a + b = %d\n", a + b); // 13
printf("a - b = %d\n", a - b); // 7
printf("a * b = %d\n", a * b); // 30
printf("a / b = %d\n", a / b); // 3 (integer division)
printf("a %% b = %d\n", a % b); // 1 (modulus)
// Division with floating point
double x = 10.0, y = 3.0;
printf("x / y = %.2f\n", x / y); // 3.33
// Integer division truncates toward zero
printf("-10 / 3 = %d\n", -10 / 3); // -3 (truncates toward zero)
return 0;
}
Expected output:
a + b = 13
a - b = 7
a * b = 30
a / b = 3
a % b = 1
x / y = 3.33
-10 / 3 = -3
Integer Division Rules
Dividing two integers yields an integer result. The fractional part is truncated (discarded). This is a common source of bugs:
int percent = (50 / 100) * 100; // 0, not 50!
int correct = (50 * 100) / 100; // 50
To get a floating-point result, make at least one operand a float or double:
double result = 10 / (double)3; // 3.333...
Modulus Operator
The % operator returns the remainder of division. It works only with integer types:
int remainder = 17 % 5; // 2 (because 5*3=15, remainder 2)
Relational Operators
Relational operators compare values and return 1 (true) or 0 (false):
#include <stdio.h>
int main() {
int a = 5, b = 10;
printf("a == b: %d\n", a == b); // 0 (false)
printf("a != b: %d\n", a != b); // 1 (true)
printf("a < b: %d\n", a < b); // 1 (true)
printf("a > b: %d\n", a > b); // 0 (false)
printf("a <= b: %d\n", a <= b); // 1 (true)
printf("a >= b: %d\n", a >= b); // 0 (false)
return 0;
}
Expected output:
a == b: 0
a != b: 1
a < b: 1
a > b: 0
a <= b: 1
a >= b: 0
In C, any non-zero value is considered true in a Boolean context. Zero is false. Relational operators always return 0 or 1.
Logical Operators
Logical operators combine Boolean expressions:
#include <stdio.h>
int main() {
int age = 25;
int has_license = 1; // true
int is_sunday = 0; // false
// AND: both must be true
if (age >= 18 && has_license) {
printf("Can drive\n");
}
// OR: at least one must be true
if (is_sunday || age >= 18) {
printf("Can go out\n");
}
// NOT: inverts the condition
if (!is_sunday) {
printf("Not Sunday\n");
}
// Short-circuit evaluation
// If the first condition is false with &&,
// the second is never evaluated
int x = 0;
if (x != 0 && 10 / x > 2) {
printf("This never executes\n");
}
return 0;
}
Expected output:
Can drive
Can go out
Not Sunday
Short-Circuit Evaluation
The && and || operators use short-circuit evaluation. For &&, if the left operand is false, the right operand is not evaluated. For ||, if the left operand is true, the right operand is not evaluated. This is crucial for writing safe conditions like checking for NULL before dereferencing.
Bitwise Operators
Bitwise operators manipulate individual bits in integer values:
#include <stdio.h>
int main() {
unsigned int a = 0b1100; // 12 in binary
unsigned int b = 0b1010; // 10 in binary
printf("a & b = %u (0b%04b)\n", a & b, a & b); // 8 (1000)
printf("a | b = %u (0b%04b)\n", a | b, a | b); // 14 (1110)
printf("a ^ b = %u (0b%04b)\n", a ^ b, a ^ b); // 6 (0110)
printf("~a = %u\n", ~a); // bitwise NOT
printf("a << 1 = %u (0b%04b)\n", a << 1, a << 1); // 24 (11000)
printf("a >> 1 = %u (0b%04b)\n", a >> 1, a >> 1); // 6 (0110)
return 0;
}
Expected output:
a & b = 8 (0b1000)
a | b = 14 (0b1110)
a ^ b = 6 (0b0110)
~a = 4294967283
a << 1 = 24 (0b11000)
a >> 1 = 6 (0b0110)
Common Bitwise Patterns
// Set bit N (0-indexed)
x |= (1 << N);
// Clear bit N
x &= ~(1 << N);
// Toggle bit N
x ^= (1 << N);
// Check bit N
if (x & (1 << N)) { ... }
// Check if power of 2
if (x && !(x & (x - 1))) { ... }
// Check if even/odd
if (x & 1) { /* odd */ } else { /* even */ }
Assignment Operators
Simple assignment = and compound assignment operators:
#include <stdio.h>
int main() {
int x = 10;
x += 5; // x = x + 5 -> 15
x -= 3; // x = x - 3 -> 12
x *= 2; // x = x * 2 -> 24
x /= 4; // x = x / 4 -> 6
x %= 3; // x = x % 3 -> 0
int y = 0b1100;
y &= 0b1010; // y = y & 0b1010 -> 8
y |= 0b0001; // y = y | 0b0001 -> 9
y ^= 0b0011; // y = y ^ 0b0011 -> 10
y <<= 1; // y = y << 1 -> 20
y >>= 2; // y = y >> 2 -> 5
printf("x: %d\n", x);
printf("y: %d\n", y);
return 0;
}
Expected output:
x: 0
y: 5
Increment and Decrement Operators
The ++ and -- operators add or subtract 1:
#include <stdio.h>
int main() {
int x = 5;
// Prefix: increment, then use
int y = ++x; // x becomes 6, y becomes 6
printf("Prefix: x=%d, y=%d\n", x, y);
// Postfix: use, then increment
int a = 5;
int b = a++; // b becomes 5, a becomes 6
printf("Postfix: a=%d, b=%d\n", a, b);
// Common usage
for (int i = 0; i < 5; i++) {
printf("%d ", i);
}
printf("\n");
return 0;
}
Expected output:
Prefix: x=6, y=6
Postfix: a=6, b=5
0 1 2 3 4
Operator Precedence
When multiple operators appear in an expression, precedence determines the order of evaluation:
#include <stdio.h>
int main() {
int result;
// Multiplication before addition
result = 5 + 3 * 4; // 17, not 32
printf("5 + 3 * 4 = %d\n", result);
// Use parentheses to override
result = (5 + 3) * 4; // 32
printf("(5 + 3) * 4 = %d\n", result);
// Relational vs logical
result = 5 > 3 && 2 < 4; // 1 (true)
printf("5 > 3 && 2 < 4 = %d\n", result);
// Bitwise vs comparison
result = 5 & 3 == 1; // 5 & (3 == 1) -> 5 & 0 -> 0
printf("5 & 3 == 1 = %d\n", result);
result = (5 & 3) == 1; // (1) == 1 -> 1
printf("(5 & 3) == 1 = %d\n", result);
return 0;
}
Expected output:
5 + 3 * 4 = 17
(5 + 3) * 4 = 32
5 > 3 && 2 < 4 = 1
5 & 3 == 1 = 0
(5 & 3) == 1 = 1
Precedence Table (Highest to Lowest)
()[]->.(postfix)++--+-!~*&sizeof(unary, R-to-L)*/%(multiplicative)+-(additive)<<>>(shift)<<=>>=(relational)==!=(equality)&(bitwise AND)^(bitwise XOR)|(bitwise OR)&&(logical AND)||(logical OR)?:(conditional, R-to-L)=+=-=etc. (assignment, R-to-L),(comma)
Common Mistakes
1. Using = Instead of ==
if (x = 5) // Always true! Assigns 5 to x, then tests x
Use == for comparison. Some compilers warn about this with -Wall.
2. Assuming Logical AND is the Same as Bitwise AND
if (x & 2) // Bitwise AND, not logical
if (x && 2) // Logical AND (both non-zero)
& is bitwise; && is logical. They behave differently with non-Boolean values.
3. Integer Division Confusion
double ratio = 1 / 3; // 0.0, not 0.333
Use 1.0 / 3 or cast one operand: (double)1 / 3.
4. Precedence Mistakes
if (x & 0x01 == 0) // Evaluated as x & (0x01 == 0) -> x & 0 -> 0
Always use parentheses with bitwise operators in conditions.
5. Side Effects in Macro Arguments
#define SQR(x) ((x)*(x))
int y = SQR(++x); // UB: x is modified twice
Never pass expressions with side effects to macros that evaluate the argument multiple times.
Practice Questions
What is the output of
5 / 2in C? 2 -- integer division truncates the fractional part.What does the
%operator do? It returns the remainder of integer division.5 % 2is 1.What is short-circuit evaluation? For
&&and||, the right operand is not evaluated if the left operand determines the result.How do you set, clear, and toggle specific bits? Set:
x |= (1 << n), Clear:x &= ~(1 << n), Toggle:x ^= (1 << n).Challenge: Write a program that counts the number of 1 bits in an integer without using a loop (hint: use a trick like
x & (x-1)).
Mini Project: Bit Manipulation Utility
Create a program that prints the binary representation of integers and demonstrates bit manipulation:
#include <stdio.h>
#include <limits.h>
void print_binary(unsigned int x) {
for (int i = sizeof(x) * CHAR_BIT - 1; i >= 0; i--) {
putchar((x & (1u << i)) ? '1' : '0');
if (i % 4 == 0) putchar(' ');
}
putchar('\n');
}
int main() {
unsigned int value = 42;
printf("Original: ");
print_binary(value);
value |= (1 << 3); // Set bit 3
printf("Set bit 3: ");
print_binary(value);
value &= ~(1 << 5); // Clear bit 5
printf("Clear bit 5: ");
print_binary(value);
value ^= (1 << 1); // Toggle bit 1
printf("Toggle bit 1: ");
print_binary(value);
return 0;
}
FAQ
What is Next
Now that you understand operators, proceed to Control Flow to learn how to use if/else, switch, and conditional expressions to control program execution.