C
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Arrays and Strings
-
Section 5: Pointers
-
Section 6: Memory Management
-
Section 7: Structures and Unions
-
Section 8: The Preprocessor and Build Process
-
Section 9: Standard Library: stdio.h
-
Section 10: Standard Library: stdlib.h
-
Section 11: Standard Library: string.h
-
Section 12: Standard Library: ctype.h and wctype.h
-
Section 13: Standard Library: math.h, complex.h, fenv.h, tgmath.h
-
Section 14: Standard Library: Type and Limit Headers
-
Section 15: Standard Library: Error Handling and Debugging
-
Section 16: Standard Library: Localization and Encoding
-
Section 17: Standard Library: time.h
-
Section 18: Standard Library: Concurrency (C11)
-
Section 19: POSIX and System Programming (unistd.h)
-
Section 20: More Data Structures
-
Section 21: Algorithms in C
-
Section 22: Bitwise Operations
-
Section 23: Command-Line Programs
-
Section 24: Debugging and Best Practices
-
Section 25: Compiler and Language Internals
-
Section 26: Embedded and Cross-Platform Considerations
-
Section 27: Networking Basics
-
Section 28: Practical Projects
-
Section 29: Interview Practice
-
Section 30: C23 Modern Features
-
Section 31: More Practice and Review
38: C-Style Strings and Null Termination
Here is the first thing you need to accept about C: there is no such thing as a "string" type. I know, it sounds like a prank, but it's true. When you see char* or char[] being used as a string, you're actually just looking at a sequence of characters in memory. The only thing that makes it a "string" is a silent agreement between the programmer and the compiler that the sequence ends with a special character: the null terminator, written as '\0'.
If you forget that null terminator, C doesn't stop. It will keep reading your memory, byte after byte, printing whatever random garbage is stored in your RAM until it happens to stumble upon a zero by pure luck—or until the operating system crashes your program for accessing memory it shouldn't.
Building a Username Truncator
Let's build something concrete. Imagine we're writing a system where usernames are limited to 8 characters. If a user provides a longer name, we need to truncate it. I'm going to write a function that takes a long input string and copies only the first 8 characters into a destination buffer.
#include <stdio.h>
void truncate_username(char *src, char *dest) {
for (int i = 0; i < 8; i++) {
dest[i] = src[i];
}
}
int main() {
char input[] = "Christopher_The_Great";
char output[9]; // 8 for the chars, 1 for the terminator
truncate_username(input, output);
printf("Truncated name: %s\n", output);
return 0;
}
The Classic "Missing Null" Disaster
If you ran the code above, you'd likely see something like Truncated name: Christop@#$!%&^ followed by a bunch of weird symbols. I did this on purpose. I made the classic mistake: I copied the characters, but I forgot to tell C where the string actually ends.
The printf function with the %s specifier doesn't know how large the output array is. It just starts at the first character and keeps going until it hits a '\0'. Since I never placed one in the output buffer, printf just kept walking right past the end of my array into the dark abyss of the stack memory.
Closing the Gap with the Terminator
To fix this, we have to manually place the null terminator at the index immediately following our last copied character. Since we copied indices 0 through 7, the terminator belongs at index 8. This is why I declared the output array as char[9]—you always need one extra slot for the null character.
Here is the corrected logic. Notice how I've added a check to handle cases where the input name is actually shorter than 8 characters, because copying past the end of the source string would be just as dangerous as reading past the end of the destination.
#include <stdio.h>
void truncate_username(char *src, char *dest) {
int i;
for (i = 0; i < 8; i++) {
// If we hit the end of the source string early, stop copying
if (src[i] == '\0') {
break;
}
dest[i] = src[i];
}
// The golden rule: Always null-terminate your C-strings
dest[i] = '\0';
}
int main() {
char input[] = "Christopher_The_Great";
char output[9];
truncate_username(input, output);
printf("Truncated name: %s\n", output); // Now prints "Christop"
return 0;
}
By adding dest[i] = '\0', we've effectively placed a "STOP" sign in memory. Now, when printf reaches that byte, it knows it has reached the end of the string and stops reading immediately.
📋 Practical Task
Build a Manual String Reverser
Write a program that takes a hard-coded string (e.g., "Software Engineering") and creates a second character array containing the string in reverse.
Your solution must:
- Calculate the length of the source string manually (by looping until you find the
'\0'). - Allocate a destination array large enough to hold the reversed characters plus the null terminator.
- Correctly place the
'\0'at the end of the reversed string so that it can be printed usingprintf("%s", ...)without displaying memory garbage.
There are no comments for now.