【问题标题】:invalid pointer when using strtok_r使用 strtok_r 时指针无效
【发布时间】:2021-02-12 23:45:28
【问题描述】:

在运行我的代码时(如第一个代码块所示),我收到此错误: *** Error in `./a.out': free(): invalid pointer: 0x0000000001e4c016 *** 我找到了一个修复程序(显示在第二个代码块中),但我不明白为什么会首先发生错误。

我阅读了有关 strtok_r 的文档,但我不明白为什么将“str”分配给新的 char* 可以解决问题。

“rest = str”不是表示 rest 和 str 指向同一个内存块。这如何解决问题???

破码:

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

int main() 
{ 
    char* str = (char*) malloc(sizeof(char) * 128);
    char* token;
    
    printf("Enter something: ");  
    fgets(str, 128, stdin);
  
    while ((token = strtok_r(str, " ", &str))) { 
        printf("%s\n", token); 
    }
    
    free(str);
    return (0); 
}

固定代码:

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

int main() 
{ 
    char* str = (char*) malloc(sizeof(char) * 128);
    char* token; 
    char* rest = str; 
    
    printf("Enter something: ");  
    fgets(str, 128, stdin);
  
    while ((token = strtok_r(rest, " ", &rest))) { 
        printf("%s\n", token); 
    }
    
    free(str);
    return (0); 
}

【问题讨论】:

  • NEVER NEVER NEVER NEVER NEVER!!!!! 转换从malloc() 返回的值。只需#include &lt;stdlib.h&gt; 并让编译器检查结果。如果你添加一个演员,你告诉编译器你知道你在做什么,而你不知道。

标签: c dynamic-memory-allocation free c-strings strtok


【解决方案1】:

显然strtok_r 的调用改变了指针str,该指针作为第三个参数通过引用传递给调用。

while ((token = strtok_r(str, " ", &str))) { 
                                   ^^^^
    printf("%s\n", token); 
}

因此,在调用函数后,指针str 可以指向原始字符串内部。因此它不会存储调用malloc 后的值。

因此使用辅助变量rest允许将初始值保留在指针str中。

注意你调用的函数不正确。这是它的描述

在第一次调用 strtok_r() 时,str 应该指向要被 已解析,并忽略 saveptr 的值。在随后的通话中,str 应该是NULL,并且saveptr 应该与上一个保持不变 打电话。

因此对于函数的第二次和后续调用,第一个参数应为NULL

【讨论】:

    【解决方案2】:

    你应该写:

    #include <stdio.h> 
    #include <string.h> 
    #include <stdlib.h>
    
    int main() 
    { 
        char  str[128];
        char *token; 
        char *rest = str; 
        
        printf("Enter something: ");  
        fgets(str, sizeof str, stdin);
      
        for (token = strtok_r(rest, " ", &rest);
             token = strtok_r(NULL, " ", &rest);
             /* just nothing here */)
        { 
            printf("%s\n", token); 
        }
        
        return (0); 
    }
    
    • 首先,您不需要为str 分配内存,因为您可以定义一个本地数组来存储数据。您可以使用sizeof 运算符,这样如果您决定更改str 的大小,就不会冒在两个地方不更新它的风险。在使用malloc 的情况下,您最好使用#define 一个常量来保存该值,同时在使用分配缓冲区大小的任何地方使用该常量。
    • 其次,从不转换malloc的返回值。相信我,这是一个非常坏的习惯。当你进行强制转换时,你告诉编译器你知道你在做什么。转换 malloc 的值是 C 中没有 void 类型时的遗留物(直到八十年代中期)。曾几何时,malloc() 曾经返回一个 char *,这通常不是您想要的指针类型,您必须强制转换指针以匹配您正在使用的指针。不仅不建议在 2021 年强制转换 malloc() 返回值,而且强烈建议不要这样做,因为许多错误来自于强制转换(当你做错事时编译器会警告你,但如果你强制转换值,它不会,通常这被解释为你告诉编译器你故意在做一些奇怪的事情,所以编译器会闭嘴,不再多说)
    • 第三,如果要提取一个字符串中的所有token,第一次调用strtok()(或者他的朋友strtok_w),第一个参数指向字符串的开头,但是其余的调用必须使用NULL 作为它的第一个参数来完成, 否则您将在刚刚返回的字符串内 进行搜索,而不是在第一次出现的后面。您的问题不在于使用strtokstrtok_r,因为strtok_r 只是strtok 的可重入版本,它允许您在第一个循环内启动嵌套循环,或从不同线程调用它。李>

    【讨论】:

      【解决方案3】:

      堆内存管理跟踪用于实现库调用的基内存地址。我们需要保留这些基地址,以便在必要时释放/重新分配。

      既然您找到了使用strtok_r() 的方法,我更喜欢以下版本:

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      
      int main () {
          char orgStr [] = "strtok does not allow you to have 2 pointers going at once on the same string";
      
          for (char *token, *rmdStr = orgStr; token = strtok_r (NULL, " ", &rmdStr); /* empty */) {
              printf ("%s\n", token);
          }
          /* Original string is chopped up with NULCHAR, now unreliable */
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-05
        • 2020-09-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-07
        • 1970-01-01
        • 2019-06-02
        相关资源
        最近更新 更多