C Language Crash Course 04: A Comprehensive Analysis of Operators from Basic Operations to Hardware Control

C Language Operators: A Comprehensive Analysis from Basic Operations to Hardware Control

I am Feri. In my 12 years of embedded development, the precise use of operators directly determines the efficiency and stability of the code. The operator system in C is like the gears of a precision instrument; mastering their engagement rules allows programs to run efficiently in different scenarios. This article will take you from the surface of syntax to the essence of operations, helping you understand the computer logic behind each operator.

1. Arithmetic Operators: The Cornerstone of Numerical Calculations

1.1 Basic Four Arithmetic Operations and Modulus

Operator Name Operation Rules Typical Error Cases
<span>+</span> Addition Integer/float addition, array index operation (<span>arr[i+1]</span>) <span>i++ + j++</span>undefined evaluation order
<span>-</span> Subtraction/Negation Supports unary (<span>-x</span>) and binary operations, pointer address offset (<span>p1 - p2</span> calculates the number of elements) Unsigned integer subtraction overflow is undefined behavior
<span>*</span> Multiplication Supports large number operations (be cautious of overflow, e.g.,<span>int</span> multiplied by<span>int</span> may exceed range) Misunderstanding<span>0.1*0.2</span> is exactly equal to 0.02 (floating-point error)
<span>/</span> Division Integer division truncates decimals (<span>5/2=2</span>), floating-point division retains precision (<span>5.0/2=2.5</span>) Integer division by zero causes program crash (runtime error)
<span>%</span> Modulus Result sign is consistent with the dividend (<span>-7%3=-1</span>,<span>7%-3=1</span>), applicable only to integer operations Using<span>%</span> on floating-point numbers (compilation error)
// Common embedded technique: Optimize code using division
#define DIV_ROUND_UP(n, d) ((n + d - 1) / d)  // Round up division
int bytes = DIV_ROUND_UP(file_size, 512);      // Calculate number of 512-byte blocks

1.2 Feri’s Tip: Risks of Integer Overflow

  • Unsigned integer overflow wraps around (<span>0xFFFFFFFF + 1 = 0</span>)
  • Signed integer overflow is undefined behavior (may cause program crash)
  • In critical calculation scenarios, it is recommended to use<span>int_least32_t</span> and other fixed-width types

2. Relational Operators: The Core of Conditional Judgments

2.1 The Essence and Traps of Six Relational Operators

if (a = b)  // Dangerous! Mistakenly wrote assignment `=` instead of comparison `==`
if (1 == a)  // Defensive coding: Avoid null pointer dereference (if a is NULL, `a == 1` will directly error)
  • Boolean Value Conversion: Non-zero values are considered true (1), zero is considered false (0)
  • Floating-point Comparison: Direct comparison using<span>==</span> is prohibited (e.g.,<span>0.1 == 0.10000000001</span> may be false), should use an error range:
    #define EPS 1e-8
    if (fabs(a - b) < EPS)  // Correct floating-point equality check
    

2.2 Best Practices: Avoid Magic Numbers

// Bad code: Directly comparing numbers
if (status == 0)

// Good code: Use enumeration to enhance readability
enum Status { ERROR = 0, OK = 1 };
if (status == OK)

3. Logical Operators: The Steering Wheel of Control Flow

3.1 The Double-Edged Sword of Short-Circuiting

  • <span>&&</span> Logical AND: If the left operand is false, the right operand is not evaluated (protects against null pointer dereference)
    if (ptr != NULL && ptr->value > 0)  // Safe pointer value check
    
  • <span>||</span> Logical OR: If the left operand is true, the right operand is not evaluated (optimizes condition checks)
    if (ret == OK || retry_count-- > 0)  // Reduces unnecessary retry calculations
    
  • <span>!</span> Logical NOT: Negates non-boolean values (<span>!0=1</span>,<span>!5=0</span>)

3.2 The Parentheses Principle for Compound Conditions

// Clear writing: Use parentheses to clarify precedence
if ((a > b) && (c < d))

// Dangerous writing: Relying on operator precedence (prone to errors)
if (a > b && c < d)  // Equivalent to the former, but less readable

4. Bitwise Operators: The Ultimate Weapon for Hardware Control

4.1 A Surgical Knife for Data at the Binary Level

Operator Name Binary Rules Typical Embedded Applications
<span>&</span> Bitwise AND 1 if both are 1, otherwise 0 (used for mask extraction) <span>val & 0x0F</span> to get the low 4 bits
` ` Bitwise OR 1 if either is 1, otherwise 0 (used for mask setting)
<span>^</span> Bitwise XOR 1 if different, 0 if the same (used for state toggling) <span>led ^= 1</span> to toggle LED state
<span>~</span> Bitwise NOT 0 becomes 1, 1 becomes 0 (used with masks to clear specific bits) <span>val = ~val & 0x0F</span> to clear the high 4 bits
<span><<</span> Left Shift Shifting left by n bits is equivalent to multiplying by 2^n (without overflow) <span>speed << 1</span> to double the speed
<span>>></span> Right Shift Unsigned numbers fill with 0, signed numbers fill with the sign bit <span>data >> 2</span> to right shift binary by 2 bits (divide by 4)

4.2 Classic Bit Manipulation Techniques

// Check odd/even: Use the least significant bit
#define IS_ODD(x) ((x) & 1)

