Mar*_*arc 18 c pointers const strchr
我试着编写自己的strchr()方法实现.
它现在看起来像这样:
char *mystrchr(const char *s, int c) {
while (*s != (char) c) {
if (!*s++) {
return NULL;
}
}
return (char *)s;
}
Run Code Online (Sandbox Code Playgroud)
最后一行原来是
return s;
Run Code Online (Sandbox Code Playgroud)
但这不起作用,因为s是const.我发现需要这个演员(char*),但老实说我不知道我在那里做什么:(有人可以解释一下吗?
Kei*_*son 19
我相信这实际上是C标准对strchr()函数定义的一个缺陷.(我会很高兴被证明是错的.)(回答这些评论,它是否真的是一个缺陷是有争议的;恕我直言,它仍然是糟糕的设计.它可以安全使用,但它太容易使用它不安全.)
这是C标准所说的:
char *strchr(const char *s, int c);
Run Code Online (Sandbox Code Playgroud)
的和strchr函数定位的第一次出现Ç (转化为焦炭)在字符串由指向小号.终止空字符被认为是字符串的一部分.
这意味着这个程序:
#include <stdio.h>
#include <string.h>
int main(void) {
const char *s = "hello";
char *p = strchr(s, 'l');
*p = 'L';
return 0;
}
Run Code Online (Sandbox Code Playgroud)
即使它小心地将指向字符串文字的指针定义为指向,也有未定义的行为,因为它修改了字符串文字.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;
}
Run Code Online (Sandbox Code Playgroud)
我认为,这意味着(回答你的问题)C实现strchr()必须将其结果转换为将其转换const char*为char*或执行等效的操作.
这就是为什么C++在对C标准库进行的少量更改之一中取代strchr()了两个同名的重载函数:
const char * strchr ( const char * str, int character );
char * strchr ( char * str, int character );
Run Code Online (Sandbox Code Playgroud)
当然C不能这样做.
另一种方法是strchr用两个函数替换,一个取a const char*并返回a const char*,另一个取a char*并返回a char*.不像在C++中,这两个函数必须有不同的名称,也许strchr和strcchr.
(从历史上看,const在strchr()已经定义之后添加到C中.这可能是在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);
Run Code Online (Sandbox Code Playgroud)
(全部申报<string.h>)和:
void *bsearch(const void *key, const void *base,
size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
Run Code Online (Sandbox Code Playgroud)
(声明<stdlib.h>).所有这些函数都使用指向const数据的指针,该数据指向数组的初始元素,并返回const指向该数组元素的非指针.
AnT*_*AnT 14
将非const指针从非修改函数返回到const数据的实践实际上是在C语言中相当广泛使用的习惯用法.它并不总是漂亮,但它已经相当成熟.
这里的reationale很简单:strchr本身是一个非修改操作.然而,我们需要strchr常量字符串和非常量字符串的功能,这也会将输入的常量传播到输出的常量.C和C++都没有为这个概念提供任何优雅的支持,这意味着在两种语言中你都必须编写两个几乎相同的函数,以避免带有const正确性的任何风险.
在C++中,您可以通过声明具有相同名称的两个函数来使用函数重载
const char *strchr(const char *s, int c);
char *strchr(char *s, int c);
Run Code Online (Sandbox Code Playgroud)
在C中你没有函数重载,所以为了在这种情况下完全强制const正确,你必须提供两个不同名称的函数,比如
const char *strchr_c(const char *s, int c);
char *strchr(char *s, int c);
Run Code Online (Sandbox Code Playgroud)
虽然在某些情况下这可能是正确的做法,但通常(并且正确地)认为它太麻烦并且涉及C标准.您可以通过仅实现一个函数以更紧凑(尽管风险更大)的方式解决这种情况
char *strchr(const char *s, int c);
Run Code Online (Sandbox Code Playgroud)
它将非const指针返回到输入字符串中(通过在出口处使用强制转换,就像你做的那样).请注意,此方法不违反任何语言规则,但它为调用者提供了违反它们的方法.通过抛弃数据的常量,这种方法简单地将责任从函数本身委托给调用者.只要调用者知道发生了什么并记得"玩得很好",即使用const限定指针指向const数据,由此类函数创建的const-correctness中的任何临时破坏都会立即得到修复.
我认为这个技巧是减少不必要的代码重复的完全可接受的方法(特别是在没有函数重载的情况下).标准库使用它.假设你明白自己在做什么,你也没有理由避免它.
现在,至于你的实现strchr,从风格的角度来看,我看起来很怪异.我会使用循环标头迭代我们正在操作的整个范围(完整字符串),并使用内部if来捕获提前终止条件
for (; *s != '\0'; ++s)
if (*s == c)
return (char *) s;
return NULL;
Run Code Online (Sandbox Code Playgroud)
但这样的事情总是个人喜好的问题.有人可能更喜欢
for (; *s != '\0' && *s != c; ++s)
;
return *s == c ? (char *) s : NULL;
Run Code Online (Sandbox Code Playgroud)
有人可能会说在函数s内修改函数parameter()是一种不好的做法.