Standard Library[lib]

C++ string comparison: ==, compare() and strcmp

Section
Standard Library [lib]
Standard
C++17
Compilers
MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2, Clang 23.1 (libc++)
Published
Length
7 min read
In this answer7 clauses
  1. 1How do you compare two strings in C++?
  2. 2How do you tell which string comes first?
  3. 3What does std::string::compare() return?
  4. 4Why does == fail on C strings, and how do you use strcmp?
  5. 5How do you compare strings ignoring case?
  6. 6How do you check how a string starts or ends?
  7. 7Which comparison should you use?

Abstract

Test two std::string values with == and !=, which compare the text and are case sensitive, and order them with < and >, which compare character codes, so “Zebra” comes before “apple”. compare() returns a negative number, zero or a positive number, and only the sign is promised. For C strings use std::strcmp, because == on two char pointers compares addresses, not text.

Two aligned rows of eight character cells. An arrow from the left stops at the fourth column, the first pair that differs, in blue; the three pairs before it match, the cells after it are grey.
Figure 1 — Two strings are compared cell by cell until one differs.

For C++ string comparison, use == to test whether two std::string values hold the same text, and < or > to see which comes first. compare() gives both answers as one number: negative, zero or positive. For C strings (char*), use strcmp, because == on two pointers compares addresses, not text.

[Example 1:
#include <iostream>
#include <string>

int main() {
    std::string a = "apple";
    std::string b = "banana";

    std::cout << std::boolalpha;
    std::cout << (a == b) << '\n';        // false
    std::cout << (a != b) << '\n';        // true
    std::cout << (a < b) << '\n';         // true
    std::cout << (a == "apple") << '\n';  // true
}

— end example]

It printed:

[Example 2:
false
true
true
true

— end example]

This answer is written for C++17; the lines that need C++20 say so. The samples were built with MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2 and Clang 23.1 (libc++), and all four printed the same unless a sentence says otherwise.

How do you compare two strings in C++?

Compare two strings with == and !=: two std::strings are equal when they have the same length and the same character at every position (the string comparison operators on cppreference). The same operators work between a std::string and a string literal, as a == "apple" did above: the string’s own operator compares the literal’s characters.

Comparison is case sensitive. “Ada” and “ada” differ in their first character, so == calls them different; ignoring case takes a few lines of your own, shown below. If std::string itself is new to you, how a C++ string works covers creating, joining and searching one.

How do you tell which string comes first?

Use <, >, <= and >=, which, like ==, also take a literal on one side: they compare in dictionary order by character code. The first character that differs decides, and if one string ends before any character differs, the shorter string comes first.

[Example 3:
std::cout << (zebra < apple) << '\n';    // true: 'Z' is 90, 'a' is 97
std::cout << (ten < nine) << '\n';       // true: '1' comes before '9'
std::cout << (app < apple) << '\n';      // true: the prefix is shorter
std::cout << (apple < apricot) << '\n';  // true: 'p' comes before 'r'

— end example]

The variables are std::strings: zebra holds “Zebra”, ten “10”, nine “9”, app “app” and apricot “apricot”.

The word apple above apricot, letter by letter. The first two columns match; the third holds p over r, marked as less than; the rest is grey, not looked at. Below, app above apple: all three letters match.
Figure 2 — The first character that differs decides the order; if none does, the shorter string comes first.

Three of those results surprise people who sort by eye:

Strings Comes first Why
“Zebra” and “apple” “Zebra” ‘Z’ is 90, ‘a’ is 97
“10” and “9” “10” digits compare as characters, not numbers
“app” and “apple” “app” all of it matches, so the shorter wins

In ASCII every uppercase letter has a lower code than every lowercase one, so a capitalized word sorts before every lowercase word. To put “9” before “10”, compare them as numbers: convert each string to an int first.

What does std::string::compare() return?

