【问题标题】:How does the function Strrchr finds the last c if c == '\0'?如果 c == '\0',函数 Strrchr 如何找到最后一个 c?
【发布时间】:2021-12-24 01:16:16
【问题描述】:

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

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

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

【问题讨论】:

  • 最后一个只能是第一个 - 否则你无法知道字符串“结束”的位置。你可以一直找零,直到内存用完。
  • 根据定义,字符串是包含终止空字符的字符序列。如果要查找字符数组中最后一个空字符,则必须知道数组的大小。
  • 如果您只有char *p 指向第一个字符,那么它不仅不是“简单的问题”,而且完全不可能。没有任何信息可以告诉您原始字符串有多长。一个\0 与另一个终止字符串一样好,因此一旦找到一个,您就无法知道它不是真实的。出于所有意图和目的,字符串以第一个 \0 结尾。
  • 为了实现strrchr,请执行以下操作:找到 first 空字符,即字符串的结尾。然后从那里返回,直到找到字符或者您位于字符串的开头。这是 4-5 行 C 代码。
  • 更高效的版本会记住搜索字符最近出现的位置,并且只需要遍历字符串一次。还是只有几行。

标签: c pointers segmentation-fault null-character


【解决方案1】:

这很简单,正如 cmets 中所解释的那样。 first \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 的字符串”。根据定义,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 */

那我肯定已经过关了。

【讨论】:

    【解决方案2】:

    “字符串”、“字符数组”和“char* 指针”之间存在区别。

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

    你说你有一个指向字符(char*p)的指针,*p 的值是't',但你认为 *p 是 C 风格字符串的第一个字符 "there is so \0ma\0ny \0 \\0 in t\0his stri\0ng !\0\0\0\0".

    正如其他人所说,因为你说这是一个 C 风格的字符串,你不知道它的长度,那么p 之后的第一个 null 将标记字符串的结尾。

    如果这是一个字符数组char str[40],那么您可以通过从数组末尾循环到开头for (i=39; i>=0; i--) 来找到最后一个空值,但是您不知道长度,所以这不起作用。

    希望对您有所帮助,如果我误入了 C++,请原谅我,因为我接触 C 已有 25 年了 :)

    【讨论】:

    • 很好地区分这三个相关概念。 (你的其他答案也很好;我没有看到任何 C++ 偏见。)
    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-25
      • 2011-05-05
      • 2010-11-24
      相关资源
      最近更新 更多