Strings

C · Lesson 17 of 32 7 min read Updated

Names, addresses, messages and file names are all text. Many languages have a built-in string type that manages its own memory, but C does not. In C, a string is simply an array of char values that ends with a special character, the null character '\0'.

This simple design makes strings fast and flexible, but it also puts you in charge of the memory. In this lesson you will learn how strings are stored, use the standard string functions, compare and search text, and copy strings safely without overflowing a buffer.

How C stores a string

When you write char city[] = "Jaipur";, the compiler creates an array of 7 characters: the 6 letters plus a '\0' at the end that marks where the string stops.

Index0123456
Value'J''a''i''p''u''r''\0'
C
#include <stdio.h>
#include <string.h>

int main(void)
{
    char city[] = "Jaipur";

    printf("City: %s\n", city);
    printf("strlen: %zu characters\n", strlen(city));
    printf("sizeof: %zu bytes\n", sizeof city);

    for (size_t i = 0; city[i] != '\0'; i++) {
        printf("[%c]", city[i]);
    }
    printf("\n");

    city[0] = 'j';
    printf("Changed: %s\n", city);
    return 0;
}
Output
City: Jaipur
strlen: 6 characters
sizeof: 7 bytes
[J][a][i][p][u][r]
Changed: jaipur

strlen counts the characters before the '\0'; sizeof gives the whole array, including the terminator. Every string function, and printf with %s, keeps reading until it finds '\0'. If the terminator is missing, they run off the end of the array into whatever memory follows. So when you size an array for text, always add one for the '\0': a 10-digit mobile number needs char mobile[11];.

Arrays versus string literals

There are two common ways to name a string:

  • char name[] = "Priya"; creates your own array, copied from the literal. You can change its characters.
  • const char *name = "Priya"; makes name point to the literal itself, which lives in read-only memory. You can read it but not change it.

Changing a string literal, for example through char *p = "Priya"; p[0] = 'p';, is undefined behaviour and usually crashes the program. Always write const char * for pointers to literals so the compiler stops such mistakes. You will learn exactly what * means in Pointers, NULL & nullptr; for now, read const char * as "a string I will not change".

Comparing and searching

The header string.h declares the standard string functions. These are the ones you will use most:

FunctionWhat it does
strlen(s)Number of characters before the '\0'
strcmp(a, b)0 if equal, negative if a sorts first, positive if b sorts first
strncmp(a, b, n)Like strcmp, but compares at most n characters
strchr(s, c)Finds the first character c in s
strstr(s, t)Finds the first place where text t appears in s
memcpy(d, s, n)Copies n bytes
strdup(s)Makes a new copy in allocated memory (standard since C23)
C
#include <stdio.h>
#include <string.h>

int main(void)
{
    char entered[] = "pune";
    const char *saved = "Pune";

    if (strcmp(entered, saved) == 0) {
        printf("Same city\n");
    } else {
        printf("Different: strcmp is case-sensitive\n");
    }

    printf("Patna before Pune? %s\n", strcmp("Patna", "Pune") < 0 ? "yes" : "no");

    const char *address = "14 MG Road, Pune 411001";
    const char *comma = strchr(address, ',');
    const char *city = strstr(address, "Pune");
    if (comma && city) {
        printf("After the comma: [%s]\n", comma + 1);
        printf("From the city on: %s\n", city);
    }
    return 0;
}
Output
Different: strcmp is case-sensitive
Patna before Pune? yes
After the comma: [ Pune 411001]
From the city on: Pune 411001

strcmp compares character codes one by one. Lowercase 'p' has a larger code than 'P', so the strings differ. Only test the sign of the result (== 0, < 0, > 0); the exact number varies between systems. strchr and strstr return the position where the match starts, so printing from there shows the rest of the string; they return a null pointer when nothing is found, which is why the if checks them first. comma + 1 starts one character after the comma.

Never compare strings with ==. For two arrays, == compares their memory addresses, which are always different, so entered == saved is false even when the text matches.

Building strings safely with snprintf

The classic functions strcpy (copy) and strcat (append) write as many characters as the source has, with no idea how big the destination is. If the source is too long, they overflow the buffer. The safest everyday tool is snprintf, which works like printf but writes into an array and never writes more than the size you give it, including the '\0'.

C
#include <stdio.h>
#include <string.h>

int main(void)
{
    char first[] = "Isha";
    char last[] = "Verma";
    char full[20];

    snprintf(full, sizeof full, "%s %s", first, last);
    printf("Full name: %s (%zu chars)\n", full, strlen(full));

    char code[8];
    int needed = snprintf(code, sizeof code, "%s-%d", "ROHTAK", 2026);
    printf("Stored: %s\n", code);
    if (needed >= (int) sizeof code) {
        printf("Truncated: needed %d characters, room for %zu\n", needed, sizeof code - 1);
    }
    return 0;
}
Output
Full name: Isha Verma (10 chars)
Stored: ROHTAK-
Truncated: needed 11 characters, room for 7