a.compare(b) returns a negative number when a comes first, zero when both hold the same text, and a positive number when a comes after, by the same rule as < (std::basic_string::compare). Only the sign is promised: for “apple” against “cherry”, compare() returned -2 with GCC’s library and -1 with Microsoft’s and with libc++. So test < 0, == 0 or > 0, never == -1. Here sign() turns the result into -1, 0 or 1:

[Example 4:
int sign(int n) {
    return (n > 0) - (n < 0);
}

int main() {
    std::string a = "apple";
    std::string b = "banana";

    std::cout << std::boolalpha;
    std::cout << sign(a.compare(b)) << '\n';        // -1: a comes first
    std::cout << sign(a.compare("apple")) << '\n';  // 0: the same text
    std::cout << sign(b.compare(a)) << '\n';        // 1: b comes after

    std::string pie = "apple pie";
    // The 5 characters from position 0, against "apple":
    std::cout << (pie.compare(0, 5, "apple") == 0) << '\n';  // true
}

— end example]

It printed:

[Example 5:
-1
0
1
true

— end example]

The last line compares part of a string. compare() comes in three shapes:

Call Compares
a.compare(b) all of a with b, a string or a C string
a.compare(pos, count, b) count characters of a from pos with b
a.compare(pos, count, b, pos2, count2) that part of a with count2 characters of b from pos2

A pos beyond the string’s size throws std::out_of_range. When a yes or no is all you need, == and < say it more plainly than compare().

Why does == fail on C strings, and how do you use strcmp?

A C string is a null-terminated array of char that you handle through a pointer, and == on two pointers asks whether they point at the same memory, not whether the text matches ([expr.eq]). Two arrays that both hold “apple” are two places in memory:

[Example 6:
char first[] = "apple";
char second[] = "apple";
const char* p = first;
const char* q = second;

std::cout << std::boolalpha;
std::cout << (p == q) << '\n';               // false: two addresses
std::cout << std::strcmp(p, q) << '\n';      // 0: the same text
std::cout << (std::string(p) == q) << '\n';  // true: text again

— end example]

It printed:

[Example 7:
false
0
true

— end example]

std::strcmp, from <cstring>, compares the text; its name is “string compare” cut to str and cmp. std::string(p) makes a std::string from p, and as soon as one side is a std::string, == compares text again. A pointer against a literal, p == "apple", compiles too and still compares addresses: every compiler here warns about it. Here it is false; when the pointer was itself set from a literal, the result is unspecified, because two copies of one literal may or may not share an address.

strcmp returns a negative number, zero or a positive number, like compare(), and again only the sign is promised: it is the sign of the difference between the first pair of characters that differ, each read as an unsigned char (std::strcmp). With the same sign() helper, and s a std::string holding “apple”:

[Example 8:
std::cout << sign(std::strcmp("apple", "banana")) << '\n';  // -1
std::cout << sign(std::strcmp("banana", "apple")) << '\n';  // 1
std::cout << std::strcmp("apple", "apple") << '\n';         // 0
std::cout << std::strcmp(s.c_str(), "apple") << '\n';       // 0

— end example]

s.c_str() hands a std::string’s text to a function that wants a C string. Both arguments must point at null-terminated text; anything else is undefined behavior, which means the program may do anything at all.

How do you compare strings ignoring case?

The standard library has no case-insensitive string comparison, so write one: compare character by character after std::tolower, converting each char to unsigned char first.

[Example 9:
bool equals_ignore_case(std::string_view a, std::string_view b) {
    return std::equal(a.begin(), a.end(), b.begin(), b.end(),
        [](char x, char y) {
            // tolower needs a value that fits in unsigned char
            return std::tolower(static_cast<unsigned char>(x)) ==
                   std::tolower(static_cast<unsigned char>(y));
        });
}

— end example]

This form of std::equal, from <algorithm>, takes both ranges whole: it returns false when the lengths differ, and otherwise compares the characters pair by pair with the lambda. The cast is not decoration: std::tolower from <cctype> has undefined behavior for a value that is neither EOF nor representable as an unsigned char (std::tolower). equals_ignore_case("Ada", "ada") printed true and equals_ignore_case("Ada", "Adam") false on all four. std::tolower follows the program’s locale, its language settings; in the “C” locale every program starts in, it folds only the ASCII letters A to Z, not accented ones.

