Introduction
C has no separate string type. A string is a sequence of characters terminated by the null character '\0' (value 0), stored in an array of char. Unbounded traversal functions such as strlen need a null terminator within accessible storage. Length-bounded operations may stop at the supplied count without encountering a null, subject to each function's preconditions. memcpy and memmove operate on counted bytes, not necessarily strings. This one convention explains most string behaviour in C, including its classic bugs: a missing terminator, a buffer too small for the terminator, and the difference between the size of an array and the length of the string inside it.
Conventions: C11, LP64, ASCII character codes. Functions are from string.h unless noted.
Prerequisites
- Arrays and Their Relation to Pointers: array decay,
a[i]as*(a + i),char *versuschar []. - Pointers: pointer arithmetic and
*p++.
Core concepts
Length versus size
For char s[] = "GATE"; the array holds the five characters 'G' 'A' 'T' 'E' '\0'.
- Length
strlen(s)counts characters before the first'\0': 4. - Size
sizeof scounts bytes of the array: 5. char t[10] = "CS";has length 2 and size 10; the 8 unused bytes are set to'\0'.char w[8] = "ab\0cd";has length 2:strlenstops at the first null even though more characters follow.char u[] = {'h', 'i'};has size 2 and no terminator: it is a character array but not a string, and passing it tostrlenorprintf("%s")reads past its end (undefined behaviour).- The empty string
""has length 0 and size 1.
String literals
A string literal such as "GATE" is a static-duration array of char. Its storage contains every explicit literal character (including embedded nulls), plus the appended terminating null. Without embedded nulls its size equals strlen + 1; for "ab\0cd", literal size is 6 but strlen is 2.
- Adjacent literals are concatenated at compile time:
"con" "cat" "enation"is the single literal"concatenation". - A literal is an array, so it can be indexed:
"hello"[1]is'e', and*("hello" + 4)is'o'. - Modifying a string literal is undefined behaviour, even though its type in C is
char [N]and notconst char [N]. On Linux literals are stored in read-only memory, and a write crashes. Whether two identical literals share storage is unspecified.
char s[] versus char *p
char s[] = "GATE"; /* array of 5 chars, a modifiable copy of the literal */
const char *p = "GATE"; /* pointer to the literal itself */| Property | char s[] = "GATE"; | const char *p = "GATE"; |
|---|---|---|
sizeof | 5 (the array) | 8 (the pointer, LP64) |
strlen | 4 | 4 |
s[0] = 'g' / p[0] = 'g' | allowed for the writable array | through const char*: constraint violation requiring a diagnostic; modifying the literal via a non-const access path: undefined behaviour |
s = ... / p = ... | does not compile (arrays are not assignable) | allowed: p can point elsewhere |
| Storage | automatic (inside the current frame) | the literal has static duration |
Writing const char * for pointers to literals lets the compiler catch accidental writes.

