C Strings — Char Arrays, Null Terminator, and string.h Functions
In this tutorial, you will learn about C Strings. We cover key concepts, practical examples, and best practices to help you master this topic.
C strings are sequences of characters stored in char arrays, terminated by a null character ('\0'). Unlike higher-level languages, C has no built-in string type -- strings are simply arrays of char with a special terminator.
Why It Matters
String handling is one of the most error-prone areas in C Programming. Buffer overflows, off-by-one errors, and null terminator issues have caused countless security vulnerabilities. Understanding exactly how strings work in memory is essential for writing safe and correct C code. Every program that reads input, prints output, or processes text uses strings.
Real-World Use
Every printf call uses format strings. File paths are strings. Network protocols send and receive text. Configuration files contain strings. The Heartbleed bug in OpenSSL was a string handling error. Durga Antivirus Pro parses file paths and virus signature strings constantly.
What You Will Learn
- Character arrays and string literals
- The null terminator and why it matters
- String input and output with printf, puts, gets, fgets
- String.h functions: strlen, strcpy, strcat, strcmp, strtok
- Safe string handling with strncpy, strncat, snprintf
- Common string operations and pitfalls
Learning Path
flowchart LR A[Arrays] --> B[Strings
You are here] B --> C[Pointers Basics] C --> D[Pointer Arithmetic] D --> E[Dynamic Memory] style B fill:#f90,color:#fff
What Is a C String?
A string in C is a char array that ends with a null character \0 (ASCII value 0):
#include <stdio.h>
int main() {
// String literal -- automatically null-terminated
char greeting[] = "Hello";
// Equivalent explicit initialization
char explicit[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
// sizeof includes the null terminator
printf("Size of 'Hello': %zu bytes\n", sizeof(greeting)); // 6
// strlen counts characters before null terminator
printf("Length of 'Hello': %zu\n", strlen(greeting)); // 5
char name[] = "Alice";
printf("Hello, %s!\n", name); // Hello, Alice!
return 0;
}
Expected output:
Size of 'Hello': 6 bytes
Length of 'Hello': 5
Hello, Alice!
The null terminator is what distinguishes a string from a plain char array. Any char array that does not contain \0 is not a valid C string.
String Literals vs Char Arrays
#include <stdio.h>
int main() {
// String literal -- stored in read-only memory
char *str1 = "Hello";
// Char array -- stored on stack, modifiable
char str2[] = "Hello";
// str1[0] = 'h'; // Undefined behavior! String literal is read-only
str2[0] = 'h'; // OK: str2 is a modifiable copy
printf("Modified: %s\n", str2); // hello
// Array vs pointer difference
printf("Size of pointer: %zu\n", sizeof(str1)); // 8 (pointer size)
printf("Size of array: %zu\n", sizeof(str2)); // 6 (array size)
return 0;
}
Expected output:
Modified: hello
Size of pointer: 8
Size of array: 6
String Input
Reading strings from user input requires care to avoid buffer overflow:
#include <stdio.h>
int main() {
char buffer[20];
// DANGEROUS: gets() has no bounds checking
// gets(buffer); // Never use gets()!
// SAFE: fgets() limits input to buffer size - 1
printf("Enter your name: ");
fgets(buffer, sizeof(buffer), stdin);
// fgets includes the newline; remove it
buffer[strcspn(buffer, "\n")] = '\0';
printf("Hello, %s!\n", buffer);
// scanf with width specifier
printf("Enter a word: ");
scanf("%19s", buffer); // Read at most 19 chars
printf("Word: %s\n", buffer);
return 0;
}
String.h Functions
The <string.h> header provides essential string functions:
#include <stdio.h>
#include <string.h>
int main() {
char dest[50];
char src[] = "Hello";
// strlen: get string length (without null terminator)
printf("Length of '%s': %zu\n", src, strlen(src)); // 5
// strcpy: copy string (DANGEROUS without size check)
strcpy(dest, src);
printf("Copied: %s\n", dest);
// strncpy: safe copy with size limit
char safe[10];
strncpy(safe, "This is a long string", sizeof(safe) - 1);
safe[sizeof(safe) - 1] = '\0'; // Ensure null termination
printf("Safe copy: %s\n", safe);
// strcat: concatenate (DANGEROUS)
char greeting[50] = "Hello, ";
strcat(greeting, "World!");
printf("Concatenated: %s\n", greeting);
// strncat: safe concatenation
char msg[20] = "Hi ";
strncat(msg, "there everyone!", sizeof(msg) - strlen(msg) - 1);
printf("Safe concat: %s\n", msg);
// strcmp: compare strings (returns 0 if equal)
char pass[] = "secret";
char input[] = "secret";
if (strcmp(pass, input) == 0) {
printf("Password correct!\n");
}
// strcmp ordering ('A' < 'B' < 'Z' < 'a' < 'z')
printf("'apple' vs 'banana': %d\n", strcmp("apple", "banana")); // < 0
return 0;
}
Expected output:
Length of 'Hello': 5
Copied: Hello
Safe copy: This is a
Concatenated: Hello, World!
Safe concat: Hi there everyon
Password correct!
'apple' vs 'banana': -1
Searching and Tokenizing
#include <stdio.h>
#include <string.h>
int main() {
char text[] = "Hello, world! Welcome to C programming.";
char *pos;
// strchr: find first occurrence of character
pos = strchr(text, 'w');
if (pos) {
printf("Found 'w' at position: %ld\n", pos - text);
// Output: Found 'w' at position: 7
}
// strstr: find substring
pos = strstr(text, "C programming");
if (pos) {
printf("Found substring at position: %ld\n", pos - text);
// Output: Found substring at position: 22
}
// strtok: split string by delimiters
char data[] = "apple,banana,cherry,date";
char *token = strtok(data, ",");
printf("Tokens:\n");
while (token != NULL) {
printf(" %s\n", token);
token = strtok(NULL, ",");
}
return 0;
}
Expected output:
Found 'w' at position: 7
Found substring at position: 22
Tokens:
apple
banana
cherry
date
Formatting Strings
#include <stdio.h>
#include <string.h>
int main() {
char buffer[100];
char name[] = "Alice";
int age = 30;
double salary = 75000.50;
// sprintf: format into string (DANGEROUS without size limit)
sprintf(buffer, "%s is %d years old and earns $%.2f", name, age, salary);
printf("%s\n", buffer);
// Alice is 30 years old and earns $75000.50
// snprintf: safe version with size limit
char safe[30];
int written = snprintf(safe, sizeof(safe), "%s is %d", name, age);
printf("Safe: '%s' (wrote %d chars)\n", safe, written);
// Safe: 'Alice is 30' (wrote 12 chars)
// sscanf: parse from string
int parsed_age;
char parsed_name[50];
sscanf(buffer, "%49s is %d years", parsed_name, &parsed_age);
printf("Parsed: name=%s, age=%d\n", parsed_name, parsed_age);
// Parsed: name=Alice, age=30
return 0;
}
String Comparison with strcmp
The strcmp function compares strings lexicographically (dictionary order):
#include <stdio.h>
#include <string.h>
int main() {
// Returns negative if first < second
printf("'a' vs 'b': %d\n", strcmp("a", "b")); // < 0
// Returns 0 if equal
printf("'abc' vs 'abc': %d\n", strcmp("abc", "abc")); // 0
// Returns positive if first > second
printf("'z' vs 'a': %d\n", strcmp("z", "a")); // > 0
// Case matters: uppercase < lowercase
printf("'A' vs 'a': %d\n", strcmp("A", "a")); // < 0
// strcasecmp (POSIX) or stricmp (Windows) for case-insensitive
// On Linux: strcasecmp("hello", "HELLO") returns 0
return 0;
}
Common Mistakes
1. Buffer Overflow
char buf[5];
strcpy(buf, "Hello World"); // Writes past end of buf!
Always use strncpy, snprintf, or ensure the buffer is large enough.
2. Forgetting the Null Terminator
char str[5] = {'H', 'e', 'l', 'l', 'o'}; // Not null-terminated!
printf("%s", str); // Prints "Hello" plus garbage until '\0'
Always leave room for the null terminator: char str[6] = "Hello";.
3. Using gets()
char buf[100];
gets(buf); // No bounds check -- never use!
Use fgets(buf, sizeof(buf), stdin) instead.
4. Confusing strlen with sizeof
char str[] = "Hello";
strlen(str) // 5 -- character count without '\0'
sizeof(str) // 6 -- includes '\0'
5. Modifying String Literals
char *str = "Hello";
str[0] = 'h'; // Undefined behavior!
Use char str[] = "Hello" to create a modifiable copy.
6. Not Checking strtok Modifies the Original
char *str = "apple,banana";
strtok(str, ","); // ERROR: string literal is read-only
Copy the string first: char copy[] = "apple,banana"; strtok(copy, ",");
Practice Questions
What marks the end of a C string? The null terminator character
\0(ASCII value 0).What is the difference between
strlenandsizeofon a char array?strlenreturns the number of characters before\0.sizeofreturns the total allocated size including\0.Why is
gets()dangerous and what should you use instead?gets()has no bounds checking and will overflow any buffer. Usefgets()which accepts a maximum size.What does
strcmpreturn when strings are equal? 0. A negative value means the first string is lexicographically smaller; positive means larger.Challenge: Write a program that counts the number of words in a string using strtok.
Mini Project: String Reverser
#include <stdio.h>
#include <string.h>
void reverse(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;
}
}
int main() {
char text[] = "Hello, World!";
printf("Original: %s\n", text);
reverse(text);
printf("Reversed: %s\n", text);
// Palindrome check
char word[] = "racecar";
char copy[20];
strcpy(copy, word);
reverse(copy);
if (strcmp(word, copy) == 0) {
printf("'%s' is a palindrome.\n", word);
} else {
printf("'%s' is not a palindrome.\n", word);
}
return 0;
}
FAQ
What is Next
Now that you understand strings, proceed to Pointers Basics to learn about memory addresses, dereferencing, and pointer types.