How do you check how a string starts or ends?

Since C++20, call starts_with() and ends_with() (std::basic_string::starts_with); before that, test a prefix with compare(0, n, prefix) == 0, where n is the prefix’s length:

[Example 10:
std::cout << name.starts_with("report") << '\n';  // C++20: true
std::cout << name.ends_with(".pdf") << '\n';      // C++20: true
std::cout << (name.compare(0, 6, "report") == 0) << '\n';  // true

— end example]

With name holding “report.pdf”, every line prints true. As C++17 the first two do not compile; GCC says std::string “has no member named ‘starts_with’”. The C++20 flag is -std=c++20 for GCC and Clang and /std:c++20 for MSVC; Since When’s C++20 table gives the first GCC, Clang and MSVC releases for its features.

Before C++20, test a suffix with compare() from size() - n, after checking that the string is at least n long; without that check, a string shorter than the suffix throws std::out_of_range:

[Example 11:
std::cout << (name.size() >= 4 &&
              name.compare(name.size() - 4, 4, ".pdf") == 0)
          << '\n';  // true

— end example]

Built as C++17 on all four, it printed true; on “a”, the form without the size check threw std::out_of_range.

Which comparison should you use?

Task Write
Same text? a == b
Which comes first? a < b
Before, equal or after, in one call a.compare(b), then test the sign
Two C strings std::strcmp(p, q) == 0
Ignoring case equals_ignore_case(a, b), from above
Prefix or suffix starts_with(), ends_with() (C++20)
Sorting a list of strings std::sort, which uses <

std::sort puts “Zebra” before “apple” for the reason in the table of surprises; writing a comparator for std::sort and std::map changes the order. Standard library has the other answers on strings and containers.

Questions

Is string comparison in C++ case sensitive?

Yes. The operators and compare() compare character codes, and an uppercase letter has a different code from its lowercase form, so “Ada” and “ada” are not equal. To ignore case, compare the two strings character by character after std::tolower, casting each char to unsigned char first.

What is the difference between == and compare() in C++?

The == operator answers one question, equal or not, with a bool. compare() also tells which string comes first, as a negative number, zero or a positive number, and can compare part of a string, as in s.compare(0, 5, “apple”). Use == or < when a yes or no is all you need.

Can you compare a std::string with a char* or a string literal?

Yes. When either side of == is a std::string, the text is compared: with a holding “apple”, a == “apple” is true. Only when both sides are C strings does == compare their addresses instead; compare those with std::strcmp.

What does strcmp return?

A negative number if the first C string comes first, zero if both hold the same text, and a positive number if it comes after. Only the sign is promised, and the numbers differ between C libraries, so test strcmp(p, q) < 0, == 0 or > 0, never == -1 or == 1.

Can you use a string in a switch statement in C++?

No. A switch cannot test a std::string, so write a chain of if and else if, each comparing with ==: if (fruit == “apple”) first, then else if (fruit == “banana”), and so on.

What is the <=> operator for strings?

It is C++20’s three-way comparison: a <=> b gives one result for before, equal and after, an ordering you compare with 0. With a holding “apple” and b holding “banana”, (a <=> b) < 0 is true.

Sources

  1. operator==, !=, <, <=, >, >=, <=> (std::basic_string)cppreference.com
  2. std::basic_string::comparecppreference.com
  3. std::strcmpcppreference.com
  4. [expr.eq] Equality operatorsC++ standard working draft
  5. std::tolowercppreference.com
  6. std::equalcppreference.com
  7. std::basic_string::starts_withcppreference.com

Read next

  1. C++ string to int: std::stoi or std::from_chars, not atoiStandard Library · Miriam Castellanos · written for C++17
  2. C++ int to string: to_string, format or to_chars, not itoaStandard Library · Miriam Castellanos · written for C++17
  3. C++ string (std::string) explained with examplesStandard Library · Miriam Castellanos · written for C++17