2

最近我在做一个涉及用 C 编写汇编程序的学校项目,我遇到了将指针传递给strtok. 我克服了代码中的错误,但我想了解为什么我所做的事情不起作用。

下面是发生错误的函数的简化示例。

void processText(FILE *f) { //takes a file opened for reading.
  char *token, *temp;
  int len;
  char buff[81]; //line buffer

  while (fgets(buff, 81, f) != NULL) { //read in each line one at a time
    len = strlen(buff);
    token = strtok(buff, "#"); //first assignment

    /*if there is a comment, strip and print it*/
    if (len != strlen(token)) {
      printf("comment: %s", strtok(NULL, "#"));
    }

    //len = strlen(token);
    //temp = malloc(len + 1);
    //strcpy(temp, token);
    //token = strtok(temp, " ");

    token = strtok(token, " "); //this segfaults...

    printf("first word: %s\n", token);
    //free(temp);
  }
}

用上面的注释代码块替换有问题的行(并在最后释放 temp)解决了这个问题,但它显然需要我无缘无故地制作我的数据的额外副本,这让我想知道为什么我不能除非我以某种方式引用它,否则请使用现有数据。

据我了解,该strtok函数分别接受参数char *const char *。在我看来,在第一次分配之后,token并且buff应该看起来strtokchar *' 指向相同的位置——也就是说,strtok作为它的第一个参数接收的值将是一个指向内存中保存该值的位置的指针无论我通过buff哪个(buff或)的第一个字符。token

我(松散地)理解这一点char[]并且char *根本不同,因为数组是静态分配的,指针是动态分配的,但我不明白为什么这在这种情况下会有任何不同,特别是考虑到系统对于从.strlenstrcpytoken

我很想了解这里发生了什么。在此先感谢您的时间!

4

4 回答 4

4

strtok是一种奇怪的功能,因为它会改变输入缓冲区以插入 nuls,并保持内部状态。

你通常最好避免strtok和使用strchr。对指向同一缓冲区的指针重复调用 strtok 可能会崩溃。

无论哪种方式,作为一般规则,函数都不会拒绝 malloc 的内存。无论您是否使用 malloc,这都是您发生腐败的线索。在 Valgrind 或类似的环境下运行你的程序会让它在哪里更明显。

那么,为什么它会在这里崩溃呢?

如果该行包含 a#那么第一次调用strtok#用 nul 替换。无论哪种方式,它都会返回一个指向字符串的第一个非 # 字节的指针。

您对 strtok 的第二次调用会打印第一个和第二个之间的字符串,#这可能不是您想要的。

您可能在这里遇到的一个问题是strtok在不包含非分隔符的行上返回 null。例如,空行或只有空格的行将导致token设置为 null,然后 printf 将崩溃。


更好的版本(未经测试)是这样的:

char *star, *token, *arg;
star = strchr(buff, '#');
if (star) *star = '\0';
token = strtok(buff, " \t");
if (!token)
    continue; // empty line
printf("operator: %s\n", token);
while ((arg = strtok(NULL, " \t")) != NULL) 
    printf("arg: %s\n", arg);

这里重要的事情:

  • 检查是否为空;)
  • 我们不使用 strtok 来做更容易用 strchr 完成的事情
  • 对于每个字符串,首先 strtok 指向缓冲区并获取第一个单词,然后其余使用 null

但正如我所说,如果语法不平凡,我会强烈考虑使用避免它。

于 2014-05-01T04:15:48.760 回答
1

为避免段错误,请在将返回的值传递给需要非空指针的函数(例如and )之前检查它strtok是否不是。 NULLprintfstrlen

如果每个字符串后面都包含 a ,则您的代码将起作用#;否则会导致未定义的行为。

您不能strtok在字符串文字上使用,因为strtok写入缓冲区,并且字符串文字不可写。不过,char buff[81];还好。

顺便说一句len != strlen(token),总是正确的。如果您不清楚这一点,请重新阅读 strtok 的文档以了解它的作用。

于 2014-05-01T04:20:51.617 回答
0

所以真的,“为什么我的 C 程序崩溃”的更一般的答案不是将它发布到 StackOverflow 并询问,或者更改它直到问题被隐藏,而是:

  1. 检查函数是否可以返回 null,如果可以,是否正确处理 -strtok可以,但不能。
  2. 在 Valgrind 或任何其他内存检查器下运行它,一旦错误发生就会标记错误,而不是它可能会破坏内存并在以后崩溃。
  3. 逐渐减少代码和输入数据,直到出现问题的最小情况。
  4. 仔细阅读您正在调用的函数的手册,并检查它们如何处理这些输入以及它们的前置/后置条件是什么。
  5. 阅读标记为 strtok 的其他 460 个问题中的一些,因为几乎可以肯定以前有人问过它。
于 2014-05-02T00:37:22.810 回答
0

此代码可以正常工作,没有任何错误:

#include <stdio.h>
#include <string.h>
int main() {
    char *token, *temp;
    int len;
    char buff[81] = "test me # a comment"; 

    len = strlen(buff);
    token = strtok(buff, "#"); 

    if (len != strlen(token)) {
        printf("comment: %s\n", strtok(NULL, "#"));
    }

    token = strtok(token, " "); 

    printf("first word: %s\n", token);
    return 0;
}

在给定段之前,您可能还有其他问题。我没有看到任何malloc真正使用的理由。

于 2014-05-01T04:21:06.437 回答