oHo
oHo

Reputation: 54591

Why strrchr() returns `char*` instead of `const char*`?

The function char* strrchr(const char *str, int ch) returns a pointer (char*) within str (const char *) where the last occurrence of ch is located.

So we can write the following code without any cast:

#include <string.h>
int main()
{
    const char CONSTSTR[] = "foo/bar/foobar.txt";
    char *ptr = strrchr (CONSTSTR, '/');
    *ptr++ = 'B';
    *ptr++ = 'A';
    *ptr++ = 'D';
}

What is the advantage to return char* instead of const char* ?

EDIT:
As a Shafik Yaghmour pointed out, there are very good answers to How does strchr implementation work?

As my code is in C++, I will use <cstring> instead of <string.h>. Thanks for your answers ;-)

However, the Mike Seymour's answer fits best the question. I have even added a more detailed answer below to clearly say as strrchr() is a C function (overload not permitted), the declaration fits both const and non-const strings. Because strrchr() can be called with a non-const string, the returned string should also be non-const.

Upvotes: 7

Views: 3390

Answers (5)

oHo
oHo

Reputation: 54591

strrchr() from <string.h> is a C function. As C does not permit function overloading, strrchr() has been designed to fit both const and non-const strings.

char* strrchr( const char *str, int ch );

strrchr() may be called with a non-const string, and therefore the returned string should also be non-const as explained in the following examples.

const context without compilation error:

#include <string.h>
int main()
{
    const char CONSTSTR[] = "foo/bar/foobar.txt";
    const char *basename = strrchr (CONSTSTR, '/');
    // basename points to "foobar.txt"
}

non-const context without compilation error:

#include <string.h>
int main()
{
    char nonconst[] = "foo/bar/foobar.txt";
    char *basename = strrchr (nonconst, '/');
    basename[0] = 'G';
    basename[3] = 'D';
    // basename points to "GooDar.txt"
}

Bad usage also without compilation error:

#include <string.h>
int main()
{
    const char CONSTSTR[] = "foo/bar/foobar.txt";
    char *nonconst = strrchr (CONSTSTR, '/');
    *nonconst++ = 'B';
    *nonconst++ = 'A';  // drawback of the unique declaration: 
    *nonconst++ = 'D';  // no compilation error
}

In C++, there are two overloaded functions:

const char* strrchr( const char* str, int ch );  //1st
      char* strrchr(       char* str, int ch );  //2nd

const context uses the 1st one:

#include <cstring>
int main()
{
    const char CONSTSTR[] = "foo/bar/foobar.txt";
    const char *basename = std::strrchr (CONSTSTR, '/');
    // basename points to "foobar.txt"
}

non-const context uses the 2nd one:

#include <cstring>
int main()
{
    char nonconst[] = "foo/bar/foobar.txt";
    char *basename = std::strrchr (nonconst, '/');
    basename[0] = 'G';
    basename[3] = 'D';
    // basename points to "GooDar.txt"
}

Bad usage should produce compilation error:

#include <cstring>
int main()
{
    const char CONSTSTR[] = "foo/bar/foobar.txt";

    char *nonconst = std::strrchr (CONSTSTR, '/');
// Visual C++ v10 (2010)
// error C2440: 'initializing' : cannot convert from 'const char *' to 'char *'

    *nonconst++ = 'B';
    *nonconst++ = 'A';
    *nonconst++ = 'D';
}

But this last example does not produce any compilation error using g++ -Wall file.cpp. Tested using GCC versions 4.1.2 (RedHat) and 4.7.2 (MinGW).

Upvotes: 2

Oliver Charlesworth
Oliver Charlesworth

Reputation: 272607

You're looking at the legacy function from the C standard library (<string.h>). The C++ library (<cstring>) introduces appropriate const and non-const overloads, so you should use that wherever possible.

Upvotes: 13

Mike Seymour
Mike Seymour

Reputation: 254561

In C, the function must either be like this, or force the user to use dodgy casts in many situations:

  • If it took a non-const pointer, you couldn't search a const string;
  • If it returned a const pointer, you couldn't use it to modify a non-const string.

In C++, you should include <cstring> rather than the deprecated C-only header. That will give you two const-correct overloads, which couldn't be done in C:

const char* strchr(const char* s, int c);
      char* strchr(      char* s, int c);

Upvotes: 7

Jack
Jack

Reputation: 133599

Why would you forbid the code from modifying a returned variable? Mind that const char * is not char * const, you would be allowed to modify the character in any case, but you won't have control on the returned value itself, which doesn't make much sense since you could want to change it and edit the underlying string in a different position for your purpose.

Upvotes: 0

johnchen902
johnchen902

Reputation: 9599

const char *str means strrchr guarantees not to modify str.

Returning const char * means strrchr forbids you to modify the returned value.

Upvotes: 3

Related Questions