我认为这实际上是C标准对strchr()
函数定义的缺陷。(我很乐意被证明是错误的。)(回复评论时,有人认为这是否真的是一个缺陷是有争议的;在我看来,它仍然是不好的设计。它可以安全使用,但是过于容易让人不安全地使用。)
以下是C标准中的内容:
char *strchr(const char *s, int c);
strchr函数在由s所指向的字符串中查找第一次出现c(转换为char)的位置。终止的空字符被视为字符串的一部分。
这意味着下面的程序:
#include <stdio.h>
#include <string.h>
int main(void) {
const char *s = "hello";
char *p = strchr(s, 'l');
*p = 'L';
return 0;
}
即使它将指针定义为指向
const
char
的指针,但由于它修改了字符串文字,因此具有未定义的行为。至少gcc没有警告,程序会因段错误而死机。
问题在于
strchr()
接受一个
const char*
参数,这意味着它承诺不修改
s
所指向的数据--但是它返回一个普通的
char*
,允许调用者修改同一数据。
这里有另一个例子; 它没有未定义的行为,但它悄悄地修改了一个带有
const
限定符的对象而不需要任何强制转换(经过进一步思考,我相信它具有未定义的行为):
#include <stdio.h>
#include <string.h>
int main(void) {
const char s[] = "hello";
char *p = strchr(s, 'l');
*p = 'L';
printf("s = \"%s\"\n", s);
return 0;
}
我的理解是,(回答你的问题)C语言中的strchr()
实现必须将其结果转换为char*
类型,以将其从const char*
类型进行转换,或者执行等效操作。
这就是为什么C++在对C标准库进行少数更改之一时,用两个同名的重载函数替换了strchr()
:
const char * strchr ( const char * str, int character );
char * strchr ( char * str, int character );
当然C语言无法做到这一点。
另一种替代方法是用两个函数来代替strchr
,一个函数接收const char*
类型的参数并返回const char*
类型的结果,另一个函数接收char*
类型的参数并返回char*
类型的结果。与C++不同的是,这两个函数必须有不同的名称,例如strchr
和strcchr
。
(历史上,在定义strchr()
之后才添加了const
关键字以保持向后兼容性。这可能是保留strchr()
而不破坏现有代码的唯一方法。)
strchr()
并不是唯一存在这个问题的C标准库函数。受影响的函数列表(我认为这个列表是完整的,但我不能保证)是:
void *memchr(const void *s, int c, size_t n);
char *strchr(const char *s, int c);
char *strpbrk(const char *s1, const char *s2);
char *strrchr(const char *s, int c);
char *strstr(const char *s1, const char *s2);
(所有都在<string.h>
中声明)并且:
void *bsearch(const void *key, const void *base,
size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
(在<stdlib.h>
中声明)。所有这些函数都需要一个指向指向数组初始元素的const
数据的指针,并返回指向该数组元素的非const
指针。
strchr
返回指向c
的第一个出现位置的指针。它并没有说您可以修改 strchr 返回的指针所指向的内容。去掉 const 并通过指针修改 const 字符串并不是 strchr 特有的。strchr 的签名仅确保字符串不会在 strchr 内部意外修改。 - P.Pconst char *
。 - AnT stands with Russiasize_t
类型的 偏移量 而不是指针。然后调用者有责任使用该偏移量(将其添加到指针或以其他方式使用),并且调用者自然可以以 const 安全的方式使类型匹配。 - R.. GitHub STOP HELPING ICEchar *const str
。 - DeftlyHacked