Strings are fundamental to almost every C program, from simple console applications to complex systems software. Unlike higher-level languages, C treats strings as arrays of characters with a special terminating null character. This low-level approach gives programmers incredible control but also demands careful management. This comprehensive guide explores every aspect of C strings, from basic concepts to advanced manipulation techniques.
What is a String in C?
In C, a string is a sequence of characters terminated by a null character ('\0'). Strings are stored in arrays of char and must always be null-terminated.
#include <stdio.h>
int main() {
// String literals (automatically null-terminated)
char str1[] = "Hello"; // Array of 6 characters: H,e,l,l,o,\0
char str2[10] = "World"; // Array of 10 chars: W,o,r,l,d,\0,?,?,?,?
char *str3 = "Pointer string"; // Pointer to string literal
// Character array initialized explicitly
char str4[] = {'C', ' ', 'S', 't', 'r', 'i', 'n', 'g', '\0'};
printf("%s\n", str1);
printf("%s\n", str2);
printf("%s\n", str3);
printf("%s\n", str4);
return 0;
}
String Memory Layout
String: "Hello" Memory: [H][e][l][l][o][\0] Index: 0 1 2 3 4 5
#include <stdio.h>
#include <string.h>
int main() {
char str[20] = "Hello";
// String occupies characters + null terminator
printf("String: '%s'\n", str);
printf("Length: %zu\n", strlen(str)); // 5 (without null)
printf("Array size: %zu\n", sizeof(str)); // 20 (total allocated)
printf("Memory address: %p\n", str);
// Display memory contents
printf("Memory contents: ");
for (int i = 0; i < 10; i++) {
if (str[i] == '\0')
printf("[\\0] ");
else
printf("[%c] ", str[i]);
}
printf("\n");
return 0;
}
String Declaration and Initialization
1. Array-Based Strings
#include <stdio.h>
int main() {
// Fixed-size array
char str1[20]; // Uninitialized, contains garbage
char str2[20] = "Hello"; // Initialize with literal
char str3[] = "Hello"; // Size determined automatically (6)
char str4[20] = {'H', 'e', 'l', 'l', 'o', '\0'}; // Explicit initialization
// Partial initialization (remaining set to zero)
char str5[20] = {0}; // All zeros (empty string)
char str6[20] = "Hello"; // str6[0-4] = Hello, str6[5] = '\0', rest = 0
printf("str2: '%s'\n", str2);
printf("str3: '%s', size: %zu\n", str3, sizeof(str3));
printf("str4: '%s'\n", str4);
printf("str5: '%s'\n", str5);
return 0;
}
2. Pointer-Based Strings
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// String literal (read-only)
const char *str1 = "Hello";
// str1[0] = 'h'; // ERROR: modifying string literal is undefined behavior
// Dynamic allocation
char *str2 = (char*)malloc(20 * sizeof(char));
if (str2) {
strcpy(str2, "Dynamic");
printf("str2: '%s'\n", str2);
free(str2);
}
// String from static array
char buffer[50];
char *str3 = buffer;
strcpy(str3, "Buffer string");
printf("str3: '%s'\n", str3);
// String literal assigned to non-const pointer (deprecated, dangerous)
char *str4 = "Literal"; // Modern compilers warn about this
return 0;
}
3. Multidimensional String Arrays
#include <stdio.h>
int main() {
// Array of strings (2D character array)
char days[7][10] = {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"
};
printf("Days of the week:\n");
for (int i = 0; i < 7; i++) {
printf(" %s\n", days[i]);
}
// Array of pointers to strings
const char *months[] = {
"January", "February", "March", "April",
"May", "June", "July", "August",
"September", "October", "November", "December"
};
printf("\nMonths:\n");
for (int i = 0; i < 12; i++) {
printf(" %s\n", months[i]);
}
// Jagged array using pointers (more memory efficient)
char *colors[] = {"Red", "Green", "Blue", "Yellow"};
return 0;
}
String Length and Size
#include <stdio.h>
#include <string.h>
int main() {
char str[50] = "Hello";
// strlen() - length without null terminator
printf("strlen(str) = %zu\n", strlen(str)); // 5
// sizeof() - total memory allocated
printf("sizeof(str) = %zu\n", sizeof(str)); // 50
// strlen vs sizeof for string literals
char *ptr = "Hello";
printf("strlen(ptr) = %zu\n", strlen(ptr)); // 5
printf("sizeof(ptr) = %zu\n", sizeof(ptr)); // 8 (pointer size)
// sizeof array vs pointer
char arr[] = "Hello";
printf("sizeof(arr) = %zu\n", sizeof(arr)); // 6 (includes null)
return 0;
}
String Input/Output
1. Basic String Output
#include <stdio.h>
int main() {
char str[] = "Hello, World!";
// printf with %s
printf("Using printf: %s\n", str);
// puts (adds newline automatically)
puts("Using puts:");
puts(str);
// fputs (no newline added)
fputs("Using fputs: ", stdout);
fputs(str, stdout);
fputs("\n", stdout);
// Character-by-character output
printf("Character by character: ");
for (int i = 0; str[i] != '\0'; i++) {
putchar(str[i]);
}
putchar('\n');
return 0;
}
2. Safe String Input
#include <stdio.h>
#include <string.h>
int main() {
char buffer[50];
// DANGEROUS: gets() - never use! (no bounds checking)
// gets(buffer); // NEVER USE THIS
// Safe: fgets() - includes newline if space allows
printf("Enter a string: ");
if (fgets(buffer, sizeof(buffer), stdin)) {
// Remove trailing newline if present
size_t len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n') {
buffer[len-1] = '\0';
}
printf("You entered: '%s'\n", buffer);
}
// Using scanf with width limit
printf("Enter another string: ");
scanf("%49s", buffer); // Limit to 49 characters + null
printf("You entered: '%s'\n", buffer);
// Reading entire line with scanf using scanset
printf("Enter a line: ");
scanf(" %[^\n]", buffer); // Read until newline
printf("You entered: '%s'\n", buffer);
return 0;
}
3. Advanced Input with getchar()
#include <stdio.h>
#include <ctype.h>
// Safe string input function
int read_line(char *buffer, int size) {
int ch;
int i = 0;
while (i < size - 1 && (ch = getchar()) != '\n' && ch != EOF) {
buffer[i++] = ch;
}
buffer[i] = '\0';
// Clear input buffer if needed
if (ch != '\n' && ch != EOF) {
while ((ch = getchar()) != '\n' && ch != EOF);
}
return i;
}
int main() {
char name[30];
char address[100];
printf("Enter your name: ");
read_line(name, sizeof(name));
printf("Enter your address: ");
read_line(address, sizeof(address));
printf("\n--- User Info ---\n");
printf("Name: %s\n", name);
printf("Address: %s\n", address);
return 0;
}
Standard String Functions
1. String Copy (strcpy, strncpy)
#include <stdio.h>
#include <string.h>
int main() {
char source[] = "Hello, World!";
char dest1[20];
char dest2[20];
char dest3[20];
// strcpy - copies entire string (unsafe if dest too small)
strcpy(dest1, source);
printf("strcpy: %s\n", dest1);
// strncpy - copies up to n characters (safer)
strncpy(dest2, source, sizeof(dest2) - 1);
dest2[sizeof(dest2) - 1] = '\0'; // Ensure null termination
printf("strncpy: %s\n", dest2);
// strcpy_s (C11 optional, not portable)
// strcpy_s(dest3, sizeof(dest3), source);
// Manual copy
char dest4[20];
for (int i = 0; source[i] != '\0' && i < 19; i++) {
dest4[i] = source[i];
}
dest4[19] = '\0';
printf("Manual: %s\n", dest4);
return 0;
}
2. String Concatenation (strcat, strncat)
#include <stdio.h>
#include <string.h>
int main() {
char str1[50] = "Hello";
char str2[] = ", World!";
char str3[50] = "Hello";
char str4[] = " C Programming";
// strcat - concatenates strings
strcat(str1, str2);
printf("strcat: %s\n", str1);
// strncat - concatenates up to n characters
strncat(str3, str4, 5);
printf("strncat (5 chars): %s\n", str3);
// Manual concatenation
char dest[100] = "Start";
char src[] = " End";
int i = strlen(dest);
int j = 0;
while (src[j] != '\0') {
dest[i++] = src[j++];
}
dest[i] = '\0';
printf("Manual: %s\n", dest);
return 0;
}
3. String Comparison (strcmp, strncmp)
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "Apple";
char str2[] = "Apple";
char str3[] = "Banana";
char str4[] = "Apricot";
// strcmp - compares entire strings
printf("strcmp(\"%s\", \"%s\") = %d\n", str1, str2, strcmp(str1, str2));
printf("strcmp(\"%s\", \"%s\") = %d\n", str1, str3, strcmp(str1, str3));
printf("strcmp(\"%s\", \"%s\") = %d\n", str3, str1, strcmp(str3, str1));
// strncmp - compares first n characters
printf("\nstrncmp(\"%s\", \"%s\", 3) = %d\n", str1, str4, strncmp(str1, str4, 3));
printf("strncmp(\"%s\", \"%s\", 3) = %d\n", str4, str1, strncmp(str4, str1, 3));
// Case-insensitive comparison (custom)
char a[] = "Hello";
char b[] = "hello";
printf("\nCase-sensitive: %d\n", strcmp(a, b));
printf("Case-insensitive (custom): %d\n", strcasecmp(a, b)); // GNU extension
return 0;
}
4. String Search Functions
#include <stdio.h>
#include <string.h>
int main() {
char text[] = "The quick brown fox jumps over the lazy dog";
char *result;
// strchr - find first occurrence of character
result = strchr(text, 'q');
if (result) {
printf("First 'q' at position: %td\n", result - text);
printf("Remaining: %s\n", result);
}
// strrchr - find last occurrence of character
result = strrchr(text, 'o');
if (result) {
printf("Last 'o' at position: %td\n", result - text);
printf("Remaining: %s\n", result);
}
// strstr - find substring
result = strstr(text, "fox");
if (result) {
printf("Found 'fox' at position: %td\n", result - text);
}
// strpbrk - find first occurrence of any character from set
result = strpbrk(text, "aeiou");
if (result) {
printf("First vowel: '%c' at position %td\n", *result, result - text);
}
return 0;
}
5. String Tokenization
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "apple,banana,orange,grape";
char *token;
// strtok - tokenizes string (modifies original!)
printf("Original: %s\n", str);
token = strtok(str, ",");
while (token != NULL) {
printf("Token: %s\n", token);
token = strtok(NULL, ",");
}
// strtok is not thread-safe, use strtok_r for reentrant version
char str2[] = "one,two,three,four";
char *saveptr;
char *token2 = strtok_r(str2, ",", &saveptr);
while (token2 != NULL) {
printf("Token2: %s\n", token2);
token2 = strtok_r(NULL, ",", &saveptr);
}
return 0;
}
Advanced String Operations
1. String Conversion Functions
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// String to integer
char num_str[] = "12345";
int num = atoi(num_str);
printf("atoi: %d\n", num);
// String to long with error checking
char *endptr;
long val = strtol(num_str, &endptr, 10);
if (*endptr == '\0') {
printf("strtol: %ld\n", val);
}
// String to float
char float_str[] = "3.14159";
float f = atof(float_str);
printf("atof: %f\n", f);
// Integer to string
char buffer[20];
sprintf(buffer, "%d", 98765);
printf("sprintf: %s\n", buffer);
// Using snprintf (safer)
snprintf(buffer, sizeof(buffer), "Value: %d", 42);
printf("snprintf: %s\n", buffer);
return 0;
}
2. Custom String Manipulation
#include <stdio.h>
#include <ctype.h>
// Reverse a string
void reverse_string(char *str) {
if (!str) return;
int len = 0;
while (str[len]) len++;
for (int i = 0; i < len / 2; i++) {
char temp = str[i];
str[i] = str[len - 1 - i];
str[len - 1 - i] = temp;
}
}
// Convert to uppercase
void to_upper(char *str) {
for (int i = 0; str[i]; i++) {
str[i] = toupper(str[i]);
}
}
// Convert to lowercase
void to_lower(char *str) {
for (int i = 0; str[i]; i++) {
str[i] = tolower(str[i]);
}
}
// Trim whitespace from both ends
void trim(char *str) {
if (!str) return;
// Trim leading spaces
char *start = str;
while (isspace(*start)) start++;
// Trim trailing spaces
char *end = str + strlen(str) - 1;
while (end > start && isspace(*end)) end--;
// Shift string
int i = 0;
while (start <= end) {
str[i++] = *start++;
}
str[i] = '\0';
}
// Remove all occurrences of a character
void remove_char(char *str, char ch) {
int write_index = 0;
for (int read_index = 0; str[read_index]; read_index++) {
if (str[read_index] != ch) {
str[write_index++] = str[read_index];
}
}
str[write_index] = '\0';
}
int main() {
char str[100] = " Hello, World! ";
printf("Original: '%s'\n", str);
trim(str);
printf("Trimmed: '%s'\n", str);
to_upper(str);
printf("Uppercase: '%s'\n", str);
reverse_string(str);
printf("Reversed: '%s'\n", str);
remove_char(str, '!');
printf("Remove '!': '%s'\n", str);
return 0;
}
3. Dynamic String Allocation
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char *data;
size_t length;
size_t capacity;
} DynamicString;
// Initialize dynamic string
DynamicString* ds_create(size_t initial_capacity) {
DynamicString *ds = malloc(sizeof(DynamicString));
ds->data = malloc(initial_capacity);
ds->data[0] = '\0';
ds->length = 0;
ds->capacity = initial_capacity;
return ds;
}
// Append to dynamic string
void ds_append(DynamicString *ds, const char *str) {
size_t needed = ds->length + strlen(str) + 1;
if (needed > ds->capacity) {
// Double capacity until sufficient
while (needed > ds->capacity) {
ds->capacity *= 2;
}
ds->data = realloc(ds->data, ds->capacity);
}
strcat(ds->data, str);
ds->length += strlen(str);
}
// Get string
const char* ds_get(const DynamicString *ds) {
return ds->data;
}
// Free dynamic string
void ds_free(DynamicString *ds) {
free(ds->data);
free(ds);
}
int main() {
DynamicString *str = ds_create(10);
ds_append(str, "Hello");
printf("After append: %s\n", ds_get(str));
ds_append(str, ", ");
ds_append(str, "World");
ds_append(str, "!");
printf("After more appends: %s\n", ds_get(str));
printf("Length: %zu, Capacity: %zu\n", str->length, str->capacity);
ds_free(str);
return 0;
}
String Validation and Safety
1. Input Validation
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
// Check if string contains only letters
bool is_alpha_string(const char *str) {
for (int i = 0; str[i]; i++) {
if (!isalpha(str[i])) {
return false;
}
}
return true;
}
// Check if string contains only digits
bool is_numeric_string(const char *str) {
for (int i = 0; str[i]; i++) {
if (!isdigit(str[i])) {
return false;
}
}
return true;
}
// Check if string is a valid email (simple check)
bool is_valid_email(const char *email) {
const char *at = strchr(email, '@');
const char *dot = strrchr(email, '.');
return (at != NULL && dot != NULL &&
at < dot &&
at > email &&
dot[1] != '\0');
}
// Check if string is within length limits
bool is_valid_length(const char *str, size_t min, size_t max) {
size_t len = strlen(str);
return len >= min && len <= max;
}
int main() {
char input[100];
printf("Enter a username (letters only, 3-20 chars): ");
fgets(input, sizeof(input), stdin);
input[strcspn(input, "\n")] = '\0';
if (!is_valid_length(input, 3, 20)) {
printf("Error: Invalid length\n");
} else if (!is_alpha_string(input)) {
printf("Error: Username must contain only letters\n");
} else {
printf("Valid username: %s\n", input);
}
printf("\nEnter an email address: ");
fgets(input, sizeof(input), stdin);
input[strcspn(input, "\n")] = '\0';
if (is_valid_email(input)) {
printf("Valid email: %s\n", input);
} else {
printf("Invalid email address\n");
}
return 0;
}
2. Buffer Overflow Prevention
#include <stdio.h>
#include <string.h>
// Safe string copy function
int safe_strcpy(char *dest, size_t dest_size, const char *src) {
size_t src_len = strlen(src);
if (src_len >= dest_size) {
return -1; // Not enough space
}
strcpy(dest, src);
return 0;
}
// Safe string concatenation
int safe_strcat(char *dest, size_t dest_size, const char *src) {
size_t dest_len = strlen(dest);
size_t src_len = strlen(src);
if (dest_len + src_len >= dest_size) {
return -1; // Not enough space
}
strcat(dest, src);
return 0;
}
int main() {
char buffer[20];
// Safe copy
if (safe_strcpy(buffer, sizeof(buffer), "Hello") == 0) {
printf("Copied: %s\n", buffer);
} else {
printf("Copy failed: buffer too small\n");
}
// Safe concatenation
if (safe_strcat(buffer, sizeof(buffer), ", World!") == 0) {
printf("Concatenated: %s\n", buffer);
} else {
printf("Concatenation failed: buffer too small\n");
}
// This would fail
if (safe_strcat(buffer, sizeof(buffer), " This is too long") == 0) {
printf("Concatenated: %s\n", buffer);
} else {
printf("Concatenation failed: buffer too small\n");
}
return 0;
}
Complete Example: Text Processing Application
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#define MAX_LINES 1000
#define MAX_LINE_LEN 256
typedef struct {
char lines[MAX_LINES][MAX_LINE_LEN];
int count;
} TextFile;
// Load text from file
int load_file(TextFile *text, const char *filename) {
FILE *fp = fopen(filename, "r");
if (!fp) return -1;
text->count = 0;
while (fgets(text->lines[text->count], MAX_LINE_LEN, fp) &&
text->count < MAX_LINES) {
// Remove trailing newline
size_t len = strlen(text->lines[text->count]);
if (len > 0 && text->lines[text->count][len-1] == '\n') {
text->lines[text->count][len-1] = '\0';
}
text->count++;
}
fclose(fp);
return 0;
}
// Save text to file
int save_file(const TextFile *text, const char *filename) {
FILE *fp = fopen(filename, "w");
if (!fp) return -1;
for (int i = 0; i < text->count; i++) {
fprintf(fp, "%s\n", text->lines[i]);
}
fclose(fp);
return 0;
}
// Count words in a string
int count_words(const char *str) {
int count = 0;
int in_word = 0;
for (int i = 0; str[i]; i++) {
if (isspace(str[i])) {
in_word = 0;
} else {
if (!in_word) {
count++;
in_word = 1;
}
}
}
return count;
}
// Find and replace in a string
int find_replace(char *str, const char *find, const char *replace) {
char buffer[MAX_LINE_LEN];
char *pos;
int found = 0;
buffer[0] = '\0';
while ((pos = strstr(str, find)) != NULL) {
// Copy part before find
*pos = '\0';
strcat(buffer, str);
// Add replacement
strcat(buffer, replace);
// Continue after find
str = pos + strlen(find);
found = 1;
}
// Add remaining part
strcat(buffer, str);
strcpy(str, buffer);
return found;
}
// Display statistics
void display_stats(const TextFile *text) {
int total_chars = 0;
int total_words = 0;
int total_lines = text->count;
for (int i = 0; i < text->count; i++) {
total_chars += strlen(text->lines[i]);
total_words += count_words(text->lines[i]);
}
printf("\n--- Statistics ---\n");
printf("Lines: %d\n", total_lines);
printf("Words: %d\n", total_words);
printf("Characters: %d\n", total_chars);
printf("Average words per line: %.2f\n",
total_lines > 0 ? (float)total_words / total_lines : 0);
}
// Search for lines containing text
void search_lines(const TextFile *text, const char *query) {
printf("\n--- Search Results for '%s' ---\n", query);
int found = 0;
for (int i = 0; i < text->count; i++) {
if (strstr(text->lines[i], query)) {
printf("%3d: %s\n", i + 1, text->lines[i]);
found++;
}
}
if (!found) {
printf("No matches found.\n");
}
}
int main(int argc, char *argv[]) {
TextFile text;
if (argc < 2) {
printf("Usage: %s <filename> [operation]\n", argv[0]);
printf("Operations:\n");
printf(" (none) - Display file content\n");
printf(" stats - Show statistics\n");
printf(" search <word> - Search for word\n");
printf(" replace <old> <new> - Replace text\n");
return 1;
}
if (load_file(&text, argv[1]) != 0) {
printf("Error loading file: %s\n", argv[1]);
return 1;
}
if (argc == 2) {
// Display file content
for (int i = 0; i < text.count; i++) {
printf("%s\n", text.lines[i]);
}
}
else if (strcmp(argv[2], "stats") == 0) {
display_stats(&text);
}
else if (strcmp(argv[2], "search") == 0 && argc >= 4) {
search_lines(&text, argv[3]);
}
else if (strcmp(argv[2], "replace") == 0 && argc >= 5) {
int modified = 0;
for (int i = 0; i < text.count; i++) {
if (find_replace(text.lines[i], argv[3], argv[4])) {
modified = 1;
}
}
if (modified) {
// Save backup
char backup[256];
snprintf(backup, sizeof(backup), "%s.bak", argv[1]);
save_file(&text, backup);
printf("Backup saved to %s\n", backup);
// Save modified file
save_file(&text, argv[1]);
printf("Replacements applied and saved.\n");
} else {
printf("No occurrences of '%s' found.\n", argv[3]);
}
}
else {
printf("Unknown operation: %s\n", argv[2]);
}
return 0;
}
String Literals and Constants
#include <stdio.h>
// String constants
#define GREETING "Hello, World!"
const char *WELCOME = "Welcome to C programming";
int main() {
// String literals are stored in read-only memory
const char *str1 = "Hello";
char str2[] = "Hello"; // Stored in modifiable memory
// str1[0] = 'h'; // ERROR: modifying string literal
str2[0] = 'h'; // OK: modifying array copy
printf("%s\n", str2);
// String literal concatenation
char *combined = "Hello" ", " "World" "!";
printf("%s\n", combined); // Hello, World!
// Multi-line string literal
char *multi = "This is a "
"multi-line "
"string literal";
printf("%s\n", multi);
return 0;
}
Common Pitfalls and Best Practices
#include <stdio.h>
#include <string.h>
// PITFALL 1: Forgetting null terminator
void pitfall_no_null() {
char str[5] = {'H', 'e', 'l', 'l', 'o'}; // No null terminator!
printf("%s\n", str); // Undefined behavior (prints beyond array)
}
// FIX: Always include null terminator
void fix_no_null() {
char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
char str2[] = "Hello"; // Better: automatic null terminator
printf("%s\n", str2);
}
// PITFALL 2: Buffer overflow with strcpy
void pitfall_buffer_overflow() {
char dest[5];
// strcpy(dest, "Hello, World!"); // Buffer overflow!
}
// FIX: Use strncpy or snprintf
void fix_buffer_overflow() {
char dest[5];
strncpy(dest, "Hello, World!", sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
printf("%s\n", dest);
}
// PITFALL 3: Using == to compare strings
void pitfall_string_compare() {
char str1[] = "Hello";
char str2[] = "Hello";
if (str1 == str2) { // Compares addresses, not content!
printf("Strings are equal\n");
} else {
printf("Strings are NOT equal\n"); // This will print
}
}
// FIX: Use strcmp
void fix_string_compare() {
char str1[] = "Hello";
char str2[] = "Hello";
if (strcmp(str1, str2) == 0) {
printf("Strings are equal\n");
}
}
// PITFALL 4: Returning pointer to local array
char* pitfall_return_local() {
char str[] = "Hello";
return str; // Returns pointer to local variable (invalid after return)
}
// FIX: Use static, dynamic allocation, or pass buffer
void fix_return_local(char *buffer, size_t size) {
strncpy(buffer, "Hello", size - 1);
buffer[size - 1] = '\0';
}
int main() {
fix_no_null();
fix_buffer_overflow();
fix_string_compare();
char buffer[20];
fix_return_local(buffer, sizeof(buffer));
printf("Returned: %s\n", buffer);
return 0;
}
Summary Table: String Functions
| Function | Purpose | Safety Notes |
|---|---|---|
strlen() | Get length (excluding null) | Safe |
strcpy() | Copy string | Unsafe (no bounds) |
strncpy() | Copy with limit | May not null-terminate |
strcat() | Concatenate | Unsafe (no bounds) |
strncat() | Concatenate with limit | Safer |
strcmp() | Compare strings | Safe |
strncmp() | Compare first n chars | Safe |
strchr() | Find character | Safe |
strstr() | Find substring | Safe |
strtok() | Tokenize | Modifies string, not thread-safe |
sprintf() | Format to string | Unsafe (no bounds) |
snprintf() | Format with limit | Safe |
atoi() | String to int | No error checking |
strtol() | String to long | Good error checking |
Best Practices Summary
- Always null-terminate: Ensure all strings end with
'\0' - Use
sizeoffor array bounds: Not for pointers - Prefer
snprintfoversprintf: Safer formatting - Use
fgetsovergets:getsis dangerous and deprecated - Validate input: Check string lengths before copying
- Use
constfor read-only strings: Prevents accidental modification - Be careful with string literals: They are read-only
- Know your string functions: Understand behavior and safety
- Handle errors: Check return values for overflow
- Free dynamic strings: Prevent memory leaks
Conclusion
Strings in C are both simple and complex. At their core, they are arrays of characters terminated by null, but their proper use requires careful attention to memory management, buffer sizes, and function behavior.
Mastering C strings is essential for systems programming, embedded development, and any application that processes text. The functions and patterns presented in this guide provide a solid foundation for working with strings safely and efficiently.
Remember that with great power comes great responsibility—C gives you direct control over memory, but you must ensure you use that power correctly to avoid buffer overflows, memory leaks, and undefined behavior. By following best practices and understanding the underlying mechanics, you can harness the full power of C strings while writing robust, secure code.