为什么strrchr()返回char*而不是const char*?

7
函数char* strrchr(const char *str, int ch)会在strconst char *)中返回最后一个出现ch的指针(char*)。

因此,我们可以编写以下代码而无需进行任何强制转换:

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

返回char*而不是const char*的优势是什么?
编辑:正如Shafik Yaghmour指出的那样,How does strchr implementation work?有非常好的答案。
由于我的代码是C++,我将使用<cstring>而不是<string.h>。谢谢你们的回答 ;-)
然而,Mike Seymour的回答最适合这个问题。我甚至添加了一个更详细的答案来清楚地说明,因为strrchr()是一个C函数(不允许重载),声明适用于constnon-const字符串。因为strrchr()可以用non-const字符串调用,返回的字符串也应该是non-const的。

1
注意,由于您将此标记为 c++ 而不是 c,因此应该使用 cstring 而不是 string.h。 </pedant> - BoBTFish
@BoBTFish:为什么你想要在程序中添加std::前缀的噪音?你允许重用string.h头文件中出现的名称吗?我肯定不会这样做。所以它们在全局范围内是可以的,就像string.h中放置它们一样。 - Balog Pal
正如您在下面的答案中所看到的那样,这实际上是解决这个难题的关键。 - Oliver Charlesworth
@BalogPal 除此之外,C风格的头文件不是标准C++的一部分。并且它们不能保证包含C++特定的重载。 - Angew is no longer proud of SO
1
@BoBTFish:实际上这里的区别并非纯粹迂腐。<cstring>提供了C无法提供的const-correct重载。 - Mike Seymour
显示剩余4条评论
5个回答

13

您正在查看来自C标准库(<string.h>)的遗留函数。C++库(<cstring>)引入了适当的const和非const重载,因此在可能的情况下应该使用它。


附录D.5p2规定:“每个C头文件,其名称形式为name.h,都表现得好像由相应的cname头文件放置在标准库命名空间中的每个名称都被放置在全局命名空间范围内。...”因此,所提到的重载也必须在sting.h中,并且没有理由仅因此使用c版本。 - Balog Pal
@BalogPal,你混淆了“名称”和“声明”。名称 strrchr<string.h> 放置在全局命名空间中,并由 <cstring> 放置在 std 命名空间中。然而,由于 <string.h>(作为 C 标头)不能包含重载,因此它仅为名称 strrchr 提供一个含义。<cstring> 是一个 C++ 标头,因此它为名称 strrchr 提供了两个(重载的)声明。 - Angew is no longer proud of SO
@andrew:你的意思是说,包含<stddef.h>头文件后,我应该得到C标准中定义的NULL版本,而不是C++要求覆盖它并禁止(void*)0的版本?只是随便挑一个差异,这将导致无数现有项目无法编译。抱歉,我坚持认为“行为如何”就是“行为如何”。 - Balog Pal

7
在C语言中,函数必须像这样写,或者在许多情况下强制用户使用不可靠的转换方式:
- 如果它接受一个非const指针,则无法搜索const字符串; - 如果它返回一个const指针,则无法将其用于修改非const字符串。
在C++中,应该包含<cstring>头文件而不是已弃用的仅限C的头文件。这将为您提供两个const-correct重载,这在C中是不可能的。
const char* strchr(const char* s, int c);
      char* strchr(      char* s, int c);

我已经接受了你的答案,但是我提供了一个更详细的答案。干杯 - oHo

3

const char *str 表示 strrchr 保证不修改 str

返回 const char * 意味着 strrchr 禁止您修改返回值。


我同意你的观点。但是为什么strrchr()返回char*而不是const char*呢? - oHo
1
他的回答告诉了你@olibre。请再读一遍。 - crush
@crush 并不完全正确。正如 OP 所示,此返回类型是不安全的。请注意,C++ 版本具有正确的重载。 - Angew is no longer proud of SO
好的,我刚写这个评论时才明白... 所以答案是:strrchr()不禁止修改指向作为参数传递的cont字符串的返回指针的内容 因为 传递的字符串可能是非const的。 我是正确的吗? - oHo
@olibre 这就是我的意思,特别是在包含 C 头文件时。 - johnchen902

2

<string.h> 中的 strrchr() 是一个 C 函数。由于 C 不允许函数重载,因此 strrchr() 被设计为适用于constnon-const字符串。

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

strrchr()函数可以使用非const字符串调用,因此根据以下示例,返回的字符串也应该是非const

const上下文不会导致编译错误:

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

没有编译错误的 非const 上下文:

#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"
}

即使没有编译错误,不良用法也会存在:

#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
}

C++中有两个重载函数:strrchr

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

const 上下文使用第一个:

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

非const上下文使用第二个:

#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"
}

不良使用应该产生编译错误:

#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';
}

但是,使用 g++ -Wall file.cpp 编译上述代码并不会产生任何编译错误。经过 GCC 版本为 4.1.2 (RedHat)4.7.2 (MinGW) 的测试。


0
为什么要禁止代码修改返回的变量?请注意,const char *不是char * const,在任何情况下您都可以修改字符,但您无法控制返回值本身,这并没有太多意义,因为您可能希望更改它并编辑底层字符串以满足您的目的。

我不太明白你的意思。你的 char * const 在哪里?这与 CONSTSTR 有关吗?我认为 const char CONSTSTR[] = "x" 就像 const char* const CONSTSTR = "x",除了 sizeof(CONSTSTR) - oHo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接