Docs

Character Arrays and Strings

Character Arrays and Strings in C

Table of Contents

  1. •Introduction
  2. •Character Arrays
  3. •Strings in C
  4. •String Initialization
  5. •String Input/Output
  6. •String Library Functions
  7. •Common String Operations
  8. •Array of Strings
  9. •Common Pitfalls
  10. •Best Practices
  11. •Summary

Introduction

In C, there is no built-in string type like in other languages. Instead, strings are implemented as arrays of characters terminated by a special null character \0.

Understanding character arrays is crucial because:

  • •Text processing is fundamental to most programs
  • •Many security vulnerabilities come from improper string handling
  • •String manipulation requires manual memory management

Character Arrays

Declaration and Initialization

// Character array (not a string - no null terminator)
char letters[5] = {'H', 'e', 'l', 'l', 'o'};

// Memory layout:
// Index:   [0]   [1]   [2]   [3]   [4]
// Value:   'H'   'e'   'l'   'l'   'o'
// ASCII:    72   101   108   108   111

Character vs Integer

char ch = 'A';   // Stores ASCII value 65
char num = 65;   // Also stores 65 (same as 'A')

printf("%c\n", ch);   // Output: A
printf("%d\n", ch);   // Output: 65

ASCII Table (Common Values)

Character    ASCII Value
───────────────────────
'0' - '9'    48 - 57
'A' - 'Z'    65 - 90
'a' - 'z'    97 - 122
' '          32
'\n'         10
'\0'         0

Strings in C

What Makes a String?

A string in C is a character array that ends with the null terminator \0.

char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

// Memory layout:
// Index:   [0]   [1]   [2]   [3]   [4]   [5]
// Value:   'H'   'e'   'l'   'l'   'o'  '\0'
//                                         ↑
//                               Null terminator (required!)

Why Null Terminator?

Without \0, functions don't know where string ends!

char text[10] = "Hi";

Memory might look like:
[0]  [1]  [2]  [3]  [4]  [5]  [6]  [7]  [8]  [9]
'H'  'i'  \0   ???  ???  ???  ???  ???  ???  ???
           ↑
    String ends here (functions stop reading)

String Length vs Array Size

char str[10] = "Hello";

// Array size: 10 bytes (allocated)
// String length: 5 (characters before \0)
// Null terminator at: index 5

sizeof(str)     → 10  (total array size)
strlen(str)     → 5   (number of characters)

String Initialization

Method 1: String Literal

char str1[] = "Hello";       // Size = 6 (includes \0)
char str2[10] = "Hello";     // Size = 10, str ends at index 5
char str3[3] = "Hello";      // WARNING! Only "Hel" stored, no \0

Method 2: Character by Character

char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

Method 3: Pointer to String Literal

char *str = "Hello";    // Points to string in read-only memory

// WARNING: Cannot modify string literals!
// str[0] = 'h';  // UNDEFINED BEHAVIOR!

Comparison of Methods

Declaration          Modifiable?   Location
─────────────────────────────────────────────
char str[] = "Hi"    Yes          Stack
char str[10] = "Hi"  Yes          Stack
char *str = "Hi"     NO!          Read-only memory

Visual: String in Memory

char name[10] = "Alice";

Stack Memory:
Address:  1000  1001  1002  1003  1004  1005  1006  1007  1008  1009
Value:    'A'   'l'   'i'   'c'   'e'   '\0'   ?     ?     ?     ?
Index:    [0]   [1]   [2]   [3]   [4]   [5]   [6]   [7]   [8]   [9]
                                         ↑
                              String ends here

String Input/Output

Output with printf

char name[] = "Alice";

printf("%s\n", name);        // Output: Alice
printf("Hello, %s!\n", name); // Output: Hello, Alice!
printf("%.3s\n", name);      // Output: Ali (first 3 chars)
printf("%10s\n", name);      // Right-aligned in 10 chars
printf("%-10s\n", name);     // Left-aligned in 10 chars

Output with puts

char str[] = "Hello";
puts(str);  // Prints "Hello" followed by newline

Input with scanf

char name[50];
scanf("%s", name);   // Note: NO & needed for arrays!

// WARNING: scanf stops at whitespace!
// Input: "John Doe" → name contains only "John"

Input with fgets (Safer)

char line[100];
fgets(line, sizeof(line), stdin);

// Reads entire line including spaces
// Includes newline character if room permits
// Maximum 99 characters + null terminator

Removing Newline from fgets

char line[100];
fgets(line, sizeof(line), stdin);

