我试着写我自己实现的,和strchr()方法。
现在看起来是这样的:
char *mystrchr(const char *s, int c) {
while (*s != (char) c) {
if (!*s++) {
return NULL;
}
}
return (char *)s;
}
最后一行原本是
return s;
但这并没有工作,因为s是常量。 我发现,需要有这个转换(字符*),但我真的不知道我在做什么有:(有人能解释一下吗?
我认为,这实际上是在C标准的定义的一个漏洞strchr()
函数。 (我会很乐意被证明是错误的。)(回复意见,这是值得商榷的是否真的是一个缺陷。恕我直言,它仍然是设计不良它可以安全使用,但它太容易不安全使用。)
下面是C标准说什么:
char *strchr(const char *s, int c);
strchr函数位于C的第一次出现(转换为字符 )中的字符串由s指向。 终止空字符被认为是字符串的一部分。
这意味着,此程序:
#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()
有投它的结果将其从转换const char*
至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
。
(从历史上看, 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
指针,指向数组的元素。
从非修改函数返回非const指针常量数据的做法实际上是相当广泛使用的C语言的习惯用法 。 它并不总是很漂亮,但它是相当完善的。
这里的reationale很简单: strchr
本身是一个非修改操作。 然而,我们需要strchr
两个常量字符串和非字符串常量,这也将传播输入的常量性到输出的常量性功能。 无论ç不是C ++提供了这个概念的任何贵人扶持,这意味着在两种语言你将不得不为了避免采取与常量,正确性承担任何风险,写两个几乎相同的功能。
在C ++中,你野可以通过声明两个函数具有相同的名称使用函数重载
const char *strchr(const char *s, int c);
char *strchr(char *s, int c);
在C你没有函数重载,所以为了充分执行在这种情况下,常量,正确性,你就必须提供两种功能不同的名字,像
const char *strchr_c(const char *s, int c);
char *strchr(char *s, int c);
虽然在某些情况下,这可能是做正确的事,这是典型的(理所当然)认为过于繁琐和C类标准涉及。 您可以通过实现只有一个函数解决在更紧凑的(虽然风险更大)的方式这种情况
char *strchr(const char *s, int c);
返回非const指针到输入字符串(通过使用在出口处铸造,正是因为你做到了)。 请注意,这种做法不违反语言的任何规则,但它提供了违反这些目标的手段的调用者 。 通过简单地虚掷常量性数据的这种做法代表有责任从功能本身给调用者观察常量,正确性。 只要主叫知道发生了什么事情,并记住“玩好”,即使用const限定的指针指向const的数据,任何临时违反通过这样的功能创建常量,正确性壁会立即修复。
我看到这招作为完全可以接受的方法来减少不必要的重复代码(特别是在不存在函数重载的)。 标准库使用它。 你没有理由,以避免它要么,假设你明白你在做什么。
现在,为您实现strchr
,它看起来怪我从风格的角度来看。 我将使用循环头遍历我们正在(满弦)经营品种齐全,并使用内部if
要赶早终止条件
for (; *s != '\0'; ++s)
if (*s == c)
return (char *) s;
return NULL;
但是,这样的事情总是个人喜好的问题。 有人可能更愿意只
for (; *s != '\0' && *s != c; ++s)
;
return *s == c ? (char *) s : NULL;
也许有人会说,修改功能参数( s
)的函数内部是一个不好的做法。
的const
关键字表示参数不能被修改。
你不能返回s
,因为直接s
被声明为const char *s
和函数的返回类型是char *
。 如果编译器允许你这样做,将有可能覆盖const
的限制。
添加一个显式的char*
告诉你知道自己在做什么(尽管埃里克解释说,这将是更好,如果你没有做到这一点)编译器。
更新:对于环境的缘故,我引用Eric的答案,因为他似乎已经删除了它:
你不应该修改小号,因为它是一个const char *。
相反,定义表示类型char *的结果的局部变量和在所述方法中使用的身体,在地S的。
函数的返回值应该是一个常量指针为一个字符:
strchr
接受const char*
,而应返回const char*
也。 你是返回一个非恒定这是因为返回值点到输入字符数组(呼叫者可能期待不断的参数保持不变,潜在的危险,但如果它的任何一部分被返回的是修改char *
指针)。
如果没有匹配的字符是函数的返回值应为NULL:
此外strchr
应该返回NULL
,如果力求字符找不到。 如果在这种情况下返回时找不到特性的非NULL,或S,主叫方(如果他认为该行为是一样的,和strchr)可能会认为,在结果中的第一个字符确实匹配(不包括NULL返回值有没有办法知道是否有一个匹配与否)。
(我不知道这是什么,你打算做的事。)
这里是做了这样的函数的例子:
我已经编写并运行这个功能多次测试; 我加了一些真正明显的健全性检查,以避免潜在的崩溃:
const char *mystrchr1(const char *s, int c) {
if (s == NULL) {
return NULL;
}
if ((c > 255) || (c < 0)) {
return NULL;
}
int s_len;
int i;
s_len = strlen(s);
for (i = 0; i < s_len; i++) {
if ((char) c == s[i]) {
return (const char*) &s[i];
}
}
return NULL;
}