// Fast zeroing: `x & (x-1)` clears the rightmost 1
int count_ones(int x) {
    int cnt = 0;
    while (x) { x &= x-1; cnt++; }
    return cnt;  // Count the number of 1s in binary
}

5. Assignment Operators: The Channel of Data Flow

5.1 Efficiency Advantages of Compound Assignment

// Equivalent writing, but compound assignment is more efficient (reduces instructions generated by the compiler)
x = x + y;
x += y;

// Array element assignment: Combined with pointer operations
int arr[5] = {1,2,3};
int *p = arr;
*p++ = 10;  // Assign first, then increment pointer (equivalent to p = p + 1)

5.2 Dangerous Assignment: Uninitialized Variables

int x;
x = x + 1;  // The value of uninitialized variable x is random, result is unpredictable

6. Ternary Operator: Balancing Conciseness and Readability

6.1 Correct Usage Scenarios for Syntax Sugar

// Simple condition check (recommended)
int max = a > b ? a : b;

// Complex scenarios (not recommended, affects readability)
result = condition1 ? (condition2 ? x : y) : (condition3 ? z : w);

// Optimization: Split into if-else statements
if (condition1) {
    result = condition2 ? x : y;
} else {
    result = condition3 ? z : w;
}

6.2 Type Conversion Rules

The two branch expressions of the ternary operator must be compatible types, otherwise automatic type promotion will occur:

int x = 10;
float y = x > 5 ? 2.5 : 3;  // The second branch is automatically promoted to float type

7. sizeof: The Memory Probe at Compile Time

7.1 Memory Size Table for Data Types

// Output the byte size of common types (example for 32-bit system)
printf("sizeof(char)=%zu\n", sizeof(char));        // 1
printf("sizeof(int)=%zu\n", sizeof(int));          // 4
printf("sizeof(long long)=%zu\n", sizeof(long long));// 8
printf("sizeof(void*)=%zu\n", sizeof(void*));      // 4 (pointer size related to address bus width)

7.2 Differences Between Arrays and Pointers

char str[] = "feri";
char *p = str;
printf("sizeof(str)=%zu\n", sizeof(str));  // 5 (including the ending \0)
printf("sizeof(p)=%zu\n", sizeof(p));      // 4 (pointer size, unrelated to array length)

7.3 Feri’s Tip: Essential Techniques for Embedded Systems

  • Calculate array length:<span>#define LEN(arr) (sizeof(arr)/sizeof((arr)[0]))</span>
  • Avoid runtime overhead:<span>sizeof</span> is calculated at compile time, result is<span>size_t</span> type (unsigned integer)

8. Operator Precedence: The Rulebook to Avoid Ambiguity

8.1 The TOP 5 Precedences You Must Master

Precedence Category Operator Associativity Memory Mnemonic
1 Postfix <span>()</span><span>[]</span><span>-></span><span>.</span> Left associative Parentheses, array, pointer, dot
2 Unary <span>!</span><span>~</span><span>++</span><span>--</span><span>-</span><span>sizeof</span> Right associative Not, complement, increment, decrement, address
3 Arithmetic (Multiplication/Division) <span>*</span><span>/</span><span>%</span> Left associative Multiply, divide, modulus
4 Arithmetic (Addition/Subtraction) <span>+</span><span>-</span> Left associative Add, subtract
5 Relational <span>></span><span><</span><span>>=</span><span><=</span> Left associative Greater, less
6 Equality <span>==</span><span>!=</span> Left associative Equal, not equal
7 Logical AND <span>&&</span> Left associative Logical AND
8 Assignment <span>=</span><span>+=</span><span>-=</span> and other compound assignments Right associative Assignment

Always remember: Add parentheses when uncertain about precedence!

9. Practical Suggestions: Writing Safe and Efficient Expressions

  1. Avoid Side Effects Stacking:

    // Bad code: i++ + ++i has undefined evaluation order (results vary across compilers)
    // Good code: Split into two steps
    i++; j = i; k = j + i;
    
  2. Prevent Errors in Floating-Point Calculations:

  • Use<span>double</span> instead of<span>float</span><code> to improve precision
  • Check if the divisor is zero before critical calculations
  • Three Principles of Bit Manipulation:

    • Read the current value before operating on registers (<span>reg = (reg & ~MASK) | NEW_VALUE</span>)
    • Define bit masks using enumerations (<span>enum Flag { FLAG_A = 1<<0, FLAG_B=1<<1 };</span>)
    • Avoid bitwise operations on boolean values (e.g.,<span>flag & FLAG_A</span> should be changed to<span>flag & FLAG_A != 0</span>)

    Operators are the instruction set for dialogue between C language and computer hardware. When you use the<span>&</span> operator, you are essentially manipulating the CPU’s logic unit; when you write<span><<</span>, you are directing the binary bits in memory to migrate. Mastering these operators is the core ability to write efficient and stable code. In the next article, we will enter the world of control flow and learn how to build complex logical structures using these operators. Follow me to unlock the underlying control power of C language!

    // Philosophical reflection on operators:
    #define TRUE 1
    #define FALSE 0
    int is_perfect(int x) {
        return x > 0 && (x & (x-1)) == 0;  // Use bitwise operation to determine if x is a power of 2
    }
    

    Leave a Comment