繁体   English   中英

使用 strtok 返回分段错误

[英]Segmentation Fault returned using strtok

我正在尝试在 c 中创建一个 function,它拆分字符串就像在 java 或许多其他语言中拆分 function 一样。 我做的

char **split(char * str, char *ch) {
  char **array = (char **)malloc((strlen(str)) * sizeof(*array));
  int i = 0;
  char *token = strtok(str, ch);
  while (token != NULL) {
    array[i++] = token;
    token = strtok(NULL, ch);
  }
  free(token);
  return array;
}

这似乎有效,但并不总是而且不正确。 假设我们以两种不同的方式调用它:第一种是工作方式:

int main(){
  
  while(1){
    sleep(1);
    char h = ':';
    char a[] = "test:1234";
    char ** result = split(a,&h);
    printf("%s\n",result[0]);
    printf("%s\n",result[1]);
    free(result);
  }
}

而第二个在第二个 while 周期给我一个分段错误:

int main(){
  char a[] = "test:1234";
  char h = ':';
  while(1){
    sleep(1);
    char ** result = split(a,&h);
    printf("%s\n",result[0]);
    printf("%s\n",result[1]);
    free(result);
  }
}

Output:

test
1234
test
Segmentation fault (core dumped)

我认为这是由于 strtok function 对字符串索引的操纵,但我不明白如何修复它以及它为什么给我一个分段错误。

一个问题是您错误地调用了strtok

strtok需要两个字符串,即要拆分的字符串和一串分隔符。

但是您没有传递一串分隔符 - 您传递了一个指向单个字符的指针。

所以改变它像:

char h = ':';                  --->  char *h = ":";

char ** result = split(a,&h);  --->  char ** result = split(a,h);

您的代码的另一个问题是您希望它始终返回至少两个有效标记。 这是一个错误的假设,它将在您的第二个代码示例的第二个循环中失败。

在第一个循环中a将更改为字符串“test”,因为strtok':'替换为字符串终止符。

因此在第二个循环中将只有一个令牌。 这意味着result[1]没有指向有效的标记,因此,您不能打印它所指向的内容。

解决该问题的一种方法是将所有result指针设置为 function 中的 NULL,例如使用calloc而不是malloc ,例如:

char **array = calloc(strlen(str), sizeof(*array));

然后像这样打印:

if (result[0]) printf("%s\n",result[0]);
if (result[1]) printf("%s\n",result[1]);

或更好:

int i = 0;
while(result[i])
{
    printf("%s\n",result[i]);
    ++i;
}

把它们放在一起:

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

char **split(char * str, char *ch) {
  char **array = calloc(strlen(str), sizeof(*array));  // Use calloc to set
                                                       // all pointers to NULL
  int i = 0;
  char *token = strtok(str, ch);
  while (token != NULL) {
    array[i++] = token;
    token = strtok(NULL, ch);
  }
  return array;
}

int main(){
  char a[] = "test:1234";
  char *h = ":";
  int z = 0;
  while(z < 5){    // Just loop 5 times
    //sleep(1);
    char ** result = split(a,h);
    int i = 0;
    while(result[i])   // Print all tokens, i.e. stop when a pointer is NULL
    {
        printf("%s\n",result[i]);
        ++i;
    }    
    free(result);
    ++z;
  }
}

Output:

test
1234
test
test
test
test

顺便提一句:

这个

free(token);

是相同的

free(NULL);

它什么都不做,所以只需删除该行。

strtok 使用起来有点棘手,因为它处理 memory 的方式与以往不同——它修改作为参数传递的字符串,返回指向 substring 的指针,当 strtok(NULL,..) 将新指针返回到缓冲区,如果缓冲区超出 scope,则指针变为无效,或者如果另一个线程正在调用 strtok,则指针变为无效,因此最好在继续之前将返回的令牌复制到另一个缓冲区之前。

这可以通过分配一个 memory 块然后在那里复制返回值来实现

char **split(char * str, char *ch) {
  char **array = (char **)malloc((strlen(str)) * sizeof(*array));
  int i = 0;
  char *token = strtok(str, ch);
  while (token != NULL) {
    char* dupToken = malloc(strlen(token)+1);
    strcpy(dupToken, token);
    array[i++] = dupToken;
    token = strtok(NULL, ch);
  }
  // free(token); // this here is wrong
  return array;
}

现在您的代码的另一个问题是调用者无法知道返回的数组中有多少标记,所以我建议另一种方法

一旦你击中最后一个标记,在返回数组之前将下一个指针设置为 NULL

char **split(char * str, char *ch) {
  char **array = (char **)malloc((strlen(str)) * sizeof(*array));
  int i = 0;
  char *token = strtok(str, ch);
  while (token != NULL) {
    char* dupToken = malloc(strlen(token)+1);
    strcpy(dupToken, token);
    array[i++] = dupToken;
    token = strtok(NULL, ch);
  }
  array[i] = NULL;    
  return array;
}

这样当你通过令牌 go 你可以只检查指针

for (int i = 0; array[i] != NULL; ++i)
{ 
...
}

编辑:然后向您的数组添加另一个条目可能很好,这样您就可以处理最大令牌数 + 1

char **array = (char **)malloc((strlen(str) + 1) * sizeof(*array));

编辑:更改了我对返回指针所发生情况的相当草率的描述,只要传递给 strtok 的原始缓冲区有效,它就有效。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM