【问题标题】:Strsep with Multiple Delimiters: Strange result带有多个分隔符的 Strsep:奇怪的结果
【发布时间】:2021-08-02 03:35:01
【问题描述】:

当使用带有多个分隔符的strsep 时,我目前遇到了一些奇怪的结果。我的分隔符包括 TAB 字符、空格字符以及><

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main()
{
    char buffer[50];
    char *curr_str = NULL;
    const char delim[4] = "\t >";
    //const char delim[4] = "\t ><"; // This does not work
  
    snprintf(buffer, 50, "%s", "echo Hello");
  
    char *str_ptr = buffer;
  
    curr_str = strsep(&str_ptr, delim);
  
    if (curr_str != NULL)
        printf("%s\n", curr_str);

    curr_str = strsep(&str_ptr, delim);
    if (curr_str != NULL)
        printf("%s\n", curr_str);
    return (0);
}

这个输出是我所期望的。

echo 
Hello

但是,一旦我为分隔符添加了“

cho

不知何故,第一个字符被截断了。发生这种情况是否有原因?

谢谢。

【问题讨论】:

    标签: c tokenize c-strings strsep


    【解决方案1】:

    "...第一个字符被截断。发生这种情况有什么原因吗?"

    是的,未定义的行为是由 C 字符串函数中使用的非空终止字符数组引起的。

    如果填充const char delim[4] 时不包含空终止,则它将只是一个char 数组,而不是C string。它可能会或可能不会表现出奇怪行为,但如果与任何C string functions(例如curr_str = strsep(&amp;str_ptr,delim);)一起使用,它将调用undefined behavior

    const char delim[4];
    

    有 4 个字符的空间。

    "\t ><"  //contains exactly 4 char
    

    在内存中可以这样概念化:

    |\t| |>|<|?|?|?|  // ? = unknown content, possibly no null termination
             ^end of owned memory
    

    它应该包含以下内容:

    |\t| |>|<|\0|?|?|  // null termination  
                ^end of owned memory (5 char wide)
    

    在声明中需要更多空间,例如以下两个选项之一:

    const char delim[5] = "\t ><";
    

    const char delim[] = "\t ><";
    

    【讨论】:

      【解决方案2】:

      const char delim[4] = "\t &gt;&lt;"; 没有定义正确的 C 字符串,因为空终止符没有空间。因此,内存中delim 之后的任何非零字节都将成为分隔符字符串的一部分。

      这当然是未定义的行为,在您的情况下,编译器可能会将delim 定位在buffer 之前而无需任何填充,从而有效地继续使用字符串中的所有字符的分隔符序列"echo Hello"。这会导致对strsep 的第一次调用返回一个空字符串。

      您可以在此Godbolt instance 上检查,在 32 位模式下确实是这种情况,但在 64 位模式下却不是(删除-m32 编译器选项)。

      这个问题很容易解决。您可以让编译器确定delim 数组的长度:

      const char delim[] = "\t ><";
      

      或者您可以使用指向字符串常量的指针:

      const char *delim = "\t ><";
      

      【讨论】:

        【解决方案3】:

        strsepdelim 的第二个参数是一个以 null 结尾的字符串(就像 C 中的所有字符串一样),因此您必须为终止字符留出空间:

        const char delim[5] = "\t ><"; // This does work
        //const char delim[] = "\t ><"; // or this
        

        如果你不结束字符串,它将去探索数组之外的内存并找到许多新的分隔字符来使用,这就是你的情况。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-02-24
          • 2015-07-28
          • 2022-08-17
          • 2018-10-19
          • 2014-06-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多