Skip to main content

Pointers, Arrays & Strings - Part 3

This section covers low-level memory manipulation functions and advanced pointer operations including direct memory access, memory copying, and working with multi-dimensional arrays.

Function Prototypes

char *_memset(char *s, char b, unsigned int n);
char *_memcpy(char *dest, char *src, unsigned int n);
char *_strchr(char *s, char c);
unsigned int _strspn(char *s, char *accept);
char *_strpbrk(char *s, char *accept);
char *_strstr(char *haystack, char *needle);
void print_chessboard(char (*a)[8]);
void print_diagsums(int *a, int size);

Memory Manipulation Functions

These functions operate on raw memory, treating data as bytes rather than specific types.

_memset()

Fills a block of memory with a constant byte value.
char *_memset(char *s, char b, unsigned int n)
{
    unsigned int i;
    
    for (i = 0; i < n; i++)
        s[i] = b;
    
    return (s);
}
Parameters:
  • s - Pointer to memory block
  • b - Byte value to set
  • n - Number of bytes to set
Common use cases:
// Initialize array to zero
int arr[100];
_memset(arr, 0, sizeof(arr));

// Initialize buffer with specific character
char buffer[50];
_memset(buffer, 'A', 50);
// buffer = "AAAAA..." (50 A's)

// Clear sensitive data
char password[20] = "secret123";
_memset(password, 0, sizeof(password));  // Securely erase
memset operates byte-by-byte. For non-char types, use with caution:
int arr[5];
_memset(arr, 1, sizeof(arr));  // Sets each BYTE to 1
// Result: arr = {0x01010101, 0x01010101, ...} (in hex)
//         arr = {16843009, 16843009, ...} (in decimal)

_memcpy()

Copies bytes from source to destination memory block.
char *_memcpy(char *dest, char *src, unsigned int n)
{
    unsigned int i;
    for (i = 0; i < n; i++)
        dest[i] = src[i];
    
    return (dest);
}
Key characteristics:
  • Copies exactly n bytes
  • Does not check for null terminators
  • Works with any data type
  • Faster than string functions for large blocks
Examples:
// Copy array
int src[5] = {1, 2, 3, 4, 5};
int dest[5];
_memcpy(dest, src, sizeof(src));
// dest = {1, 2, 3, 4, 5}

// Copy struct
struct Person {
    char name[20];
    int age;
};

struct Person p1 = {"Alice", 30};
struct Person p2;
_memcpy(&p2, &p1, sizeof(struct Person));
// p2 is exact copy of p1

// Partial string copy (without null terminator check)
char src[] = "Hello World";
char dest[6];
_memcpy(dest, src, 5);
dest[5] = '\0';
// dest = "Hello"
memcpy does NOT handle overlapping memory regions. If source and destination overlap, behavior is undefined!
char str[] = "Hello World";
_memcpy(str + 2, str, 5);  // ⚠️ OVERLAP - undefined behavior!

// Use memmove() for overlapping regions instead

String Searching Functions

_strchr()

Locates the first occurrence of a character in a string.
char *_strchr(char *s, char c)
{
    while (*s != c && *s != '\0')
    {
        s++;
    }
    if (*s == c)
    {
        return (s);
    }
    else
    {
        return (NULL);
    }
}
Returns:
  • Pointer to first occurrence of c
  • NULL if character not found
Examples:
char str[] = "Hello World";
char *ptr;

ptr = _strchr(str, 'W');
if (ptr != NULL)
    printf("Found at position: %ld\n", ptr - str);  // Position 6

ptr = _strchr(str, 'o');
if (ptr != NULL)
    printf("String from first 'o': %s\n", ptr);  // "o World"

ptr = _strchr(str, 'x');
if (ptr == NULL)
    printf("Character not found\n");
Finding file extension:
char filename[] = "document.pdf";
char *ext = _strchr(filename, '.');
if (ext != NULL)
    printf("Extension: %s\n", ext);  // ".pdf"

_strspn()

Calculates the length of the initial segment consisting only of characters from a specified set.
unsigned int _strspn(char *s, char *accept)
{
    int i;
    int j;
    
    for (i = 0; s[i] != '\0'; i++)
    {
        for (j = 0; accept[j] != s[i]; j++)
        {
            if (accept[j] == '\0')
                return (i);
        }
    }
    return (i);
}
Returns: Number of characters in the initial segment of s that consist only of characters from accept. Examples:
char str1[] = "12345abc";
char digits[] = "0123456789";
int len = _strspn(str1, digits);
// len = 5 (stops at 'a')

char str2[] = "   hello";
char whitespace[] = " \t\n";
int spaces = _strspn(str2, whitespace);
// spaces = 3 (number of leading spaces)

char str3[] = "abcdef";
char vowels[] = "aeiou";
int vowel_count = _strspn(str3, vowels);
// vowel_count = 1 (only 'a' at start)
Practical use - parsing:
char input[] = "123.456 meters";
char numbers[] = "0123456789.";
int num_len = _strspn(input, numbers);
char number[20];
_strncpy(number, input, num_len);
number[num_len] = '\0';
// number = "123.456"

_strpbrk()

Locates the first occurrence of any character from a set.
char *_strpbrk(char *s, char *accept);
Searches s for the first occurrence of any character from accept. Example:
char str[] = "Hello, World!";
char punctuation[] = ".,!?";
char *ptr = _strpbrk(str, punctuation);
if (ptr != NULL)
    printf("First punctuation: %c at position %ld\n", *ptr, ptr - str);