char s[] = "GATE" as five modifiable cells on the stack ending in a null character, beside const char *p = "GATE": an 8-byte pointer that refers to a separate read-only literal of five bytes.The main library functions
| Function | Effect | Returns | Watch out for |
|---|---|---|---|
strlen(s) | counts characters before '\0' | size_t | time; not the array size |
strcpy(d, s) | copies s including '\0' into d | d | d must have room for strlen(s) + 1 bytes |
strncpy(d, s, n) | copies at most n characters | d | if s has n or more characters, no '\0' is written; if fewer, pads with '\0' up to n |
strcat(d, s) | appends s to the end of d | d | d must hold both strings plus '\0' |
strcmp(a, b) | compares lexicographically by unsigned char values | negative, 0 or positive | not necessarily , 0, 1 |
strchr(s, c), strrchr(s, c) | first or last occurrence of c | pointer or NULL | |
strstr(h, n) | first occurrence of substring n in h | pointer or NULL | |
memcpy(d, s, n) | copies n bytes | d | undefined if the regions overlap |
memmove(d, s, n) | copies n bytes as if through a temporary | d | safe for overlapping regions |
How strcmp orders strings. It compares character by character; the first differing character decides, and a string that is a proper prefix of the other is smaller. So "abc" < "abd", "ab" < "abc", "b" > "abc", and "Z" < "a" because in ASCII uppercase letters (65 to 90) come before lowercase ones (97 to 122).
Comparing strings with == compares addresses, not contents. For two arrays char s1[] = "hi", s2[] = "hi";, s1 == s2 is 0 while strcmp(s1, s2) == 0 is 1.
Input and output
printf("%s", s)prints characters up to the terminator;%cprints one character.scanf("%s", buf)skips leading white space and reads one word, stopping at the next white space; give a width ("%15s"for a 16-byte buffer) to avoid overflow. On inputhello world again,scanf("%15s %15s", w1, w2)storeshelloandworld.- With a valid buffer of capacity at least
nandn > 1,fgets(buf, n, stdin)reads at mostn-1characters and keeps a newline if it fits. Check its return value: a successful read null-terminates; on EOF/error, handle the failure before using the buffer as a string. getswas removed in C11 because it cannot limit the input size.
How it works: string functions with pointers
The library functions are short pointer loops. Writing them is a useful exercise.
size_t my_strlen(const char *s) {
const char *p = s;
while (*p) p++; /* stop at '\0' */
return p - s; /* elements between the two pointers */
}
void my_strcpy(char *d, const char *s) {
while ((*d++ = *s++)) /* copy, then test the copied char */
; /* the '\0' is copied before the loop stops */
}In my_strcpy, each iteration evaluates *d = *s (the value of the assignment is the copied character), advances both pointers, and stops after copying the terminating '\0', whose value 0 ends the loop.
Reversing a string in place swaps s[i] and s[j] while i < j, with i from 0 and j from strlen(s) - 1. The empty string must be handled separately, because strlen(s) - 1 is a huge size_t when the length is 0.
Worked examples
All outputs below are for C11 with gcc on x86-64 Linux (LP64).
Example 1 (sizes and lengths). For char s[] = "GATE"; const char *p = "GATE"; char t[10] = "CS";, the expression list sizeof s, strlen(s), sizeof p, strlen(p), sizeof t gives 5 4 8 4 10.
Example 2 (library calls).
char a[20] = "data"; char b[] = "base";
strcat(a, b); /* a is "database", length 8 */
char c[20]; strcpy(c, a); c[4] = '\0';/* c is "data" */
char m[] = "abcdef";
memmove(m + 2, m, 3); /* m is "ababcf" */For memmove(m + 2, m, 3) the source abc and destination positions 2 to 4 overlap; memmove copies as if through a buffer, giving a b a b c f. With memcpy the result would be undefined.
Example 3 (strncpy does not always terminate). char d[4]; strncpy(d, "hello", 4); fills d with h e l l and writes no '\0'. char e[6]; strncpy(e, "hi", 6); writes h i followed by four '\0' characters.
Example 4 (searching). In "operating systems", strchr finds 'r' at index 3, strstr finds "sys" at index 10, and strrchr(hay, 's') points to the final "s".
Example 5 (pointer arithmetic on strings). char str[] = "GATE2027"; char *q = str + 4; then printf("%s %c %s", q, *q, str + 6) prints 2027 2 27.
Example 6 (counting characters). Counting vowels in "Programming in C" with strchr("aeiouAEIOU", *s) gives 4 (o, a, i, i).
Important properties and observations
- A string needs one byte more than its length.
sizeofgives array size (or pointer size);strlengives the length and costs .- Calling
strlenin a loop condition,for (i = 0; i < strlen(s); i++), makes the loop unless the compiler hoists the call. - String literals must not be modified; use
const char *to point at them. strcmpreturns a sign, not necessarily ; compare its result with 0.strncpymay leave the destination unterminated.memcpyon overlapping regions is undefined; usememmove.
GATE examination perspective
- Output questions with
printf("%s", p + k)print from position to the terminator. sizeofversusstrlenon arrays, pointers, and arrays with embedded nulls.- Pointer loops such as
while (*s++)orwhile ((*d++ = *s++)): track where each pointer ends (one past the terminator in the first case). - Modifying a literal through
char *p = "..."makes the program undefined; the same mistake can appear inside a string-reversal function. - Lexicographic comparison rules for
strcmp, including case and prefixes.
Summary
Sources
- ISO/IEC 9899:2011 (C11), committee draft N1570, (consulted 2026-11-09), Section 6.4.5 (string literals), 6.7.9 (initialization of character arrays), 7.24 (string handling: 7.24.2 copying functions, 7.24.3 concatenation, 7.24.4 comparison, 7.24.5 search, 7.24.6.3 strlen), 7.21.6.2 (fscanf, the %s directive), 7.21.7.2 (fgets); Foreword (removal of gets).
- B. W. Kernighan and D. M. Ritchie, The C Programming Language, 2nd ed., Prentice Hall, 1988: Sections 5.5 (character pointers and functions) and Appendix B3 (string functions).