Skip to main content

Pointers, Arrays & Strings - Part 2

This section covers more advanced string manipulation functions including concatenation, copying with limits, string comparison, and array operations.

Function Prototypes

char *_strcat(char *dest, char *src);
char *_strncat(char *dest, char *src, int n);
char *_strncpy(char *dest, char *src, int n);
int _strcmp(char *s1, char *s2);
void reverse_array(int *a, int n);
char *string_toupper(char *);
char *cap_string(char *);
char *leet(char *);

String Concatenation

_strcat()

Concatenates (appends) one string to another.
char *_strcat(char *dest, char *src)
{
    int a;
    int i;
    a = -1;
    
    for (i = 0; dest[i] != '\0'; i++)
        ;
    
    while (src[++a] != '\0')
    {
        dest[i] = src[a];
        i++;
    }
    
    return (dest);
}
How it works:
  1. Find the end of dest string (first \0)
  2. Copy characters from src to dest starting at that position
  3. The null terminator from src automatically terminates the result
Example:
char dest[20] = "Hello";
char src[] = " World";
_strcat(dest, src);
// Result: dest = "Hello World"
The destination buffer must be large enough to hold both strings plus the null terminator. If dest is too small, buffer overflow occurs!
char dest[6] = "Hello";  // Only room for 6 chars total
char src[] = " World";   // 6 chars + \0
_strcat(dest, src);      // ⚠️ BUFFER OVERFLOW!

_strncat()

Concatenates up to n bytes from source string.
char *_strncat(char *dest, char *src, int n)
{
    int legnth_dest, legnth_src, i;
    for (legnth_dest = 0; dest[legnth_dest] != '\0'; legnth_dest++)
        ;
    for (legnth_src = 0; src[legnth_src] != '\0'; legnth_src++)
        ;
    for (i = 0; i < n && i < legnth_src; i++)
        dest[legnth_dest + i] = src[i];
    dest[legnth_dest + i] = '\0';
    return (dest);
}
Key features:
  • Copies at most n characters
  • Stops early if src is shorter than n
  • Always adds null terminator
Example:
char dest[20] = "Hello";
char src[] = " World";
_strncat(dest, src, 3);
// Result: dest = "Hello Wo"
Unlike strcat, strncat provides protection against copying too much data, but you still need to ensure the destination has enough space.

String Copying with Limits

_strncpy()

Copies up to n characters from source to destination.
char *_strncpy(char *dest, char *src, int n)
{
    int i;
    
    for (i = 0; i < n && src[i] != '\0'; i++)
        dest[i] = src[i];
    for (; i < n; i++)
        dest[i] = '\0';
    return (dest);
}
Important behavior:
  1. Copies characters until n is reached OR null terminator is found
  2. If src is shorter than n, fills remaining space with \0
  3. If src is longer than n, does not add null terminator
Examples:
char dest[10];
char src1[] = "Hi";
char src2[] = "HelloWorld";

_strncpy(dest, src1, 5);
// dest = "Hi\0\0\0" (padded with nulls)

_strncpy(dest, src2, 5);
// dest = "Hello" (NO null terminator!)
If the source string is longer than n, strncpy does NOT null-terminate the destination. Always manually add a null terminator if needed:
_strncpy(dest, src, n);
dest[n - 1] = '\0';  // Ensure null termination

String Comparison

_strcmp()

Compares two strings lexicographically.
int _strcmp(char *s1, char *s2)
{
    int i;
    
    for (i = 0; s1[i] != '\0' && s2[i] != '\0'; i++)
    {
        if (s1[i] != s2[i])
        {
            return (s1[i] - s2[i]);
        }
    }
    return (0);
}
Return values:
  • 0 if strings are equal
  • Positive value if s1 > s2
  • Negative value if s1 < s2
Examples:
_strcmp("abc", "abc");   // Returns 0 (equal)
_strcmp("abc", "abd");   // Returns negative (c < d)
_strcmp("abd", "abc");   // Returns positive (d > c)
_strcmp("ab", "abc");    // Returns negative (shorter)
Comparison is based on ASCII values:
'A' = 65, 'a' = 97
'B' = 66, 'b' = 98
'0' = 48, '9' = 57