// Output: First punctuation: , at position 5

_strstr()

Locates a substring within a string.
char *_strstr(char *haystack, char *needle)
{
    while (*haystack)
    {
        char *Begin = haystack;
        char *pattern = needle;
        
        while (*haystack && *pattern && *haystack == *pattern)
        {
            haystack++;
            pattern++;
        }
        if (!*pattern)
            return (Begin);
        
        haystack = Begin + 1;
    }
    return (NULL);
}
Returns:
  • Pointer to the beginning of the substring
  • NULL if substring not found
Algorithm:
  1. For each position in haystack
  2. Try to match the entire needle
  3. If complete match found, return starting position
  4. Otherwise, move to next position
Examples:
char text[] = "The quick brown fox jumps";
char *ptr;

ptr = _strstr(text, "quick");
if (ptr != NULL)
    printf("Found: %s\n", ptr);  // "quick brown fox jumps"

ptr = _strstr(text, "brown");
if (ptr != NULL)
    printf("Position: %ld\n", ptr - text);  // Position: 10

ptr = _strstr(text, "slow");
if (ptr == NULL)
    printf("Not found\n");
Checking file types:
char filename[] = "report.pdf";
if (_strstr(filename, ".pdf") != NULL)
    printf("This is a PDF file\n");

Multi-Dimensional Arrays

Multi-dimensional arrays are arrays of arrays. Understanding pointer notation is crucial. Demonstrates working with 2D arrays using pointer notation.
void print_chessboard(char (*a)[8])
{
    int i;
    int j;
    
    for (i = 0; i < 8; i++)
    {
        for (j = 0; j < 8; j++)
        {
            _putchar(a[i][j]);
        }
        _putchar('\n');
    }
}
Understanding the syntax:
  • char (*a)[8] - pointer to an array of 8 chars
  • a[i][j] - equivalent to (*(a + i))[j]
  • Each a[i] is an array of 8 characters
Usage example:
char board[8][8] = {
    {'r', 'k', 'b', 'q', 'k', 'b', 'k', 'r'},
    {'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'},
    {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
    {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
    {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
    {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
    {'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'},
    {'R', 'K', 'B', 'Q', 'K', 'B', 'K', 'R'}
};

print_chessboard(board);
Memory layout:
board[0] → [r][k][b][q][k][b][k][r]
board[1] → [p][p][p][p][p][p][p][p]
board[2] → [ ][ ][ ][ ][ ][ ][ ][ ]
...
Different pointer notations for 2D arrays:
int arr[3][4];

// These are different:
int *p = arr;        // ❌ Type mismatch
int (*p)[4] = arr;   // ✓ Pointer to array of 4 ints
int *p = arr[0];     // ✓ Pointer to first element

Advanced Pointer Arithmetic

int matrix[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

// Different ways to access element [1][2] (value 7):
matrix[1][2]              // Standard notation
*(matrix[1] + 2)          // Pointer arithmetic on row
*(*(matrix + 1) + 2)      // Full pointer notation
*(&matrix[0][0] + 1*4 + 2) // Treating as flat array

Pointer to Array vs Array of Pointers

// Pointer to array of 5 ints
int (*ptr1)[5];
int arr[5];
ptr1 = &arr;

// Array of 5 int pointers
int *ptr2[5];
int a=1, b=2, c=3, d=4, e=5;
ptr2[0] = &a;
ptr2[1] = &b;
// etc.

Performance and Memory Considerations

memcpy vs strcpy

// For large strings, memcpy is faster (no null check)
char src[1000] = "large text...";
char dest[1000];

// Faster - knows exact size
_memcpy(dest, src, 1000);

// Slower - checks each byte for null
_strcpy(dest, src);

Memory Initialization

// Zero initialization
int arr[100];

// Method 1: memset (fast for zero)
_memset(arr, 0, sizeof(arr));

// Method 2: loop (flexible but slower)
for (int i = 0; i < 100; i++)
    arr[i] = 0;

// Method 3: at declaration (compiler optimized)
int arr[100] = {0};

Common Pitfalls

1. Overlapping Memory in memcpy

// ❌ BAD - overlapping regions
char str[20] = "Hello";
_memcpy(str + 2, str, 5);  // Undefined behavior

// ✅ GOOD - use temporary buffer
char temp[20];
_memcpy(temp, str, 5);
_memcpy(str + 2, temp, 5);

2. memset with Non-Zero Values for Integers

// ❌ BAD - unexpected results
int arr[10];
_memset(arr, 1, sizeof(arr));  // arr != {1,1,1,...}

// ✅ GOOD - use loop for non-zero
for (int i = 0; i < 10; i++)
    arr[i] = 1;

3. Forgetting NULL Check

// ❌ BAD
char *ptr = _strchr(str, 'x');
printf("%c", *ptr);  // Crash if NULL!

// ✅ GOOD
char *ptr = _strchr(str, 'x');
if (ptr != NULL)
    printf("%c", *ptr);

Key Takeaways

  • memset fills memory with a byte value (use for zero initialization)
  • memcpy copies raw bytes without checking content
  • Never use memcpy on overlapping memory regions
  • String search functions return pointers or NULL
  • strchr finds characters, strstr finds substrings
  • strspn counts matching prefix characters
  • Multi-dimensional arrays use pointer-to-array notation
  • char (*a)[8] is different from char *a[8]
  • Always check return values for NULL before dereferencing
  • Memory functions are faster but less safe than string functions

Build docs developers (and LLMs) love