Docs
README
Character Arrays and Strings in C
Table of Contents
- ā¢Introduction
- ā¢Character Arrays
- ā¢Strings in C
- ā¢String Initialization
- ā¢String Input/Output
- ā¢String Library Functions
- ā¢Common String Operations
- ā¢Array of Strings
- ā¢Common Pitfalls
- ā¢Best Practices
- ā¢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:
- ā¢Learn about Pointers for deeper string manipulation
- ā¢Study Dynamic Memory for variable-length strings
- ā¢Explore String Algorithms (parsing, tokenizing)
"A string in C is just an array with a promise - the promise of a null terminator."