Efficient C Programming Techniques

Efficient programming techniques in C include:

  • Bit Manipulation: Prefer using bit manipulation instead of multiplication and division.

  • Global Variables: Accessing global variables is usually faster than accessing local variables.

  • Pointers: Accessing array elements using pointers is generally faster than using array indices.

  • Algorithm Optimization: Arrange branches and loops logically to reduce unnecessary calculations.

  • Macros: Use macros to reduce the overhead of function calls.

  • Unsigned Integers: Some processors are more efficient when handling unsigned integers.

  • Bit Fields: Use bit fields to save memory.

  • Pointer Initialization: Assign unused pointers a value of<span>NULL</span> to avoid undefined behavior.

  • Decrementing Loops: Decrementing loops are usually more efficient than incrementing loops.

  • Leaf Functions: Use inline functions to reduce the overhead of function calls.

1. Bit Manipulation Instead of Multiplication and Division

Bit manipulation is generally faster than multiplication and division because it directly operates on the CPU’s registers.

(1) Multiplication

  • Left Shift Operator<span><<</span>: Can be used to multiply by powers of 2.

    int result = a << 3; // Equivalent to a * 8

(2) Division

  • Right Shift Operator<span>>></span>: Can be used to divide by powers of 2.

    int result = a >> 3; // Equivalent to a / 8

(3) Modulus Operation

  • Use<span>if</span> statements instead of modulus operations:

uint modulo_func2(uint count) {    if (++count >= 60)        count = 0;    return count;}

2. Global Variables vs Local Variables

(1) Global Variables

  • Global variables are stored in the data segment, and their access speed is usually faster than that of local variables.

  • Global variables are initialized when the program starts, eliminating the need for allocation and deallocation during function calls.

(2) Local Variables

  • Local variables are stored on the stack, and their access speed may be slower than that of global variables.

  • Local variables are allocated during function calls and released when the function returns.

Example Code:

int global_var = 10; // Global variable
void func() {    int local_var = global_var; // Local variable    // Use local_var}

3. Pointers Are Faster Than Array Indices

Using pointers to access array elements is generally faster than using array indices because pointer operations directly map to CPU instructions.

Example Code:

int arr[10];int *ptr = arr;
for (int i = 0; i < 10; i++) {    *ptr = i; // Accessing array elements using pointers    ptr++;}

4. Using Algorithm Calculations

(1) Frequently Used Branches at the Front

  • Placing the most likely executed branches at the front of<span>if-else</span> or <span>switch</span> can reduce jump counts.

Example Code:

if (condition1) {    // Most likely executed branch} else if (condition2) {    // Less frequently executed branch}

(2) Reasonable Nesting Can Improve Speed

  • Reasonable nesting can reduce unnecessary calculations.

Example Code:

if (condition1) {    if (condition2) {        // Execute code    }}

(3) More Iterations in Inner Loops

  • The efficiency of inner loops has a greater impact on overall performance.

Example Code:

for (int i = 0; i < 1000; i++) {    for (int j = 0; j < 1000; j++) {        // Loop body    }}

5. Use of Macros

Macros can reduce the overhead of function calls but may increase code size.

Example Code:

#define SQUARE(x) ((x) * (x))

Notes:

  • Macros are expanded during the preprocessing stage, avoiding the overhead of function calls.

  • Be cautious with parentheses when using macros to avoid operator precedence issues.

6. Unsigned Integers vs Signed Integers

Some processors are more efficient when handling unsigned integers compared to signed integers.

Example Code:

unsigned int a = 10;unsigned int b = 20;unsigned int result = a + b; // Unsigned integer operation

Notes:

  • Unsigned integer operations do not require handling of sign bits, which can make them faster.

  • Integer operations do not require a floating-point unit (FPU) or floating-point operation libraries.

7. Bit Fields

Bit fields can save memory and are suitable for memory-constrained embedded systems.

Example Code:

struct {    unsigned int bit1 : 1;    unsigned int bit2 : 1;    unsigned int bit3 : 1;} bitfields;

Notes:

  • Bit fields define length in bits, which can save memory.

  • Bit fields can only be declared as<span>int</span> or <span>unsigned int</span> types.

  • Bit field variables cannot cross array boundaries and cannot have their addresses taken.

8. Pointers

(1) <span>void *</span> Type Pointer

<span>void *</span> is a generic pointer that can point to any type of data.

Example Code:

void *f1(void *p) {    return p;}
int main() {    int a = 100;    int *pp = &a;    printf("%d\n", *((int *)f1(pp))); // Outputs 100    return 0;}

(2) Character Pointer Initialization

Character pointers can point to string literals but cannot modify their contents.

Example Code:

const char *str = "Hello"; // Character pointer initialization
printf("%s\n", str); // Outputs Hello

(3) Pointer Initialization

  • Non-static local pointers have an indeterminate value if not assigned after declaration.

  • Global and static local pointers are automatically initialized to<span>NULL</span>.

Example Code:

int *ptr; // Non-static local pointer, uninitialized
int global_ptr; // Global pointer, automatically initialized to NULL

Notes:

  • Using uninitialized pointers can lead to program crashes or undefined behavior.

  • Assigning unused pointers a value of<span>NULL</span> is a good practice.

9. Decrementing Loops

Decrementing loops are usually more efficient than incrementing loops because the termination condition is simpler.

Example Code:

for (int i = 10; i--; ) {    // Loop body}

Notes:

  • The termination condition for decrementing loops is<span>i--</span>, which is simpler than<span>i < 10</span> for incrementing loops.

10. Leaf Functions

Leaf functions are functions that do not call any other functions, allowing the compiler to optimize them.

Example Code:

__inline int add(int a, int b) {    return a + b;}

Notes:

  • Inline functions can reduce the overhead of function calls but may increase code size.

Leave a Comment