Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

C Language Operators and Expressions

Tech 1

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:

  1. Unary operators
  2. Arithmetic operators
  3. Relational operators
  4. Logical operators
  5. Assignment operators
  6. 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 include 5 % 3 (returns 2) and -3 % 2 (returns -1 per C standards); 3 % 2.3 is invalid due to non-integer operands.

Division Behavior

  • Integer division truncates toward zero: 5 / 3 returns 1, -5 / 2 returns -2
  • Floating-point division returns fractional results: 5 / 2.0 returns 2.5
  • To perform floating-point division with integer operands, cast one operand: (double)3 / 2 returns 1.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; sets y=6 and x=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++; sets y=5 and x=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 >3 returns 1
  • 5>7 returns 0
  • Chained comparisons like 5>4>3 evaluate left-to-right: (5>4)>31>30, which differs from mathematical notation where 5>4>3 means 5>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

  1. 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;
}
  1. Write a logical expression to check if year y is a leap year: ((y%4 ==0) && (y%100 !=0)) || (y%400 ==0)
  2. 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 !:

  • !3 returns 0 (logical NOT of non-zero value)
  • ~3 returns -4 on 32-bit signed integers (binary 11111111 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&51 (binary 0011 &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.

####3. Bitwise OR (|) Returns 1 if either corresponding bit is 1, else 0.

  • Example: 3|77 (binary 0011|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.

####4. Bitwise XOR (^) Returns 1 if corresponding bits differ, 0 if they match.

  • Example: 2^3234 (binary 00000010 ^00100000=00100010)
  • Use cases: Flip bits, swap variables without a temporary
    • Flip the 5th bit of a: a =a ^ (1<<5)
    • Swap a and b without 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.

####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<<232 (binary 00001000<<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 is 63 (00111111); signed -1 shifted right 31 positions is -1 on 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=0x345678AB with mask 0xff: x&0xff0xAB
  • 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 +=b is equivalent to a =a +b

Assignment Expression Value

The value of an assignment expression is the value assigned to the left operand. For example:

  • x=3 has a value of 3
  • Chained assignment: y=x=3 is evaluated as y=(x=3), so both x and y equal 3

###3.6 Conditional Operator (? :) The only ternary operator in C, with syntax condition ? expr1 : expr2

  • Evaluate condition: if non-zero (true), the expression returns expr1; else returns expr2
  • 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
  • Example: a=(3,2) assigns 2 to a; a=3, a=2 is a comma expression evaluating to 2

###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) returns 4 on most 32/64-bit systems, sizeof(char) returns 1

###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.5 returns 3, (double)3/2 returns 1.5, while (double)(3/2) returns 1.0

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.