C Programming Basics: Part 3 – Operators and Expressions
Welcome to the third part of our C programming tutorial series! In our previous post, we explored variables and data types in C. Now, we’ll dive into operators and expressions, which allow us to manipulate data and perform calculations in our programs.
What Are Operators and Expressions?
In C, operators are symbols that tell the compiler to perform specific mathematical or logical operations. Expressions are combinations of variables, constants, and operators that evaluate to a single value.
Think of operators as verbs in a sentence, telling the computer what action to take with the data.
Arithmetic Operators
Arithmetic operators perform mathematical calculations:
Operator | Description | Example |
---|---|---|
+ | Addition | a + b |
- | Subtraction | a - b |
* | Multiplication | a * b |
/ | Division | a / b |
% | Modulus (remainder) | a % b |
++ | Increment | a++ or ++a |
-- | Decrement | a-- or --a |
Example:
#include <stdio.h>
int main() {
int a = 10, b = 3;
printf("Addition: %d\n", a + b); // 13
printf("Subtraction: %d\n", a - b); // 7
printf("Multiplication: %d\n", a * b); // 30
printf("Division: %d\n", a / b); // 3
printf("Modulus: %d\n", a % b); // 1
int c = a++;
printf("Post-increment: c = %d, a = %d\n", c, a); // c = 10, a = 11
int d = ++b;
printf("Pre-increment: d = %d, b = %d\n", d, b); // d = 4, b = 4
return 0;
}
Integer Division vs. Floating-Point Division
Be careful with the division operator (/
). When both operands are integers, C performs integer division, discarding any remainder:
int result = 5 / 2; // result = 2, not 2.5
To get a floating-point result, at least one operand must be a float:
float result = 5.0 / 2; // result = 2.5
// or
float result = (float)5 / 2; // result = 2.5
Increment and Decrement Operators
The increment (++
) and decrement (--
) operators are unique because they can be used in two ways:
- Pre-increment/decrement:
++a
or--a
(increment/decrement first, then use the value) - Post-increment/decrement:
a++
ora--
(use the value first, then increment/decrement)
Let’s visualize how these operators work:
sequenceDiagram participant Pre as Pre-increment (++a) participant Post as Post-increment (a++) Note over Pre: Initial value: a = 5 Pre->>Pre: Increment first (a = 6) Pre->>Pre: Then use value (6) Note over Post: Initial value: a = 5 Post->>Post: Use value first (5) Post->>Post: Then increment (a = 6)
Assignment Operators
Assignment operators assign values to variables:
Operator | Description | Example | Equivalent |
---|---|---|---|
= | Basic assignment | a = b | a = b |
+= | Add and assign | a += b | a = a + b |
-= | Subtract and assign | a -= b | a = a - b |
*= | Multiply and assign | a *= b | a = a * b |
/= | Divide and assign | a /= b | a = a / b |
%= | Modulus and assign | a %= b | a = a % b |
Example:
#include <stdio.h>
int main() {
int a = 10;
a += 5; // a = a + 5
printf("After a += 5: %d\n", a); // 15
a -= 3; // a = a - 3
printf("After a -= 3: %d\n", a); // 12
a *= 2; // a = a * 2
printf("After a *= 2: %d\n", a); // 24
a /= 4; // a = a / 4
printf("After a /= 4: %d\n", a); // 6
a %= 4; // a = a % 4
printf("After a %%= 4: %d\n", a); // 2
return 0;
}
Comparison Operators
Comparison operators compare two values and return a boolean result (1 for true, 0 for false):
Operator | Description | Example |
---|---|---|
== | Equal to | a == b |
!= | Not equal to | a != b |
> | Greater than | a > b |
< | Less than | a < b |
>= | Greater than or equal to | a >= b |
<= | Less than or equal to | a <= b |
Example:
#include <stdio.h>
int main() {
int a = 10, b = 5;
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;
}
Common Mistake: Confusing =
and ==
A common error in C is using the assignment operator (=
) when you mean to use the equality comparison operator (==
):
if (a = b) { // WRONG: This assigns b to a, then evaluates if a is non-zero
// Code here
}
if (a == b) { // CORRECT: This checks if a is equal to b
// Code here
}
Logical Operators
Logical operators perform logical operations, typically on boolean expressions:
Operator | Description | Example |
---|---|---|
&& | Logical AND | a && b |
|| | Logical OR | a || b |
! | Logical NOT | !a |
Example:
#include <stdio.h>
int main() {
int a = 1, b = 0; // In C, 1 is true, 0 is false
printf("a && b: %d\n", a && b); // 0 (false)
printf("a || b: %d\n", a || b); // 1 (true)
printf("!a: %d\n", !a); // 0 (false)
printf("!b: %d\n", !b); // 1 (true)
return 0;
}
Logical Operator Behavior
Here’s a visualization of how logical operators evaluate:
graph LR subgraph "Logical AND" A1["true && true"] --> B1[true] A2["true && false"] --> B2[false] A3["false && true"] --> B3[false] A4["false && false"] --> B4[false] end subgraph "Logical OR" C1["true || true"] --> D1[true] C2["true || false"] --> D2[true] C3["false || true"] --> D3[true] C4["false || false"] --> D4[false] end subgraph "Logical NOT" E1["!true"] --> F1[false] E2["!false"] --> F2[true] end
Short-Circuit Evaluation
C uses short-circuit evaluation for logical operators, which means it stops evaluating as soon as the result is determined:
- For
&&
: If the first operand is false, the second is not evaluated - For
||
: If the first operand is true, the second is not evaluated
This can be useful for conditional checks:
// Check if pointer is not NULL before accessing its value
if (ptr != NULL && ptr->value > 0) {
// Safe to use ptr
}
Bitwise Operators
Bitwise operators manipulate individual bits in integer values:
Operator | Description | Example |
---|---|---|
& | Bitwise AND | a & b |
| | Bitwise OR | a | b |
^ | Bitwise XOR | a ^ b |
~ | Bitwise NOT | ~a |
<< | Left shift | a << n |
>> | Right shift | a >> n |
Example:
#include <stdio.h>
int main() {
unsigned char a = 5; // 00000101 in binary
unsigned char b = 9; // 00001001 in binary
printf("a & b: %d\n", a & b); // 1 (00000001)
printf("a | b: %d\n", a | b); // 13 (00001101)
printf("a ^ b: %d\n", a ^ b); // 12 (00001100)
printf("~a: %d\n", ~a); // 250 (11111010)
printf("a << 1: %d\n", a << 1); // 10 (00001010)
printf("b >> 1: %d\n", b >> 1); // 4 (00000100)
return 0;
}
Visualizing Bitwise Operations
Let’s visualize how bitwise AND, OR, and XOR work:
Bitwise operators are powerful for low-level operations like:
- Setting, clearing, or toggling specific bits
- Creating bit masks for flags or options
- Efficient multiplication or division by powers of 2
- Working with hardware registers
The Conditional (Ternary) Operator
The conditional operator (? :
) is a shorthand for an if-else statement:
condition ? expression_if_true : expression_if_false
Example:
#include <stdio.h>
int main() {
int a = 10, b = 5;
int max = (a > b) ? a : b;
printf("Maximum of %d and %d is %d\n", a, b, max); // 10
// Equivalent if-else statement:
/*
int max;
if (a > b) {
max = a;
} else {
max = b;
}
*/
return 0;
}
Other Important Operators
The Sizeof Operator
The sizeof
operator returns the size (in bytes) of a variable or data type:
#include <stdio.h>
int main() {
int a;
double b;
printf("Size of int: %zu bytes\n", sizeof(int)); // Typically 4
printf("Size of double: %zu bytes\n", sizeof(double)); // Typically 8
printf("Size of variable a: %zu bytes\n", sizeof(a)); // Same as sizeof(int)
return 0;
}
Address and Dereference Operators
- The address operator (
&
) returns the memory address of a variable - The dereference operator (
*
) accesses the value at a memory address
#include <stdio.h>
int main() {
int a = 10;
int *ptr = &a; // ptr holds the address of a
printf("Value of a: %d\n", a); // 10
printf("Address of a: %p\n", &a); // Memory address (e.g., 0x7ffeeb1be2bc)
printf("Value of ptr: %p\n", ptr); // Same address as &a
printf("Value pointed to by ptr: %d\n", *ptr); // 10
*ptr = 20; // Change the value at the address stored in ptr
printf("New value of a: %d\n", a); // 20
return 0;
}
Operator Precedence and Associativity
Not all operators are created equal! C has rules about which operators are evaluated first in an expression:
- Precedence: Determines which operator is evaluated first. Higher precedence operators are evaluated before lower precedence ones.
- Associativity: Determines the order of evaluation when operators have the same precedence (left-to-right or right-to-left).
Here’s a simplified precedence table (from highest to lowest):
Precedence | Operator | Description | Associativity |
---|---|---|---|
1 | () [] -> . | Parentheses, array subscript, member access | Left to right |
2 | ! ~ ++ -- + - * & sizeof | Unary operators | Right to left |
3 | * / % | Multiplication, division, modulus | Left to right |
4 | + - | Addition, subtraction | Left to right |
5 | << >> | Bit shift | Left to right |
6 | < <= > >= | Relational operators | Left to right |
7 | == != | Equality operators | Left to right |
8 | & | Bitwise AND | Left to right |
9 | ^ | Bitwise XOR | Left to right |
10 | | | Bitwise OR | Left to right |
11 | && | Logical AND | Left to right |
12 | || | Logical OR | Left to right |
13 | ? : | Conditional operator | Right to left |
14 | = += -= etc. | Assignment operators | Right to left |
15 | , | Comma operator | Left to right |
Example:
#include <stdio.h>
int main() {
int a = 5, b = 3, c = 8;
int result;
// Without parentheses, follows precedence rules
result = a + b * c; // * has higher precedence than +
printf("a + b * c = %d\n", result); // 5 + (3 * 8) = 5 + 24 = 29
// With parentheses, we can override precedence
result = (a + b) * c;
printf("(a + b) * c = %d\n", result); // (5 + 3) * 8 = 8 * 8 = 64
return 0;
}
Using Parentheses for Clarity
When in doubt, use parentheses to make your intentions clear. Even if they’re not strictly necessary for correct evaluation, parentheses can make your code more readable:
// Hard to read at a glance
int result = a * b + c * d + e / f;
// Much clearer
int result = (a * b) + (c * d) + (e / f);
Expressions and Statements
Now that we understand operators, let’s clarify the difference between expressions and statements:
- Expression: A combination of operators, constants, and variables that evaluates to a value
- Statement: A complete instruction that performs some action
An expression becomes a statement when it’s followed by a semicolon:
// Expression
a + b
// Statement
a + b; // This evaluates a + b but does nothing with the result
// Useful statement
c = a + b; // The expression a + b is calculated and assigned to c
Practical Applications
Let’s see some practical examples of operators and expressions in action:
Temperature Conversion
#include <stdio.h>
int main() {
float celsius, fahrenheit;
printf("Enter temperature in Celsius: ");
scanf("%f", &celsius);
// Formula using operators: F = (C * 9/5) + 32
fahrenheit = (celsius * 9.0/5.0) + 32.0;
printf("%.2f°C is %.2f°F\n", celsius, fahrenheit);
return 0;
}
Calculating a Bill with Tax and Tip
#include <stdio.h>
int main() {
float bill_amount, tax_rate, tip_percentage;
printf("Enter bill amount: $");
scanf("%f", &bill_amount);
printf("Enter tax rate (%%): ");
scanf("%f", &tax_rate);
printf("Enter tip percentage (%%): ");
scanf("%f", &tip_percentage);
// Calculate tax amount
float tax_amount = bill_amount * (tax_rate / 100);
// Calculate subtotal (bill + tax)
float subtotal = bill_amount + tax_amount;
// Calculate tip amount based on subtotal
float tip_amount = subtotal * (tip_percentage / 100);
// Calculate total bill
float total = subtotal + tip_amount;
printf("\n--- Bill Summary ---\n");
printf("Original bill: $%.2f\n", bill_amount);
printf("Tax (%.2f%%): $%.2f\n", tax_rate, tax_amount);
printf("Subtotal: $%.2f\n", subtotal);
printf("Tip (%.2f%%): $%.2f\n", tip_percentage, tip_amount);
printf("Total: $%.2f\n", total);
return 0;
}
Conclusion
Operators and expressions are the workhorses of C programming, allowing you to manipulate data in countless ways. From simple arithmetic to complex bitwise operations, understanding how operators work is essential for becoming proficient in C.
Remember these key points:
- Understand operator precedence and use parentheses when in doubt
- Be careful with integer division
- Watch out for common mistakes like confusing
=
and==
- Use short-circuit evaluation to your advantage
- Choose the appropriate operators for your task to make code efficient and readable
In the next part of our series, we’ll explore control flow in C, including conditional statements and loops that allow your programs to make decisions and repeat actions.
Practice Exercises
- Write a program that takes three integers as input and outputs the largest value using only the conditional operator.
- Create a program that converts a decimal number to its binary representation using bitwise operators.
- Write a program that swaps the values of two variables without using a temporary variable. (Hint: Use arithmetic or XOR operations)
- Create a program that determines if an integer is even or odd, positive or negative, and a multiple of 5 or not.
- Write a program that calculates the area and perimeter of a rectangle using variables, arithmetic operators, and formatted output.
Happy coding!