【问题标题】:Unexpected behaviour while extracting substrings from a string从字符串中提取子字符串时出现意外行为
【发布时间】:2018-01-18 06:48:03
【问题描述】:

我正在尝试打印一个大字符串中包含的所有子字符串,每个子字符串'/' 字符分隔。我的功能没有按预期工作,但我不明白它有什么问题。这是我写的函数:

void print_serial_list(char *serial_list) {
    char *iter = serial_list;
    while (*iter != '\0') { // Traverse the whole string
        char *tmp_fn;
        tmp_fn = strtok(iter,"/");
        printf("Extracted entry: '%s'\n", tmp_fn);
        iter = iter + sizeof(tmp_fn);
    }
}

直接传递字符串

如果我像这样运行这个函数:

char *string = "Lorem.ipsum/dolor-sit-amet/consectetur/adipiscing.elit/";
printf("%s\n", string);
print_serial_list(string);

我遇到了分段错误:

Lorem.ipsum/dolor-sit-amet/consectetur/adipiscing.elit/
Segmentation fault (core dumped)

使用get_string() 函数

另一方面,如果我运行这个:

char *string = get_string();
printf("%s\n", string);
print_serial_list(string);

我得到以下输出(仍然错误):

Lorem.ipsum/dolor-sit-amet/consectetur/adipiscing.elit/
Extracted entry: 'Lorem.ipsum'
Extracted entry: 'sum'
Extracted entry: 'r-sit-amet'
Extracted entry: 'et'
Extracted entry: 'ctetur'
Extracted entry: 'dipiscing.elit'
Extracted entry: 'g.elit'
Extracted entry: '�'
Extracted entry: 'x[�V'
Extracted entry: 'x[�V'

期望

为了清楚起见,我希望输出在这两种情况下都是:

Lorem.ipsum/dolor-sit-amet/consectetur/adipiscing.elit/
Extracted entry: 'Lorem.ipsum'
Extracted entry: 'dolor-sit-amet'
Extracted entry: 'consectetur'
Extracted entry: 'adipiscing.elit'

(注意:我希望get_string() 的代码对于理解这个问题不是必需的......我想尽量不要保留这个帖子长)

编辑

根据 cmets 中的一些建议,我以这种方式编辑了函数:

char *iter = serial_list;
bool first = true;
while (*iter != '\0') { // Traverse the whole string
    char *tmp_fn;
    if (first)
        tmp_fn = strtok(iter, "/");
    else
        tmp_fn = strtok(NULL, "/");
    size_t tmp_size = strlen(tmp_fn);
    printf("Extracted entry: '%s' - size = %zu\n", tmp_fn, tmp_size);
    iter = iter + tmp_size;
    first = false;
}

我得到的输出仍然有一些问题,但与我想要的更相似!

Lorem.ipsum/dolor-sit-amet/consectetur/adipiscing.elit/
Extracted entry: 'Lorem.ipsum' - size = 11

【问题讨论】:

  • 检查sizeof(tmp_fn)..打印它。
  • 指针上的 sizeof 为您提供指针本身的大小,而不是指针所指向的大小。如果要获取字符串的长度,请使用strlen
  • 此外,请记住strtok 可以返回一个空指针,您必须检查它。而且你真的用错了strtok
  • @Someprogrammerdude 谢谢!你对strlen 的事情是完全正确的。我更改了它,结果只得到了第一个条目:Extracted entry: 'Lorem.ipsum' - size = 11...
  • 已解决:我根本不需要增加指向 char 的指针,因为 strtok 我已经处理好了(所以我刚刚删除了 iter = iter + tmp_size;

标签: c string segmentation-fault strtok


【解决方案1】:

If I run this function like this, I get a segmentation fault:

char *string = "Lorem.ipsum/dolor-sit-amet/consectetur/adipiscing.elit/";

您的程序有undefined behavior,因为它正在尝试修改字符串文字,因为您将字符串文字传递给strtok()

char * strtok(c​​har * str, const char * 分隔符);

将字符串拆分为标记

对该函数的一系列调用将 str 拆分为标记,这些标记是由作为定界符一部分的任何字符分隔的连续字符序列。

string 是指向字符串文字的指针,其内容无法修改。并且试图通过指针修改它们是未定义的行为。

要解决此问题,您只需执行以下操作:

char string[] = "Lorem.ipsum/dolor-sit-amet/consectetur/adipiscing.elit/";
           ^^

而在print_serial_list()函数中,你可以这样做:

void print_serial_list(char *serial_list) {
    char *iter = serial_list;

    if (serial_list == NULL)
            return;

    char *tmp_fn = strtok(iter, "/");
    while (tmp_fn != NULL)
    {
            printf ("Extracted entry: '%s'\n", tmp_fn);
            tmp_fn = strtok(NULL, "/");
    }
}

print_serial_list() 输出是(对于Lorem.ipsum/dolor-sit-amet/consectetur/adipiscing.elit/ 输入字符串):

Extracted entry: 'Lorem.ipsum'
Extracted entry: 'dolor-sit-amet'
Extracted entry: 'consectetur'
Extracted entry: 'adipiscing.elit'

这里要注意的一点是print_serial_list() 将修改字符串string,因为它将它传递给strtok()。如果您不希望在调用print_serial_list() 函数后修改输入字符串,请将其复制到print_serial_list() 函数中。


来自strtok

错误 使用这些功能时要小心。如果您确实使用它们,请注意:

*这些函数修改它们的第一个参数。

*这些函数不能用于常量字符串。

*分隔字节的标识丢失。

感谢 @David C. Rankin 在评论中分享这些 strtok() 错误。

【讨论】:

  • 我有点喜欢man 3 strtok 中 BUGS 下的前两个要点,这确实有助于将事实带回家,例如"These functions modify their first argument.""These functions cannot be used on constant strings."
猜你喜欢
  • 2011-07-21
  • 1970-01-01
  • 1970-01-01
  • 2022-01-01
  • 2018-09-29
  • 2021-12-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多