// Remove trailing newline
size_t len = strlen(line);
if (len > 0 && line[len - 1] == '\n') {
    line[len - 1] = '\0';
}

Character Input with getchar

int ch;
while ((ch = getchar()) != '\n' && ch != EOF) {
    // Process character
    putchar(ch);
}

String Library Functions

Include <string.h> for these functions.

strlen - String Length

char str[] = "Hello";
size_t len = strlen(str);  // Returns 5 (not counting \0)

strcpy - String Copy

char src[] = "Hello";
char dest[10];

strcpy(dest, src);  // Copies "Hello\0" to dest

// WARNING: dest must be large enough!

strncpy - Safe Copy

char src[] = "Hello World";
char dest[6];

strncpy(dest, src, 5);  // Copy at most 5 chars
dest[5] = '\0';         // Ensure null termination

// dest now contains "Hello"

strcat - Concatenate

char str[20] = "Hello";
strcat(str, " World");  // str = "Hello World"

// WARNING: Destination must have enough space!

strncat - Safe Concatenate

char str[15] = "Hello";
strncat(str, " World!", 6);  // Append at most 6 chars
// str = "Hello World"

strcmp - Compare Strings

char a[] = "apple";
char b[] = "banana";

int result = strcmp(a, b);
// result < 0: a comes before b
// result = 0: strings are equal
// result > 0: a comes after b

strncmp - Compare N Characters

char a[] = "Hello World";
char b[] = "Hello There";

int result = strncmp(a, b, 5);  // Compare first 5 chars
// result = 0 (first 5 chars are equal)

strchr - Find Character

char str[] = "Hello World";
char *p = strchr(str, 'W');  // Find first 'W'

if (p != NULL) {
    printf("Found at index: %ld\n", p - str);  // Output: 6
}

strstr - Find Substring

char str[] = "Hello World";
char *p = strstr(str, "Wor");

if (p != NULL) {
    printf("Found: %s\n", p);  // Output: World
}

Quick Reference Table

Function    Purpose                 Returns
─────────────────────────────────────────────
strlen      Length of string        size_t
strcpy      Copy string             char*
strncpy     Copy n characters       char*
strcat      Concatenate             char*
strncat     Concatenate n chars     char*
strcmp      Compare strings         int
strncmp     Compare n chars         int
strchr      Find character          char* or NULL
strstr      Find substring          char* or NULL

Common String Operations

Checking if String is Empty

char str[100];
fgets(str, sizeof(str), stdin);

if (str[0] == '\0' || str[0] == '\n') {
    printf("Empty input\n");
}

Converting Case

#include <ctype.h>

void toUpperCase(char str[]) {
    for (int i = 0; str[i] != '\0'; i++) {
        str[i] = toupper(str[i]);
    }
}

void toLowerCase(char str[]) {
    for (int i = 0; str[i] != '\0'; i++) {
        str[i] = tolower(str[i]);
    }
}

Reversing a String

void reverseString(char str[]) {
    int len = strlen(str);
    for (int i = 0; i < len / 2; i++) {
        char temp = str[i];
        str[i] = str[len - 1 - i];
        str[len - 1 - i] = temp;
    }
}

Counting Words

int countWords(const char str[]) {
    int count = 0;
    int inWord = 0;

    for (int i = 0; str[i] != '\0'; i++) {
        if (str[i] == ' ' || str[i] == '\n' || str[i] == '\t') {
            inWord = 0;
        } else if (!inWord) {
            inWord = 1;
            count++;
        }
    }
    return count;
}

Checking Character Types

#include <ctype.h>

// These functions return non-zero if true, 0 if false
isalpha(c)   // Is letter (a-z, A-Z)?
isdigit(c)   // Is digit (0-9)?
isalnum(c)   // Is letter or digit?
isspace(c)   // Is whitespace?
isupper(c)   // Is uppercase?
islower(c)   // Is lowercase?
ispunct(c)   // Is punctuation?

Array of Strings

Method 1: 2D Character Array

char names[3][20] = {
    "Alice",
    "Bob",
    "Charlie"
};

// Memory: Fixed 20 bytes per string (wasted space)

// Access:
printf("%s\n", names[0]);  // Alice
printf("%c\n", names[1][0]); // B

Method 2: Array of Pointers

char *names[] = {
    "Alice",
    "Bob",
    "Charlie"
};

// Memory: Only pointer size per element
// Strings stored in read-only memory

// Access:
printf("%s\n", names[0]);  // Alice
printf("%c\n", names[1][0]); // B

