我相信这实际上是 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;
}
即使它仔细地将指向字符串文字的指针定义为指向 的指针,也具有未定义的行为,因为它修改了字符串文字。至少 gcc 不会对此发出警告,并且程序会因分段错误而死亡。const
char
问题是strchr()
接受一个const char*
参数,这意味着它承诺不修改s
指向的数据——但它返回一个 plain 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()
必须将其结果转换const char*
为char*
,或做一些等效的事情。
这就是为什么 C++ 在它对 C 标准库所做的少数更改之一中,strchr()
用两个同名的重载函数替换:
const char * strchr ( const char * str, int character );
char * strchr ( char * str, int character );
当然C不能这样做。
另一种方法是strchr
用两个函数替换,一个接受 aconst char*
并返回 a const char*
,另一个接受 achar*
并返回 a char*
。与 C++ 不同,这两个函数必须有不同的名称,也许strchr
和strcchr
.
(从历史上看,在已经定义const
之后被添加到 C中。这可能是在不破坏现有代码的情况下保持不变的唯一方法。)strchr()
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
指向该数组元素的非指针。