snprintf returns the length the full text would have had. If that is greater than or equal to the buffer size, the text was cut short. The result is still a valid, terminated string, and your program can decide what to do, instead of silently corrupting memory.

Buffer overflows

Here is the bug that snprintf prevents:

C
char name[6];
strcpy(name, "Ananya Krishnan");   // 16 bytes into a 6-byte array!

strcpy writes all 15 letters and the '\0', overwriting 10 bytes of memory that belong to something else. GCC sometimes notices fixed cases like this and warns, but when the text comes from user input, nothing warns you. Overflows like this have caused real security breaches, because attackers can craft input that overwrites important data. The fixes are to size buffers generously, check lengths with strlen before copying, and use size-limited functions such as snprintf and fgets.

strncpy looks like a safe version, but it has a trap: if the source is too long, it copies exactly n characters and does not add a '\0'. Prefer snprintf(dest, sizeof dest, "%s", src).

Working with characters: ctype.h

The header ctype.h classifies and converts single characters: isalpha, isdigit, isspace, isupper, islower, toupper and tolower. Pass them an unsigned char value, because a negative char value is not allowed.

C
#include <ctype.h>
#include <stdio.h>

int main(void)
{
    char text[] = "Train 10042 leaves Pune at 06:15";
    int letters = 0, vowels = 0, digits = 0;

    for (size_t i = 0; text[i] != '\0'; i++) {
        unsigned char ch = (unsigned char) text[i];
        if (isalpha(ch)) {
            letters++;
            int lower = tolower(ch);
            if (lower == 'a' || lower == 'e' || lower == 'i' || lower == 'o' || lower == 'u') {
                vowels++;
            }
        } else if (isdigit(ch)) {
            digits++;
        }
        text[i] = (char) toupper(ch);
    }
    printf("%s\n", text);
    printf("Letters: %d, vowels: %d, digits: %d\n", letters, vowels, digits);
    return 0;
}
Output
TRAIN 10042 LEAVES PUNE AT 06:15
Letters: 17, vowels: 8, digits: 9

toupper leaves non-letters unchanged, so the digits and the colon stay as they were. This loop changes text in place, which works because text is our own array, not a string literal.

Common mistakes

  • No room for '\0'. char code[5] = "Rohan"; is legal C, but the array has no terminator, so printf("%s", code) reads past its end. Use char code[] = "Rohan"; or a size of at least 6.
  • Comparing with ==. Use strcmp(a, b) == 0.
  • Changing a string literal. Copy it into a char array first.
  • scanf("%s", ...) and strcpy without limits. Use fgets and snprintf with sizeof.
  • Confusing sizeof and strlen. sizeof is the array's capacity; strlen is the text's current length.

Key points

  • A C string is a char array ending with '\0'; always leave room for the terminator.
  • strlen counts characters before '\0'; sizeof gives the whole array size.
  • String literals are read-only; use const char * to point at them and char[] for text you change.
  • Compare with strcmp and test only the sign of the result; search with strchr and strstr.
  • Build and copy strings with snprintf, which never overflows and reports truncation.
  • Pass unsigned char values to the ctype.h functions.

Exercise

Write a function bool is_palindrome(const char *s) that returns true when a word reads the same forwards and backwards. Use two indexes, one from the start and one from the end, moving towards the middle. Test it with "malayalam", "kochi" and "level".

Show solution
C
#include <stdio.h>
#include <string.h>

bool is_palindrome(const char *s)
{
    size_t len = strlen(s);
    if (len == 0) {
        return true;
    }
    size_t left = 0;
    size_t right = len - 1;
    while (left < right) {
        if (s[left] != s[right]) {
            return false;
        }
        left++;
        right--;
    }
    return true;
}

int main(void)
{
    const char *words[] = {"malayalam", "kochi", "level"};
    for (int i = 0; i < 3; i++) {
        printf("%s: %s\n", words[i], is_palindrome(words[i]) ? "palindrome" : "not a palindrome");
    }
    return 0;
}
Output
malayalam: palindrome
kochi: not a palindrome
level: palindrome

The empty-string check matters because len - 1 on a size_t of 0 would wrap around to a huge number. words is an array of read-only strings, which you will study further in Pointer Arithmetic & Arrays.

Quick Quiz

1. How many bytes does char word[] = "Kochi"; occupy?

2. How should you check whether two strings a and b contain the same text?

3. What does snprintf(buf, sizeof buf, "%s", text) do if text is too long?

4. What is wrong with char *s = "Pune"; s[0] = 'p';?

Learn C with a mentor

Free lessons get you started. Our Java, C & C++ Programming training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call