Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does strcmp() in a template function return a different value?

Tags:

I am reading again "C++ Primer, 5th edition". In chapter 16 about templates, there is an example of "template Non-type parameters":

template<unsigned N, unsigned M>
int compare(const char (&p1)[N], const char (&p2)[M])
{
    return strcmp(p1, p2);
}

int main()
{

    cout << compare("hi", "mom") << endl;
    cout << strcmp("hi", "mom") << endl;


    std::cout << "\ndone!\n";
}
  • As we know, strcmp() compares two character strings and returns 0 for equality, a positive value if str1 is greater than str2, and a negative value if str1 is less than str2, and this is what I get inside main() calling strcmp().

  • The problem is in the book example that calls strcmp() inside the template function, so when I run the program I get:

output:

-5
-1

What is the problem in the code? And why are the two giving different values for the same arguments?

like image 601
Maestro Avatar asked Oct 22 '20 18:10

Maestro


People also ask

What does strcmp function return?

The return value from strcmp is 0 if the two strings are equal, less than 0 if str1 compares less than str2 , and greater than 0 if str1 compares greater than str2 .

Is strcmp faster than ==?

While comparing the two function, in a loop repeted 500'000'000 times, strcmp execute too much fast, about x750 times faster. The execution time of that function is 3.058s , while strcmp only 0.004s .

Does strcmp check for NULL?

The strcmp() built-in function compares the string pointed to by string1 to the string pointed to by string2 The string arguments to the function must contain a NULL character ( \0 ) marking the end of the string.


1 Answers

This is a compiler optimization applied when strcmp is passed literal parameters, even on -O0. See this compiler explorer link: https://godbolt.org/z/T4EKxr

#include <cstring>

template<unsigned N, unsigned M>
int compare(const char (&p1)[N], const char (&p2)[M])
{
    return strcmp(p1, p2);
}

int a() {
    return compare("hi", "mom");
}

int b() {
    return strcmp("hi", "mom");
}

The generated assembly:

.LC0:
        .string "mom"
.LC1:
        .string "hi"
a():
        push    rbp
        mov     rbp, rsp
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:.LC1
        call    int compare<3u, 4u>(char const (&) [3u], char const (&) [4u])
        pop     rbp
        ret
b():
        push    rbp
        mov     rbp, rsp
        mov     eax, -1
        pop     rbp
        ret
int compare<3u, 4u>(char const (&) [3u], char const (&) [4u]):
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     QWORD PTR [rbp-8], rdi
        mov     QWORD PTR [rbp-16], rsi
        mov     rdx, QWORD PTR [rbp-16]
        mov     rax, QWORD PTR [rbp-8]
        mov     rsi, rdx
        mov     rdi, rax
        call    strcmp
        leave
        ret

As we can see, for b() above, gcc is optimizing the call to strcmp to just a -1, whereas it actually calls strcmp for a(). This is valid behavior, as strcmp returns:

Negative value if lhs appears before rhs in lexicographical order.

Zero if lhs and rhs compare equal.

Positive value if lhs appears after rhs in lexicographical order.

-1 is negative.

If we turn on optimizations, gcc will similarly optimize a().

like image 88
Justin Avatar answered Sep 27 '22 19:09

Justin