7

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.

Community
  • 1
  • 1
oHo
  • 51,447
  • 27
  • 165
  • 200
  • 1
    Note, as you have this tagged `c++` and not `c`, you should really use `cstring`, rather than `string.h`. – BoBTFish May 31 '13 at 15:10
  • This answer is pretty good: http://stackoverflow.com/a/14368141/1708801 – Shafik Yaghmour May 31 '13 at 15:11
  • possible duplicate of [How does strchr implementation work](http://stackoverflow.com/questions/14367727/how-does-strchr-implementation-work) – Oliver Charlesworth May 31 '13 at 15:13
  • @BoBTFish: why would you want to infest your program with std:: prefix noise? Do you allow reusing names appearing in string.h header for any purpose? I surely do not. So they are fine in global where string.h puts them. – Balog Pal May 31 '13 at 15:18
  • @BalogPal: As you will see from the answer(s) below, this is in fact the key to solving this conundrum. – Oliver Charlesworth May 31 '13 at 15:20
  • @BalogPal Except, the C-style headers are not part of standard C++. And they are not guaranteed to contain the C++-specific overloads. – Angew is no longer proud of SO May 31 '13 at 15:20
  • 1
    @BoBTFish: Actually the distinction isn't purely pedantic here. `` provides the const-correct overloads which are impossible to provide in C. – Mike Seymour May 31 '13 at 15:20
  • @Angew: The C++ standard includes the C standard by reference so they ARE in fact part. As for the C vs. C++ differences, my interpretation was that those apply to both forms, and only differ in where the names are mandatorily dumped, per footnote 177 in C++11. But it will worth asking. – Balog Pal Jun 01 '13 at 13:30
  • @OliCharlesworth: I see but is is not correct. Despite getting the ton of up votes. – Balog Pal Jun 01 '13 at 16:43

5 Answers5

13

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.

leemes
  • 44,967
  • 21
  • 135
  • 183
Oliver Charlesworth
  • 267,707
  • 33
  • 569
  • 680
  • Annex D.5p2 states that "Every C header, each of which has a name of the form name.h, behaves as if each name placed in the standard library namespace by the corresponding cname header is placed within the global namespace scope. ..." So the mentioned overload must be in sting.h as well, and no reason to use the c version for that reason only. – Balog Pal Jun 01 '13 at 16:45
  • @BalogPal You're confusing "name" and "declaration." The *name* `strrchr` is placed into the global namespace by ``, and into namespace `std` by ``. Nevertheless, `` (being a C header) cannot contain overloads, so it provides just one meaning for the name `strrchr`. `` is a C++ header, so it provides two (overloaded) declarations for the name `strrchr`. – Angew is no longer proud of SO Jun 02 '13 at 18:56
  • @andrew: so you say including I shall get NULL version as defined in the C standard instead of how C++ requires it to be overridden, forbidding (void*)0? Just to pick one random delta that would render a zillion existing projects fail to compile. Sorry, I keep with my interpretation that "behaves as" means "behaves as". – Balog Pal Jun 02 '13 at 19:16
7

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);
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
3

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

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

johnchen902
  • 9,531
  • 1
  • 27
  • 69
  • I agree with you. But why does `strrchr()` returns `char*`? Why not `const char*`? – oHo May 31 '13 at 15:12
  • 1
    His answered told you that @olibre. Read it again. – crush May 31 '13 at 15:12
  • @crush Not really. As the OP demonstrates, this return type is unsafe. Note that the C++ version has the correct overloads. – Angew is no longer proud of SO May 31 '13 at 15:18
  • OK I have just understood while writing this comment... So the answer is: `strrchr()` does not forbid to modify the content of the returned pointer pointing to the cont string passed as parameter **because** the passed string may be non-const. I am correct? – oHo May 31 '13 at 15:20
  • @olibre This is what I mean. Especially when including a c header. – johnchen902 Jun 01 '13 at 06:20
2

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).

oHo
  • 51,447
  • 27
  • 165
  • 200
0

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.

Jack
  • 131,802
  • 30
  • 241
  • 343
  • I do not really understand what you mean. Where is your `char * const`? Is it about `CONSTSTR`? I think `const char CONSTSTR[] = "x"` is like `const char* const CONSTSTR = "x"` except for `sizeof(CONSTSTR)`. – oHo May 31 '13 at 15:24