Menu

String Functions in C: strlen, strcpy, strcat, strcmp, and More

A working tour of <string.h>: measuring with strlen, copying with strcpy and strncpy, joining with strcat, comparing with strcmp, searching with strchr and strstr - and how to size buffers so none of them overflow.

This page includes runnable editors - edit, run, and see output instantly.

Every function in <string.h> is a small loop over a char array that stops at the null terminator. Once that is clear, the library stops looking like a list of cryptic names and starts looking like the handful of loops you would have written yourself. This page covers the ones that carry almost all real work - and the sizing rules that keep them from writing past the end of your buffers.

Everything here needs one include:

#include <string.h>

strlen: How Many Characters

strlen counts bytes up to but not including the terminator. It returns size_t, so print it with %zu.

strlen is 6; sizeof is 32. The first is the text, the second is the storage - see strings for why that distinction matters so much.

strlen walks the whole string each call, so do not put it in a loop condition over the same unchanging string:

/* re-counts the whole string on every iteration */
for (size_t i = 0; i < strlen(s); i++) { ... }

/* count once */
size_t n = strlen(s);
for (size_t i = 0; i < n; i++) { ... }

strcpy and strncpy: Copying

strcpy(dst, src) copies the characters plus the terminator. It has no idea how large dst is, so the caller must guarantee dst holds at least strlen(src) + 1 bytes.

strncpy takes a maximum count, but it has a famous trap: when the source is at least n bytes long it copies exactly n characters and no terminator. Always terminate yourself:

The sizeof dst - 1 plus explicit terminator is the pattern to memorize. (strncpy also pads short sources with zeros out to n bytes, which is wasted work for large buffers - it was designed for fixed-width records, not for safety.)

strcat and strncat: Joining

strcat(dst, src) appends src to the end of whatever is already in dst. The destination must hold both strings plus one terminator, and it must already contain a valid string - appending to uninitialized memory is undefined behavior.

Note char path[64] = "/home/ada"; rather than char path[64]; - the initializer is what makes it a valid string for the first strcat.

strncat(dst, src, n) appends at most n characters and always adds a terminator, so n is the space remaining, not the total buffer size:

strncat(dst, src, sizeof dst - strlen(dst) - 1);

Repeated strcat calls re-scan the destination to find its end every time. Building a long string in a loop that way is quadratic; for anything sizeable, track your own write position or use snprintf, covered in string conversion.

strcmp: Comparing

== compares addresses, so it is false for two distinct arrays holding the same text. strcmp compares the characters and returns the sign of the difference.

Three rules:

  • Test strcmp(a, b) == 0 for equality. Writing if (strcmp(a, b)) means "if they differ", which reads backwards to almost everyone.
  • Only the sign is defined. Do not compare the result against 1 or -1.
  • The ordering is by byte value, so "Zebra" sorts before "apple" in ASCII. For case-insensitive comparison, lowercase both copies first - the widely seen strcasecmp is a POSIX extension, not standard C.

strncmp(a, b, n) compares only the first n characters, which is the clean way to test a prefix:

strchr and strstr: Searching

strchr(s, ch) finds the first occurrence of a character; strrchr finds the last. strstr(haystack, needle) finds a substring. All three return a pointer into the original string, or NULL when there is no match.

Subtracting the returned pointer from the start gives the index; its type is ptrdiff_t, printed with %td. Because the result points inside the original array, at + 1 is the rest of the string with no copying at all - a very common C idiom.

Always check for NULL before dereferencing. strchr(email, '@') + 1 on a string with no @ computes an address from NULL and the program is no longer defined.

Splitting a String

There is no split function, but strchr plus a terminator does the job in place. This example cuts an email into user and domain:

memcpy copies an exact byte count without caring about terminators, which is what you want when you already know the length. The terminator is then written by hand.

A Worked Example: Normalizing a Name

Putting the tour together - trim, compare, copy, and join:

Every write is bounded by the destination's own sizeof, and every strncpy is followed by an explicit terminator. That discipline, applied consistently, is what makes C string code safe.

Sizing Rules to Keep

  • strcpy needs strlen(src) + 1 bytes free in the destination.
  • strcat needs strlen(dst) + strlen(src) + 1.
  • After strncpy, write dst[n - 1] = '\0' yourself.
  • For strncat, the count is the space left over: sizeof dst - strlen(dst) - 1.
  • Check strchr and strstr for NULL before using the result.
  • Use sizeof dst only when dst is a real array. Inside a function taking char *dst, sizeof gives the pointer's size - pass the buffer length as a separate parameter.

Frequently Asked Questions

What does strcmp return in C?

Zero when the two strings are identical, a negative value when the first sorts before the second, and a positive value when it sorts after. Only the sign is meaningful - never assume it returns -1 or 1. The test you want is almost always if (strcmp(a, b) == 0).

Why can't I compare strings with == in C?

a == b compares the two addresses, not the characters. Two separate arrays holding "cat" live at different addresses, so the comparison is false even though the text matches. Use strcmp(a, b) == 0 from <string.h>.

What is the difference between strcpy and strncpy?

strcpy copies until the source's '\0' with no idea how big the destination is. strncpy stops after at most n bytes - but if the source is that long it copies no terminator, so you must write dst[n - 1] = '\0'; yourself. Neither is automatically safe; the size discipline is yours.

How do I find a substring in C?

strstr(haystack, needle) returns a pointer to the first occurrence, or NULL if there is none. Because it returns a pointer into the original string, found - haystack gives you the index. Use strchr when you are looking for a single character.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED