Day 5: Operators in ANSI C
Josue Batey
@AmaliTech Back-end | JS/TS | Python | Low level code enthusiast | Open Source enthusiast
Operators in C are symbols or keywords used to perform operations on variables and values. They are broadly classified into different categories:
1. Arithmetic Operators
Arithmetic operators are used for basic mathematical operations:
+ : Addition (e.g., b + b)
- : Subtraction (e.g., a - b)
* : Multiplication (e.g., a * b)
/ : Division (e.g., a / b)
% : Modulus (remainder) (e.g., a % b)
Example:
int a = 10, b = 3;
int sum = a + b; // sum = 13
int remainder = a % b; // remainder = 1
2. Unary Operators
Unary operators operate on a single operand:
+ : Unary plus (e.g., +a)
- : Unary minus (e.g., -a)
++ : Increment (e.g., ++a or a++)
-- : Decrement (e.g., --a or a--)
! : Logical NOT (e.g., !a)
Example:
int x = 5;
x++; // Post-increment: x becomes 6
--x; // Pre-decrement: x becomes 5 again
3. Assignment Operators
Assignment operators are used to assign values to variables:
= : Assign (e.g., a = 5)
+= : Add and assign (e.g., a += 5 is equivalent to a = a + 5)
-= : Subtract and assign
*= : Multiply and assign
/= : Divide and assign
%= : Modulus and assign
Example:
int a = 10;
a += 5; // a = 15
4. Logical Operators
Logical operators are used to perform logical operations:
- && : Logical AND (true if both operands are true)
- || : Logical OR (true if at least one operand is true)
- ! : Logical NOT (negates the operand)
Example:
int a = 1, b = 0;
if (a && b) {
printf("Both are true\n");
} else {
printf("At least one is false\n");
}
5. Bitwise Operators
Bitwise operators allow you to manipulate data at the binary level, which can be extremely useful for tasks like low-level programming, setting or clearing bits, and optimizing performance:
& : Bitwise AND (Compares each bit of two numbers and sets the result bit to 1 if both corresponding bits are 1; otherwise, it's 0.)
| : Bitwise OR (Compares each bit of two numbers and sets the result bit to 1 if at least one of the corresponding bits is 1)
^ : Bitwise XOR (Compares each bit of two numbers and sets the result bit to 1 if the corresponding bits are different (i.e., one is 1 and the other is 0); otherwise, it's 0.)
~ : Bitwise NOT ( Inverts all the bits of a number (1 becomes 0 and 0 becomes 1).)
<< : Left shift (Shifts all bits of a number to the left by a specified number of positions. Each left shift multiplies the number by 2.)
>> : Right shift (Shifts all bits of a number to the right by a specified number of positions. Each right shift divides the number by 2.)
Example:
int a = 5; // Binary: 0101
int b = 3; // Binary: 0011
// &
int result = a & b; // Binary: 0001 (result = 1)
// |
int result = a | b; // Binary: 0111 (result = 7)
// ^
int result = a ^ b; // Binary: 0110 (result = 6)
// ~
int result = ~a; // Binary: 1010 (Two's complement representation: -6)
// <<
int result = a << 1; // Binary: 1010 (result = 10)
// >>
int result = a >> 1; // Binary: 0010 (result = 2)
6. Signed Number Representation
In C, signed numbers are represented using the Two's Complement method. This is the most common way to encode positive and negative integers in binary.
Key Concepts
1. Most Significant Bit (MSB):
- The leftmost bit (MSB) is used to indicate the sign:
- 0: Positive number.
- 1: Negative number.
2. Positive Numbers:
- Represented in regular binary format.
Example for 5 (8-bit representation):
Decimal: 5
Binary: 00000101
3. Negative Numbers (Two's Complement):
- To represent a negative number, take the two's complement of the corresponding positive number:
1. Start with the positive number in binary.
2. Invert all the bits (flip 0 to 1 and 1 to 0).
3. Add 1 to the inverted bits.
Example for -5 (8-bit representation):
Step 1: Start with 5 in binary: 00000101
Step 2: Invert all bits: 11111010
Step 3: Add 1: 11111011
Result: -5 is represented as 11111011.
When performing operations like right-shifting on signed numbers, the MSB (sign bit) may be replicated (arithmetic shift), ensuring the sign is preserved. This behavior depends on the compiler and platform.
7. Operator Precedence and Associativity
Precedence determines the order of evaluation when multiple operators are used. Associativity defines the order in which operators of the same precedence are evaluated.
When writing complex expressions in C, understanding operator precedence and associativity is crucial for ensuring the correct evaluation of operations
What is Operator Precedence?
Precedence determines the order in which operators are evaluated in an expression. For example, multiplication (`*`) has a higher precedence than addition (`+`), so it is evaluated first unless parentheses override the order.
Example of Precedence:
#include <stdio.h>
int main() {
int result = 2 + 3 4; // Multiplication () is evaluated first
printf("Result: %d\n", result); // Output: 14
return 0;
}
In the above example, the expression 3 * 4 is calculated first, followed by the addition of 2.
What is Operator Associativity?
Associativity determines the direction of evaluation for operators with the same precedence level. It can either be left-to-right or right-to-left.
Example of Left-to-Right Associativity:
Most operators, such as +, -, *, /, follow left-to-right associativity.
#include <stdio.h>
int main() {
int a = 10, b = 5, c = 2;
int result = a - b - c; // Evaluated as (a - b) - c
printf("Result: %d\n", result); // Output: 3
return 0;
}
Here, 10 - 5 is evaluated first, followed by 5 - 2, resulting in 3.
Example of Right-to-Left Associativity:
Some operators, such as the assignment (`=`) and ternary (`?:`) operators, follow right-to-left associativity.
#include <stdio.h>
int main() {
int a = 10, b = 20, c;
c = a = b; // Evaluated as c = (a = b)
printf("a: %d, c: %d\n", a, c); // Output: a: 20, c: 20
return 0;
}
Here, a = b is evaluated first, assigning 20 to a. Then, c = a assigns the value 20 to c.
Best Practices
1. Use Parentheses for Clarity: Always use parentheses to make your expressions easier to read and maintain.
2. Avoid Overcomplicated Expressions: Break down complex calculations into smaller, manageable parts for better readability and debugging.
Example:
#include <stdio.h>
int main() {
int a = 10, b = 5, c = 2;
int result = (a - b) * c; // Parentheses make the precedence explicit
printf("Result: %d\n", result); // Output: 10
return 0;
}
By understanding precedence and associativity, you can write more accurate and efficient code, avoiding unexpected results in complex expressions.