0

例如,给定 achar *p指向 中的第一个字符"there is so \0ma\0ny \0 \\0 in t\0his stri\0ng !\0\0\0\0",如何Strrchr()找到最后一次出现的空字符?

出现以下问题:
=>停止循环取决于什么条件!
=>我认为在所有情况下它都会尝试访问下一个内存区域以检查其状况?在某些时候绕过字符串边界,UB!安全吗?

如果我错了,请随时纠正我!

4

3 回答 3

4

正如评论中所解释的,这非常简单。第一个 \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 */

那么我肯定会超越边缘。

于 2021-11-11T15:06:57.793 回答
2

“字符串”、“字符数组”和“char* 指针”是有区别的。

  • AC 字符串是由空字符终止的多个字符。
  • 字符数组是定义数量的字符。
  • char* 指针在技术上是指向单个字符的指针,但通常用于标记 C 样式字符串中的点。

您说您有一个指向字符 ( char*p) 的指针,其值为*pis '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 年了 :)

于 2021-11-11T15:15:54.533 回答
0

在您提出的情况下,您永远无法知道您找到的空字符是否是最后一个,因为您无法保证字符串的结尾。由于它是一个 c 字符串,因此可以保证字符串以 '\0' 结尾,但是如果您决定超出此范围,您将无法知道您正在访问的内存是否属于您。从数组中访问内存具有未定义的行为,因为您可以仅访问属于您的内存中的下一个对象,也可以触摸未分配的内存,但其块仍属于您的进程,或者您可以尝试触摸一个完全不属于你的部分。只有第三个会导致 SIGSEGV。你可以看到这个问题来检查分段错误而不会使你的程序崩溃,

字符串有一个结束字符是有原因的。如果您坚持在字符串中的多个位置使用 \0,则可以使用另一个字符终止,但请注意,所有库函数仍将第一个 \0 视为字符串的结尾。

在你的字符串中有多个 \0 被认为是一种不好的做法,也是一件非常糟糕的事情,所以如果可以的话,请避免它。

于 2021-11-11T15:04:04.353 回答