strcmp(3)

C library function

Compares two strings byte by byte and returns a negative, zero or positive int. Why == does not compare strings, and why only the sign counts.

Synopsis
#include <string.h>
int strcmp(const char *s1, const char *s2);

Description

strcmp walks through s1 and s2 together and stops at the first byte that differs, or at the null byte if they are identical all the way through. The standard specifies that the two bytes being compared are read as unsigned char. The comparison therefore follows byte values, not the alphabetical order of any language: the locale is not taken into account, which is the job of strcoll.

Return value

A negative integer if s1 sorts before s2, zero if the two strings are equal, a positive integer if s1 sorts after s2. Only the sign is guaranteed. The exact value depends on the implementation, as the example shows: the same comparison gives -32 with glibc on this ARM64 machine and -1 with musl.

Pitfalls

== compares addresses. if (answer == "yes") asks whether answer and the string literal are stored at the same place, which almost never happens. GCC flags it with -Wall: comparison with string literal results in unspecified behavior. To compare the contents, write strcmp(answer, "yes") == 0.

if (strcmp(a, b)) is true when the strings are different, since equality returns 0. Always write the comparison out in full, == 0 or != 0; the code reads better.

Never test == 1 or == -1: it works with one library and not with another. Compare with zero, < 0 or > 0.

The order of byte values can surprise you. Uppercase letters (65 to 90 in ASCII) come before lowercase letters (97 to 122), so "Zoe" sorts before "alice". In UTF-8, an accented letter starts with a byte above 127, so "éclair" sorts after "zoo".

Passing NULL to strcmp is undefined behavior, usually a segmentation fault.

Example

The program compares its arguments two at a time. They arrive at run time, so the C library really computes the result: with two string literals, GCC can do the comparison itself at compile time.

compare.c
#include <stdio.h>#include <string.h>
int main(int argc, char **argv){    for (int i = 1; i + 1 < argc; i += 2)        printf("strcmp(\"%s\", \"%s\") = %d\n", argv[i], argv[i + 1], strcmp(argv[i], argv[i + 1]));    return 0;}

With glibc (Debian 13):

Terminal
$ gcc -Wall -Wextra compare.c -o compare$ ./compare abc abc abc abd abd abc ab abc Zoe alice éclair zoostrcmp("abc", "abc") = 0strcmp("abc", "abd") = -32strcmp("abd", "abc") = 32strcmp("ab", "abc") = -198strcmp("Zoe", "alice") = -7strcmp("éclair", "zoo") = 73

With musl, in the Grademe exercise environment:

Terminal
$ gcc -Wall -Wextra compare.c -o compare$ ./compare abc abc abc abd abd abc ab abc Zoe alice éclair zoostrcmp("abc", "abc") = 0strcmp("abc", "abd") = -1strcmp("abd", "abc") = 1strcmp("ab", "abc") = -99strcmp("Zoe", "alice") = -7strcmp("éclair", "zoo") = 73

The signs match line for line, the values do not. musl returns the difference between the two bytes ('c' - 'd' is -1, '\0' - 'c' is -99), while the glibc version for this ARM64 machine returns something else. Both conform to the standard.

C

strcmp

Write gm_strcmp, which walks both strings to the first byte that differs and returns a value whose sign gives their order.

Open the exercise

Now write it yourself.