C Language Operators and Expressions
1. Core Operator Concepts
What is an Operator?
An operator is a symbol representing a computational or logical operation, such as +, -, *, /.
Operand Count Classification
Operators are grouped by the number of required operands:
- Unary operators: Require exactly one operand, e.g.,
++,--,& - Binary operators: Require two operands, e.g.,
+,-,*,/ - Ternary operators: Require three operands, e.g., the conditional operator
? :
Associativity
Associativity defines the order of operand evaluation for operators of identical precedence. It follows two patterns:
- Left-to-right: Evaluate the leftmost operation first, progressing rightward
- Right-to-left: Evaluate the rightmost operation first, progressing leftward
For example, subtraction (-) uses left-to-right associativity: a - b - c is evaluated as (a - b) - c, not a - (b - c).
Operator Precedence
Precedence dictates which operator executes first in a mixed-operator expression. The standard precedence hierarchy (highest to lowest) is:
- Unary operators
- Arithmetic operators
- Relational operators
- Logical operators
- Assignment operators
- Comma operator
2. Expressions
What is a Expression?
In C, an expression is a sequence of operators and operands that computes a value. Examples include 3 + 5, x *= 2, and ++counter.
Expression Value
Every valid C expression has a computed value, which represents the final result of the sequence.
3. Operator Categories
3.1 Arithmetic Operators
Arithmetic operators perform basic mathematical computations, including unary increment/decrement and binary arithmetic operations.
Key Rules
+,-,*,/work with both integer and floating-point operands- The modulus operator
%requires integer operands and returns the remainder of integer division. Valid uses include5 % 3(returns2) and-3 % 2(returns-1per C standards);3 % 2.3is invalid due to non-integer operands.
Division Behavior
- Integer division truncates toward zero:
5 / 3returns1,-5 / 2returns-2 - Floating-point division returns fractional results:
5 / 2.0returns2.5 - To perform floating-point division with integer operands, cast one operand:
(double)3 / 2returns1.5
Increment (++) and Decrement (--) Operators
These unary operators modify a variable by ±1, and require a modifiable lvalue (a variable, not a literal or temporary expression).
- Prefix form:
++var/--var: Update the variable first, then use the new value in the expression. Example:int x=5; int y=++x;setsy=6andx=6 - Postfix form:
var++/var--: Use the current variable value in the exprestion first, then udpate the variable. Example:int x=5; int y=x++;setsy=5andx=6
Invalid usage: 5++ or (a + b)++, as literals and temporary expressions are not modifiable lvalues.
Practice Code Examples
#include <stdio.h>
int main() {
int counter =5;
int total = (counter++) + (counter++) + (counter++);
printf("%d\n", total);
return 0;
}
#include <stdio.h>
int main() {
int idx=5;
printf("%d,%d,%d\n", idx++, idx++, idx++);
return 0;
}
Note: These examples have undefined behavior across compilers, as the order of evaluation for postfix increments and function arguments is not strictly standardized.
3.2 Relational Operators
Relational operators compare numeric values and return 1 (true) or 0 (false) in C. All are binary operators with left-to-right associativity:
- Greater than:
> - Greater than or equal to:
>= - Less than:
< - Less than or equal to:
<= - Equal to:
== - Not equal to:
!=
Expression Behavior
A relational expression returns 1 if the comparison holds, 0 otherwise. For example:
5 >3returns15>7returns0- Chained comparisons like
5>4>3evaluate left-to-right:(5>4)>3→1>3→0, which differs from mathematical notation where5>4>3means5>4 AND 4>3.
3.3 Logical Operators
Logical operators perform boolean logic, treating operands as true (non-zero) or false (zero).
- Logical NOT (
!): Unary operator, inverts the boolean value of the operand - Logical AND (
&&): Binary operator, returns true only if both operands are true - Logical OR (
||): Binary operator, returns true if either operand is true
Truth Table
| a (non-zero=1) | b (non-zero=1) | a && b | a || b | |---|---|---|---| |1|1|1|1| |0|1|0|1| |1|0|0|1| |0|0|0|0|
Short-Circuit Evaluation
C uses short-circuit (lazy) evaluation for logical operators:
- For
a && b && c: Evaluate left to right, stop at the first false operand - For
a || b || c: Evaluate left to right, stop at the first true operand
Example Code
int x=4, y=5;
// Returns 1, both x and y are non-zero
x && y;
// Returns 0, !x is 0
!x && y;
// Evaluates to 0: 5>3 is 1, 8 <4 -!0 →8<3→0, so 1&&0→0
5>3 &&8<4 -!0;
Practical Exercises
- Analyze the output of this program:
#include <stdio.h>
int main() {
int a=1, b=2, c=3, d=4;
int m=1, n=1;
(m =a>b) && (n =c>d);
printf("%d,%d,%d,%d,%d,%d\n",a,b,c,d,m,n);
return 0;
}
- Write a logical expression to check if year
yis a leap year:((y%4 ==0) && (y%100 !=0)) || (y%400 ==0) - Write a logical expression to check if a year is NOT a leap year:
!(((y%4 ==0) && (y%100 !=0)) || (y%400 ==0))
3.4 Bitwise Operators
Bitwise operators manipulate individual bits of integer operands. All except ~ are binary, with left-to-right associativity:
- Bitwise AND:
& - Bitwise OR:
| - Bitwise XOR:
^ - Bitwise NOT:
~ - Left shift:
<< - Right shift:
>>
1. Bitwise NOT (~)
Flips every bit of the operand (0→1, 1→0). Differs from logical NOT !:
!3returns0(logical NOT of non-zero value)~3returns-4on 32-bit signed integers (binary11111111 11111111 11111111 11111100)
Example Code:
#include <stdio.h>
int main() {
int val = ~(-3);
printf("%d\n", val); // Outputs 2
printf("%u\n", val); // Outputs 4294967298
return 0;
}
int main() {
int val = !(-3);
printf("%d\n", val); // Outputs 0
printf("%u\n", val); // Outputs 0
return 0;
}
2. Bitwise AND (&)
Returns 1 only if both corresponding bits are 1, else 0. No carryover occurs.
- Example:
3&5→1(binary0011 &0101=0001) - Use cases: Clear specific bits, extract bit values
- Clear the 5th bit of integer
a:a = a & ~(1<<5) - A bit AND with 0 clears the bit, AND with 1 preserves the bit.
- Clear the 5th bit of integer
####3. Bitwise OR (|) Returns 1 if either corresponding bit is 1, else 0.
- Example:
3|7→7(binary0011|0111=0111) - Use cases: Set specific bits
- Set the 5th bit of integer
a:a =a | (1<<5) - A bit OR with 1 sets the bit, OR with 0 preserves the bit.
- Set the 5th bit of integer
####4. Bitwise XOR (^) Returns 1 if corresponding bits differ, 0 if they match.
- Example:
2^32→34(binary00000010 ^00100000=00100010) - Use cases: Flip bits, swap variables without a temporary
- Flip the 5th bit of
a:a =a ^ (1<<5) - Swap
aandbwithout a temp variable:a =a ^b; b =a ^b; a =a ^b; - A bit XOR with 0 preserves the bit, XOR with 1 flips the bit.
- Flip the 5th bit of
####5. Left Shift (<<)
Shifts all bits left by the specified number of positions. High bits are discarded, low empty bits are filled with 0. If shifted-out high bits are all 0, shifting left by n positions equals multiplying by 2^n.
- Example:
8<<2→32(binary00001000<<2=00100000)
####6. Right Shift (>>) Shifts all bits right by the specified number of positions. Low bits are discarded.
- For unsigned operands: High empty bits are filled with 0
- For signed operands: High empty bits are filled with the original sign bit (arithmetic shift)
- Example: Unsigned
255(11111111) shifted right 2 positions is63(00111111); signed-1shifted right 31 positions is-1on most compilers.
####7. Bit Masks A bit mask is a pattern that selects specific bits from an integer. Bits set to 1 in the mask target the desired positions.
- Example: Select low 8 bits of
x=0x345678ABwith mask0xff:x&0xff→0xAB - Example: Extract bits 5-8 of
x:(x & (0xf<<5)) >>5
###3.5 Assignment Operators Assignment operators assign the right-hand expression's value to a left-hand modifiable lvalue. They have right-to-left associativity and the second-lowest precedence (only the comma operator is lower).
- Basic assignment:
= - Compound assignment operators combine arithmetic/bitwise ops with assignment:
+=,-=,*=,/=,%=,<<=,>>=,&=,|=,^=- Example:
a +=bis equivalent toa =a +b
- Example:
Assignment Expression Value
The value of an assignment expression is the value assigned to the left operand. For example:
x=3has a value of3- Chained assignment:
y=x=3is evaluated asy=(x=3), so bothxandyequal3
###3.6 Conditional Operator (? :)
The only ternary operator in C, with syntax condition ? expr1 : expr2
- Evaluate
condition: if non-zero (true), the expression returnsexpr1; else returnsexpr2 - Example: Replace an if-else block:
// Original if-else if (a>b) x=250; else x=360; // Equivalent conditional expression x =a>b ?250 :360;
###3.7 Comma Operator (,) A binary operator with left-to-right associativity and the lowest precedence. Evaluates the left operand, discards its value, then evaluates the right operand; the entire expression's value is the right operand's value.
- Extended form: Multiple comma-separated expressions:
expr1, expr2, ..., exprn- Evaluates left to right, final value is
exprn
- Evaluates left to right, final value is
- Example:
a=(3,2)assigns2toa;a=3, a=2is a comma expression evaluating to2
###3.8 Pointer Operators Core pointer operators, detailed in a separate section:
- Address-of operator:
& - Dereference operator:
*
###3.9 Sizeof Operator Unary operator that returns the size in bytes of its operand (type or variable).
- Example:
sizeof(int)returns4on most 32/64-bit systems,sizeof(char)returns1
###3.10 Member Operators Access struct and union members:
- Dot operator
.: Direct member access (e.g.,struct_var.member) - Arrow operator
->: Pointer-to-struct member access (e.g.,struct_ptr->member)
###3.11 Subscript Operator ([])
Accesses array elements: array[index] is equivalent to *(array + index)
###3.12 Cast Operator
Converts an expression's value to a specified type: (type)expression
- Example:
(int)3.5returns3,(double)3/2returns1.5, while(double)(3/2)returns1.0