Array Operations

reverse_array()

Reverses an array of integers in place.
void reverse_array(int *a, int n)
{
    int i, j, temp;
    
    i = 0;
    j = n - 1;
    
    while (i < j)
    {
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
        i++;
        j--;
    }
}
Algorithm:
  • Use two pointers: one at start, one at end
  • Swap elements and move pointers toward center
  • Stop when pointers meet
Example:
int arr[] = {1, 2, 3, 4, 5};
reverse_array(arr, 5);
// Result: {5, 4, 3, 2, 1}
Visualization:
[1, 2, 3, 4, 5]
 i           j   → Swap: [5, 2, 3, 4, 1]
    i     j      → Swap: [5, 4, 3, 2, 1]
       i,j       → Stop (i >= j)

String Transformation

string_toupper()

Converts all lowercase letters to uppercase.
char *string_toupper(char *s)
{
    int i;
    
    for (i = 0; s[i] != '\0'; i++)
    {
        if (s[i] >= 'a' && s[i] <= 'z')
        {
            s[i] = s[i] - 32;
        }
    }
    return (s);
}
How it works:
  • ASCII value of ‘a’ is 97, ‘A’ is 65
  • Difference is 32
  • Subtracting 32 converts lowercase to uppercase
Example:
char str[] = "Hello World!";
string_toupper(str);
// Result: "HELLO WORLD!"
ASCII conversion table:
'a' (97) - 32 = 'A' (65)
'b' (98) - 32 = 'B' (66)
'z' (122) - 32 = 'Z' (90)
This function modifies the string in place and returns the pointer, allowing for function chaining or immediate use of the result.

Practical Examples

Building a Full Name

char first[50] = "John";
char last[] = " Doe";
_strcat(first, last);
// first = "John Doe"

Safe String Copying

char buffer[10];
char input[] = "This is a very long string";

_strncpy(buffer, input, 9);
buffer[9] = '\0';  // Ensure null termination
// buffer = "This is a"

Case-Insensitive Comparison

char s1[] = "Hello";
char s2[] = "hello";

string_toupper(s1);
string_toupper(s2);

if (_strcmp(s1, s2) == 0)
    printf("Strings are equal (case-insensitive)\n");

Common Pitfalls

1. Buffer Overflow in Concatenation

// ❌ BAD
char dest[10] = "Hello";
char src[] = " World!";
_strcat(dest, src);  // Overflow! Needs 13 bytes, has 10

// ✅ GOOD
char dest[20] = "Hello";
char src[] = " World!";
_strcat(dest, src);  // OK, enough space

2. Forgetting Null Terminator with strncpy

// ❌ BAD
char dest[5];
_strncpy(dest, "HelloWorld", 5);
printf("%s", dest);  // May print garbage

// ✅ GOOD
char dest[5];
_strncpy(dest, "HelloWorld", 4);
dest[4] = '\0';
printf("%s", dest);  // Prints "Hell"

3. Comparing Strings with ==

// ❌ BAD
char *s1 = "Hello";
char *s2 = "Hello";
if (s1 == s2)  // Compares pointers, not strings!

// ✅ GOOD
if (_strcmp(s1, s2) == 0)  // Compares string contents

Performance Considerations

String Length Caching

// Less efficient - recalculates length each iteration
for (int i = 0; i < _strlen(str); i++)
    process(str[i]);

// More efficient - calculates once
int len = _strlen(str);
for (int i = 0; i < len; i++)
    process(str[i]);

Key Takeaways

  • strcat appends strings but requires sufficient buffer space
  • strncat and strncpy limit bytes copied for safety
  • strncpy may not null-terminate if source is too long
  • strcmp returns difference in ASCII values, not just -1/0/1
  • Always allocate enough space for concatenated strings
  • In-place string modifications are efficient but destructive
  • ASCII math enables easy case conversion (±32)

Build docs developers (and LLMs) love