C Programming Techniques: Pointer Arithmetic, Array Operations, and String Processing

Locating Array Extrema Using Pointer Parameters

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define SIZE 5

void populate(int arr[], int len);
void display(const int arr[], int len);
void compute_bounds(const int arr[], int len, int* smallest, int* largest);

int main(void) {
    int values[SIZE];
    int minimum, maximum;
    
    printf("Enter %d integers:\n", SIZE);
    populate(values, SIZE);
    
    printf("Array contents: ");
    display(values, SIZE);
    
    compute_bounds(values, SIZE, &minimum, &maximum);
    
    printf("Range: %d to %d\n", minimum, maximum);
    return 0;
}

void populate(int arr[], int len) {
    for (int i = 0; i < len; i++) {
        scanf("%d", &arr[i]);
    }
}

void display(const int arr[], int len) {
    for (int i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

void compute_bounds(const int arr[], int len, int* smallest, int* largest) {
    *smallest = *largest = arr[0];
    for (int i = 1; i < len; i++) {
        if (arr[i] < *smallest) {
            *smallest = arr[i];
        } else if (arr[i] > *largest) {
            *largest = arr[i];
        }
    }
}

The compute_bounds function receives two pointer arguments to modify the caller's variables directly, demonstrating pass-by-reference semantics in C.

Returning Pointers to Array Elements

#include <stdio.h>
#define SIZE 5

void read_data(int buf[], int n);
int* locate_peak(int buf[], int n);

int main(void) {
    int dataset[SIZE];
    int* peak_ref;
    
    printf("Input %d values:\n", SIZE);
    read_data(dataset, SIZE);
    
    peak_ref = locate_peak(dataset, SIZE);
    printf("Maximum value: %d\n", *peak_ref);
    
    return 0;
}

void read_data(int buf[], int n) {
    for (int i = 0; i < n; i++) {
        scanf("%d", &buf[i]);
    }
}

int* locate_peak(int buf[], int n) {
    int peak_pos = 0;
    for (int i = 1; i < n; i++) {
        if (buf[i] > buf[peak_pos]) {
            peak_pos = i;
        }
    }
    return &buf[peak_pos];
}

This approach returns the memory address of the maximum element rather than its value, allowing the caller to access or modify the original array location.

Character Arrays vs. Character Pointers

Consider the distinction between array allocation and pointer initialization:

#include <stdio.h>
#include <string.h>
#define BUFFER 80

int main(void) {
    char buffer1[BUFFER] = "Programming is fascinating";
    char buffer2[BUFFER] = "Programming is exhausting";
    char temp[BUFFER];
    
    printf("Memory analysis:\n");
    printf("Size of buffer1: %zu bytes\n", sizeof(buffer1));
    printf("String length: %zu characters\n", strlen(buffer1));
    
    printf("\nBefore exchange:\n");
    printf("A: %s\n", buffer1);
    printf("B: %s\n", buffer2);
    
    strcpy(temp, buffer1);
    strcpy(buffer1, buffer2);
    strcpy(buffer2, temp);
    
    printf("\nAfter exchange:\n");
    printf("A: %s\n", buffer1);
    printf("B: %s\n", buffer2);
    
    return 0;
}

Contrast this with pointer manipulation:

#include <stdio.h>
#include <string.h>

int main(void) {
    const char* phrase1 = "Knowledge is power";
    const char* phrase2 = "Ignorance is bliss";
    const char* swap;
    
    printf("Pointer size: %zu bytes\n", sizeof(phrase1));
    printf("Content length: %zu\n", strlen(phrase1));
    
    printf("\nOriginal assignments:\n");
    printf("Ptr1 -> %s\n", phrase1);
    printf("Ptr2 -> %s\n", phrase2);
    
    swap = phrase1;
    phrase1 = phrase2;
    phrase2 = swap;
    
    printf("\nSwapped references:\n");
    printf("Ptr1 -> %s\n", phrase1);
    printf("Ptr2 -> %s\n", phrase2);
    
    return 0;
}

Key observations:

  • sizeof on an array yields the total allocated bytes (80), while on a pointer it yields the address width (4 or 8 bytes)
  • Array assignment copies content; pointer assignment changes reference
  • String literals reside in read-only memory; attempting modification through pointers causes undefined behavior

Accessing Two-Dimensional Arrays

#include <stdio.h>

int main(void) {
    int matrix[2][4] = {{1, 9, 8, 4}, {2, 0, 4, 9}};
    int row, col;
    
    printf("Standard indexing:\n");
    for (row = 0; row < 2; row++) {
        for (col = 0; col < 4; col++) {
            printf("%d ", matrix[row][col]);
        }
        printf("\n");
    }
    
    printf("\nLinear pointer arithmetic:\n");
    int* element = &matrix[0][0];
    for (int i = 0; i < 8; i++) {
        printf("%d ", *element++);
        if ((i + 1) % 4 == 0) printf("\n");
    }
    
    printf("\nRow-wise pointer access:\n");
    int (*row_ptr)[4] = matrix;
    for (; row_ptr < matrix + 2; row_ptr++) {
        for (col = 0; col < 4; col++) {
            printf("%d ", (*row_ptr)[col]);
        }
        printf("\n");
    }
    
    return 0;
}

The declaration int (*row_ptr)[4] establishes a pointer to an array of four integers, enabling row-by-row navigation through the matrix.

In-Place Character Substitution

#include <stdio.h>
#define CAPACITY 80

void transform(char* sequence, char target, char replacement);

int main(void) {
    char sentence[CAPACITY] = "Debugging is twice as hard as writing the code.";
    
    printf("Original: %s\n", sentence);
    transform(sentence, 'e', '#');
    printf("Modified: %s\n", sentence);
    
    return 0;
}

void transform(char* sequence, char target, char replacement) {
    while (*sequence) {
        if (*sequence == target) {
            *sequence = replacement;
        }
        sequence++;
    }
}

This implementation iterates until the null terminator, directly dereferencing the pointer to modify the original character array.

Conditional String Termination

#include <stdio.h>
#define LIMIT 80

char* terminate_on(char* text, char delimiter);

int main(void) {
    char input[LIMIT];
    char marker;
    
    while (printf("Enter text: "), gets(input) != NULL) {
        printf("Enter delimiter: ");
        marker = getchar();
        
        terminate_on(input, marker);
        printf("Result: %s\n\n", input);
        
        getchar();
    }
    return 0;
}

char* terminate_on(char* text, char delimiter) {
    char* cursor = text;
    while (*cursor) {
        if (*cursor == delimiter) {
            *cursor = '\0';
            return text;
        }
        cursor++;
    }
    return text;
}

The function truncates the string at the first occurrence of the specified delimiter by inserting a null character. The getchar() call after processing clears the input buffer to prevent immediate loop termination on subsequent iterations.

Format Validation for Identification Codes

#include <stdio.h>
#include <string.h>
#define RECORDS 5

int validate_format(const char* code);

int main(void) {
    const char* ids[RECORDS] = {
        "31010120000721656X",
        "3301061996X0203301",
        "53010220051126571",
        "510104199211197977",
        "53010220051126133Y"
    };
    
    for (int i = 0; i < RECORDS; i++) {
        printf("%s\t%s\n", ids[i], 
               validate_format(ids[i]) ? "Valid" : "Invalid");
    }
    return 0;
}

int validate_format(const char* code) {
    if (strlen(code) != 18) return 0;
    
    for (int i = 0; i < 17; i++) {
        if (code[i] < '0' || code[i] > '9') {
            return 0;
        }
    }
    
    char last = code[17];
    if (!((last >= '0' && last <= '9') || last == 'X')) {
        return 0;
    }
    return 1;
}

Circular Character Shifting

#include <stdio.h>
#define CAPACITY 80

void shift_forward(char* text, int offset);
void shift_backward(char* text, int offset);

int main(void) {
    char message[CAPACITY];
    int shift;
    
    printf("Enter message: ");
    gets(message);
    printf("Shift amount: ");
    scanf("%d", &shift);
    
    shift_forward(message, shift);
    printf("Encoded: %s\n", message);
    
    shift_backward(message, shift);
    printf("Decoded: %s\n", message);
    
    return 0;
}

void shift_forward(char* text, int offset) {
    for (int i = 0; text[i]; i++) {
        char c = text[i];
        if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
            char base = (c >= 'a') ? 'a' : 'A';
            text[i] = base + (c - base + offset) % 26;
        }
    }
}

void shift_backward(char* text, int offset) {
    for (int i = 0; text[i]; i++) {
        char c = text[i];
        if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
            char base = (c >= 'a') ? 'a' : 'A';
            text[i] = base + (c - base - offset + 26) % 26;
        }
    }
}

Both functions preserve case and ignore non-alphabetic characters. The modulo operation ensures proper wrapping around the alphabet boundaries.

Sorting Command-Line Arguments

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int string_compare(const void* first, const void* second);

int main(int argc, char* argv[]) {
    if (argc < 2) return 0;
    
    qsort(argv + 1, argc - 1, sizeof(char*), string_compare);
    
    for (int i = 1; i < argc; i++) {
        printf("Greetings, %s\n", argv[i]);
    }
    return 0;
}

int string_compare(const void* first, const void* second) {
    const char** str1 = (const char**)first;
    const char** str2 = (const char**)second;
    return strcmp(*str1, *str2);
}

The comparison function casts the void pointers to pointer-to-pointer types since argv contains addresses of character strings. The qsort invocation skips the program name (argv[0]) to sort only the user-provided arguments.

Tags: C pointers Arrays String Manipulation Memory Management

Posted on Fri, 04 Sep 2026 16:22:57 +0000 by Dj Kat