例如,给定 achar *p
指向 中的第一个字符"there is so \0ma\0ny \0 \\0 in t\0his stri\0ng !\0\0\0\0"
,如何Strrchr()
找到最后一次出现的空字符?
出现以下问题:
=>停止循环取决于什么条件!
=>我认为在所有情况下它都会尝试访问下一个内存区域以检查其状况?在某些时候绕过字符串边界,UB!安全吗?
如果我错了,请随时纠正我!
例如,给定 achar *p
指向 中的第一个字符"there is so \0ma\0ny \0 \\0 in t\0his stri\0ng !\0\0\0\0"
,如何Strrchr()
找到最后一次出现的空字符?
出现以下问题:
=>停止循环取决于什么条件!
=>我认为在所有情况下它都会尝试访问下一个内存区域以检查其状况?在某些时候绕过字符串边界,UB!安全吗?
如果我错了,请随时纠正我!
正如评论中所解释的,这非常简单。第一个 \0
是 C 字符串中的最后一个也是唯一一个。
所以如果你写
char *str = "there is so \0ma\0ny \0 \\0 in t\0his stri\0ng !\0\0\0\0";
char *p = strrchr(str, 's');
printf("%s\n", p);
它会打印
so
因为strchr
会在“so”中找到“s”,它是你给它的字符串中的最后一个“s”。并且(回答你的具体问题)如果你写
p = strrchr(str, '\0');
printf("%d %s\n", (int)(p - str), p+1);
它会打印
12 ma
证明strchr
找到了第一个 \0
。
很明显,这str
是一个长字符串,其中嵌入了一些\0
'。但是,在 C 中,没有“其中嵌入了\0
's 的字符串”之类的东西。根据定义,C 字符串不可能包含嵌入的\0
. 第一个\0
,根据定义,结束字符串。
还有一点。您曾提到,如果您要“访问下一个内存区域”,您将“在某个时候绕过字符串边界,UB!” 你是对的。在我的回答中,当我说
p = strrchr(str, '\0');
printf("%d %s\n", (int)(p - str), p+1);
在这里,p
指向strrchr
认为是字符串的结尾,所以当我计算p+1
并尝试使用 打印它时%s
,如果我们不知道更好,看起来我确实误入了未定义的行为。当然,在这种情况下它是安全的,因为我们确切地知道第一个\0
. 但如果我要写
char *str2 = "hello";
p = strrchr(str2, '\0');
printf("%s\n", p+1); /* WRONG */
那么我肯定会超越边缘。
“字符串”、“字符数组”和“char* 指针”是有区别的。
您说您有一个指向字符 ( char*p
) 的指针,其值为*p
is 't'
,但您认为 *p 是 C 样式字符串的第一个字符
"there is so \0ma\0ny \0 \\0 in t\0his stri\0ng !\0\0\0\0"
。
正如其他人所说,因为您说这是一个 C 风格的字符串并且您不知道它的长度,所以后面的第一个 nullp
将标记字符串的结尾。
如果这是一个字符数组char str[40]
,那么您可以通过从数组的末尾循环到开头for (i=39; i>=0; i--)
但您不知道长度来找到最后一个空值,这样就行不通了。
希望对您有所帮助,如果我误入了 C++,请原谅我,因为我使用 C 已有 25 年了 :)
在您提出的情况下,您永远无法知道您找到的空字符是否是最后一个,因为您无法保证字符串的结尾。由于它是一个 c 字符串,因此可以保证字符串以 '\0' 结尾,但是如果您决定超出此范围,您将无法知道您正在访问的内存是否属于您。从数组中访问内存具有未定义的行为,因为您可以仅访问属于您的内存中的下一个对象,也可以触摸未分配的内存,但其块仍属于您的进程,或者您可以尝试触摸一个完全不属于你的部分。只有第三个会导致 SIGSEGV。你可以看到这个问题来检查分段错误而不会使你的程序崩溃,
字符串有一个结束字符是有原因的。如果您坚持在字符串中的多个位置使用 \0,则可以使用另一个字符终止,但请注意,所有库函数仍将第一个 \0 视为字符串的结尾。
在你的字符串中有多个 \0 被认为是一种不好的做法,也是一件非常糟糕的事情,所以如果可以的话,请避免它。