Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
C Strings and Null Terminator
How C stores text as a char array ended by the NUL byte, explained with a runnable example and a byte-level memory view.
What You Will Learn
- What a C string really is, and why C has no separate string type
- How the NUL terminator
'\0'marks the end of text - The difference between
'\0','0',NULLand"" - How to build a string by hand and by literal, and verify the bytes with a program
- Why a plain
chararray is not automatically a string - The mistakes that cause garbage output and buffer over-reads in embedded C programming
Prerequisites
- Arrays and indexing from the Arrays chapter
- The
chartype and character constants - Basic
printfusage
Welcome to Chapter 9 of the free C programming course from EmbeddedPathashala. Strings are where C is at its most honest: there is no string object, no length field and no safety net, only bytes in memory and one agreed marker for where the text stops. In this free C tutorial you will see exactly how C strings work, byte by byte, because every later topic (string literals, scanf, library functions, UART parsers) builds on this one idea. If you are following this free embedded C course to write firmware, treat this lecture as the foundation of everything text-related.
What Is a String in C?
Languages such as Python or Java give you a string type that remembers its own length. C does not. In C a string is just a contiguous array of char that ends with a zero byte. The zero byte is called the NUL terminator, and in source code it is written '\0'. Text such as a word, a sentence or a sensor name is stored in the array, and the terminator tells every function where the text stops.
Because the length is not stored anywhere, a function that wants to know how long a string is has to walk the bytes and count until it meets the zero. That single design choice explains most of the speed, the flexibility and the danger of C strings.
| Idea | Plain char array | C string |
|---|---|---|
| Is it an array of char? | Yes | Yes |
| Ends with a zero byte? | Not necessarily | Always |
| Safe for printf(“%s”) and strlen? | No | Yes |
| Length stored anywhere? | No | No, found by scanning for the zero |
| Can hold arbitrary binary data? | Yes | No, a zero byte inside ends the text early |
The NUL Terminator in C Strings
The terminator is an escape sequence whose numeric value is 0. It is the very first entry in the ASCII table, a control character that prints nothing. People mix it up with three look-alikes, so get them straight now:
| Spelling | Meaning | Value | Typical use |
|---|---|---|---|
'\0' | NUL character | 0 | Ends every C string |
'0' | The digit zero as text | 48 (0x30) | Printing the digit 0 |
NULL | Null pointer constant | pointer, not a char | A pointer that points nowhere |
"" | Empty string literal | one byte: 0 | A string with zero characters |
Do not confuse these
'\0' and NULL both evaluate to zero in many expressions, which tempts people to swap them. Keep '\0' for characters and NULL for pointers so the code says what you mean.
How the string UART sits in memory
| Index | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| Character | U | A | R | T | \0 |
| Byte (hex) | 0x55 | 0x41 | 0x52 | 0x54 | 0x00 |
| Role | text | text | text | text | terminator |
Four visible characters need five bytes. The extra byte is the price of knowing where the text ends, and it is the reason every string buffer must be at least one byte longer than the longest text it will hold.
Build a C String Two Ways
The program below builds the same string by listing every character yourself and by using a string literal. It then prints the size, the length and each stored byte, and also shows a char array that is deliberately not a string.
Save it as nul_demo.c.
#include <stdio.h>
#include <string.h>
int main(void)
{
char a[] = {'U', 'A', 'R', 'T', '\0'}; /* hand-built string */
char b[] = "UART"; /* same thing, shorter */
char raw[4] = {'U', 'A', 'R', 'T'}; /* char array, NOT string */
printf("sizeof a = %zu, strlen a = %zu\n", sizeof a, strlen(a));
printf("sizeof b = %zu, strlen b = %zu\n", sizeof b, strlen(b));
printf("same bytes? %s\n", memcmp(a, b, sizeof a) == 0 ? "yes" : "no");
puts("--- bytes of b ---");
for (size_t i = 0; i < sizeof b; i++) {
if (b[i] != '\0')
printf("b[%zu] = 0x%02X '%c'\n", i, (unsigned char)b[i], b[i]);
else
printf("b[%zu] = 0x%02X NUL terminator\n", i, (unsigned char)b[i]);
}
printf("--- raw has %zu bytes and no NUL, so never use %%s on it ---\n",
sizeof raw);
return 0;
}
Build and run it with the C17 standard and warnings switched on:
gcc -std=c17 -Wall -Wextra -o nul_demo nul_demo.c
./nul_demo
Expected output:
sizeof a = 5, strlen a = 4
sizeof b = 5, strlen b = 4
same bytes? yes
--- bytes of b ---
b[0] = 0x55 'U'
b[1] = 0x41 'A'
b[2] = 0x52 'R'
b[3] = 0x54 'T'
b[4] = 0x00 NUL terminator
--- raw has 4 bytes and no NUL, so never use %s on it ---
What the output tells you
sizeofreports 5 because it counts all array elements, including the terminator.strlenreports 4 because it stops at the first zero byte and does not count it.memcmpconfirms that the hand-built array and the literal-initialised array hold identical bytes.- The array
rawholds exactly the four letters and nothing else. It is a valid array of char, but it is not a string.
Why the Terminator Matters
Standard functions never receive the array size. They receive only the address of the first byte and keep going until they see a zero. Look at how printf("%s", text) walks through the bytes:
How printf with percent s finds the end of the text
If the zero is missing, the loop does not stop at the end of your array. It carries on through whatever happens to sit next in memory, perhaps another variable, perhaps a stack frame, until some zero byte appears by chance. On a PC that usually prints junk. On a microcontroller it can print secrets, corrupt a display or trigger a hard fault.
A string needs room for the terminator
When you size an array by hand, count the characters and add one. The expression char name[5] = "UART" fits exactly. char name[4] would leave no room for the zero. In this C programming for embedded systems habit-forming moment, always write the size as longest text + 1, or let the compiler count by leaving the brackets empty.
Real-World Uses of C Strings in Embedded Systems
Log and debug messages
Boot banners, error texts and status lines sent over a UART are all strings that end in a zero byte.
Command parsers
A serial console receives text such as a command name, compares it with a table of known commands and acts on the match.
Configuration names
Device names, file names and Wi-Fi network names are stored and compared as strings.
Display text
Character LCDs and OLED drivers walk a string one character at a time until the terminator.
Common Mistakes With C Strings
| Mistake | What happens | Fix |
|---|---|---|
| Sizing the array to the text length only | No room for the terminator; later reads run past the end | Size = characters + 1 |
Writing '0' when you mean '\0' | The string never ends where you expect | Use '\0' for the terminator |
Calling strlen on a plain char array | Undefined behaviour, a read past the array | Make it a string first, or track the length yourself |
Assuming sizeof equals strlen | Off-by-one bugs in loops and copies | Remember sizeof includes the terminator |
| Storing binary data in a string | A data byte equal to zero cuts the text short | Use a length plus a raw buffer for binary data |
Best Practices, Performance and Security
- Let the compiler count: write
char msg[] = "..."when the array should hold exactly this text. - Name the capacity in a macro or constant so every copy, read and comparison uses the same number.
- Treat every string that arrives from outside your program (UART, network, file) as untrusted and bound all reads.
- Performance:
strlenis O(n) because it scans for the zero. Inside a loop condition it is re-run on every pass, so compute it once and store it. - Security: a missing terminator causes buffer over-reads, a classic source of information leaks. Bounded functions and explicit sizes are your defence.
Summary and Key Takeaways
- C has no string type. A string is a
chararray that ends with'\0'. - The terminator takes one byte, so storage is always length plus one.
'\0','0',NULLand""are four different things.- A char array without the terminator is valid data but is not safe to pass to string functions.
This lecture gave you the mental model for how C strings work across the whole chapter: text in C is bytes plus a marker, and good C strings habits start with counting that extra byte. In the next lecture of this free C programming course you will meet string literals, the quoted text you write directly in source code, and learn that the compiler turns each one into exactly this kind of array and hands you a pointer to it.
Frequently Asked Questions
Is there a string data type in C?
No. C has no built-in string type. A string is a convention: an array of char whose last meaningful element is the byte value 0, written '\0'. Every standard string function relies on that convention.
What is the difference between ‘\0’, ‘0’ and NULL?
'\0' is the character with value 0, the string terminator. '0' is the digit character with ASCII value 48. NULL is a null pointer constant used with pointers, not with characters.
Does strlen count the NUL terminator?
No. strlen counts the characters before the first NUL. sizeof on an array holding the string does include the terminator, so for char b[] = "UART" you get strlen 4 and sizeof 5.
What happens if I forget the terminator?
Functions such as printf("%s") and strlen keep reading bytes past the end of your array until they happen to meet a zero byte. You get garbage output, a crash, or silently leaked memory contents.
Does the compiler add the NUL automatically?
Yes, when you initialise an array from a string literal such as char b[] = "UART". It does not add one when you list characters yourself unless you include '\0', and it cannot add one if you fix the array size too small.
Why does embedded C programming care so much about string length?
Microcontrollers have kilobytes of RAM. Every string costs its length plus one byte, and a missing terminator in a UART or log buffer can corrupt neighbouring variables. Knowing the exact byte layout is part of writing safe firmware.
Topics Covered in This Lecture
Keep Going With the Free C Programming Course
EmbeddedPathashala teaches C from the first printf to embedded-ready pointers and strings, completely free. Next: how string literals are stored and why they behave like pointers.
Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.

1 Comment