// WARNING: Cannot modify string literals!

Visual Comparison

2D Array (char names[3][20]):
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│ A l i c e \0 . . .   │  ← 20 bytes each
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ B o b \0 . . . . .   │  ← Wasted space
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│ C h a r l i e \0 .   │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
Total: 60 bytes

Array of Pointers (char *names[]):
ā”Œā”€ā”€ā”€ā”€ā”€ā”     ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  ──────→  │ Alice\0  │  (in read-only memory)
ā”œā”€ā”€ā”€ā”€ā”€ā”¤     ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
│  ──────→  │ Bob\0    │
ā”œā”€ā”€ā”€ā”€ā”€ā”¤     ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
│  ──────→  │ Charlie\0│
ā””ā”€ā”€ā”€ā”€ā”€ā”˜     ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
Pointers: 24 bytes (on 64-bit)
Strings: Variable size

Sorting Array of Strings

void sortStrings(char *arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (strcmp(arr[j], arr[j + 1]) > 0) {
                char *temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

Common Pitfalls

1. Forgetting Null Terminator

// WRONG
char str[5] = "Hello";  // No room for \0!

// CORRECT
char str[6] = "Hello";  // 5 chars + \0

2. Buffer Overflow

char small[5];
strcpy(small, "This is too long!");  // DANGER! Buffer overflow

// Use strncpy instead
strncpy(small, "This is too long!", 4);
small[4] = '\0';

3. Comparing Strings with ==

char a[] = "Hello";
char b[] = "Hello";

// WRONG
if (a == b)  // Compares addresses, not content!

// CORRECT
if (strcmp(a, b) == 0)  // Compares content

4. Modifying String Literals

char *str = "Hello";
str[0] = 'h';  // UNDEFINED BEHAVIOR!

// Use array instead
char str[] = "Hello";
str[0] = 'h';  // OK

5. Uninitialized Strings

char str[100];
printf("%s\n", str);  // UNDEFINED! Contains garbage

// Initialize properly
char str[100] = "";  // or = {0}

6. scanf Without Size Limit

char input[10];
scanf("%s", input);  // Dangerous if input > 9 chars

// Safer
scanf("%9s", input);  // Read at most 9 characters

Best Practices

1. Always Allocate Space for \0

// If you need to store 10 characters, allocate 11
char str[11];  // 10 chars + null terminator

2. Use Safe String Functions

// Instead of strcpy, use strncpy
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';

// Instead of strcat, use strncat
strncat(dest, src, sizeof(dest) - strlen(dest) - 1);

3. Prefer fgets Over scanf for Strings

// Bad
scanf("%s", input);

// Good
fgets(input, sizeof(input), stdin);

4. Check Return Values

char *result = strchr(str, 'x');
if (result != NULL) {
    // Found it
} else {
    // Not found
}

5. Use const for Read-Only Strings

void printString(const char *str) {
    printf("%s\n", str);
    // str[0] = 'x';  // Error: cannot modify const
}

6. Initialize Strings

char str[100] = "";    // Initialize to empty string
char str[100] = {0};   // Initialize all bytes to 0

Summary

Key Concepts

Concept                 Description
─────────────────────────────────────────────────
Null Terminator        '\0' marks end of string
String Length          Characters before \0
Array Size             Total allocated bytes
String Literal         "text" in quotes (read-only)
Character Array        char[] can be modified
char pointer           char* to literal is read-only

Essential Functions

#include <string.h>

strlen(s)              // Length of string
strcpy(d, s)           // Copy s to d
strncpy(d, s, n)       // Copy up to n chars
strcat(d, s)           // Append s to d
strncat(d, s, n)       // Append up to n chars
strcmp(a, b)           // Compare strings
strncmp(a, b, n)       // Compare n chars
strchr(s, c)           // Find char in string
strstr(s, sub)         // Find substring

Memory Rules

āœ“ char str[] = "Hi";     → Modifiable, on stack
āœ— char *str = "Hi";      → NOT modifiable (literal)
āœ“ char str[10] = "Hi";   → Modifiable, fixed size
āœ“ Always leave room for \0
āœ“ Use strn* functions for safety

Next Steps

After mastering character arrays and strings:

  1. •Learn about Pointers for deeper string manipulation
  2. •Study Dynamic Memory for variable-length strings
  3. •Explore String Algorithms (parsing, tokenizing)

"A string in C is just an array with a promise - the promise of a null terminator."

Character Arrays And Strings - C Programming Tutorial | DeepML