简体   繁体   English

使用 strtok_r 时指针无效

[英]invalid pointer when using strtok_r

When running my code (shown in the first code block), I get this error: *** Error in `./a.out': free(): invalid pointer: 0x0000000001e4c016 *** I found a fix (which is shown in the second code block), but I don't understand why the error is happening in the first place.运行我的代码时(显示在第一个代码块中),我收到此错误: *** Error in `./a.out': free(): invalid pointer: 0x0000000001e4c016 ***我找到了一个修复程序(如图所示在第二个代码块中),但我不明白为什么首先会发生错误。

I read the documentation regarding strtok_r, but I don't understand why assigning "str" to a new char* fixes the problem.我阅读了有关 strtok_r 的文档,但我不明白为什么将“str”分配给新的 char* 可以解决问题。

Doesn't "rest = str" mean that rest and str point to the same block of memory. “rest = str”是否意味着 rest 和 str 指向 memory 的同一块。 How does this fix the problem???这如何解决问题???

Broken code:损坏的代码:

#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); 
}

Fixed code:固定代码:

#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); 
}

It looks evidently that a call of strtok_r changes the pointer str that is passed to the call by reference as the third parameter.显然,对strtok_r的调用更改了指针str ,该指针作为第三个参数通过引用传递给调用。

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

So after a call of the function the pointer str can point inside the original string.因此,在调用 function 之后,指针str可以指向原始字符串内部。 So it will not store the value that it had after a call of malloc .所以它不会存储它在调用malloc之后的值。

Thus using the auxiliary variable rest allows to keep the initial value in the pointer str .因此,使用辅助变量 rest 允许将初始值保留在指针str中。

Pay attention to that you are calling the function incorrectly.请注意,您错误地调用了 function。 Here is its description这是它的描述

On the first call to strtok_r() , str should point to the string to be parsed, and the value of saveptr is ignored.在第一次调用strtok_r()时, str应该指向要解析的字符串,并且忽略saveptr的值。 In subsequent calls, str should be NULL , and saveptr should be unchanged since the previous call.在随后的调用中, str应该是NULL ,并且saveptr应该自上次调用以来保持不变。

So for the second and subsequent calls of the function the first argument shall be NULL .因此,对于 function 的第二次和后续调用,第一个参数应为NULL

You should write:你应该写:

#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); 
}
  • First, you don't need to allocate the memory for str , as you can define a local array to store the data.首先,您不需要为str分配 memory ,因为您可以定义一个本地数组来存储数据。 You can use the sizeof operator so you don't run the risk of not updating it in two places if you decide to change the size of str .您可以使用sizeof运算符,这样如果您决定更改str的大小,就不会冒在两个地方不更新它的风险。 In the case of using malloc you had better #define a constant to hold the value while you use the constant everywhere you are using the size of the allocated buffer.在使用malloc的情况下,您最好#define一个常量来保存该值,同时在使用分配缓冲区大小的任何地方使用该常量。
  • Second, never cast the returned value of malloc .其次,永远不要malloc的返回值。 Believe me, it is a very bad habit.相信我,这是一个非常坏的习惯。 When you do a cast, you tell the compiler you know what you are doing.当你进行强制转换时,你告诉编译器你知道你在做什么。 Casting the value of malloc is a legacy from when there was no void type in C (this is so far as the middle eighties).铸造 malloc 的值是malloc中没有void类型时的遗留物(直到八十年代中期)。 Once upon a time, malloc() used to return a char * which normally was not the type of pointer you wanted, and you had to cast the pointer to match the one your were using.曾几何时, malloc()用于返回一个char * ,这通常不是您想要的指针类型,您必须强制转换指针以匹配您正在使用的指针。 Casting malloc() return value in 2021 is not only not recommended, but it is strongly discouraged, as many errors come from having cast it (the compiler warns you when you are doing something bad, but it will not, if you cast the value, normally that is interpreted as you telling the compiler you are doing something weird on purpose, so the compiler shuts up, and doesn't say more)不仅不建议在 2021 年强制转换malloc()返回值,而且强烈建议不要这样做,因为许多错误来自于强制转换(编译器会在您做错事时警告您,但如果您强制转换该值,则不会,通常这被解释为你告诉编译器你故意在做一些奇怪的事情,所以编译器关闭,不再多说)
  • Third, if you are going to extract all the tokens in a string, the first time you need to call strtok() (or his friend strtok_w ) with a first parameter pointing to the start of the string, but the rest of the calls have to be done with NULL as it first parameter, or you'll be searching inside the string just returned , and not behind the first occurrence.第三,如果要提取字符串中的所有标记,第一次调用strtok() (或他的朋友strtok_w )时,第一个参数指向字符串的开头,但调用的 rest 有以NULL作为第一个参数来完成,否则您将在刚刚返回的字符串中进行搜索,而不是在第一次出现的后面。 Your problem was not about using strtok or strtok_r , as strtok_r is just a reentrant version of strtok that allows you to start a nested loop, inside the first, or to call it from different threads.您的问题与使用strtokstrtok_r ,因为strtok_r只是strtok的可重入版本,它允许您在第一个循环内启动嵌套循环,或从不同的线程调用它。

Heap memory management keeps track of base memory addresses for implementing library calls.堆 memory 管理跟踪基本 memory 地址以实现库调用。 We need to preserve those base-addresses to free/reallocate whenever necessary.我们需要保留这些基地址,以便在必要时释放/重新分配。

Now that you found a way to use strtok_r() , I prefer the below version:既然您找到了使用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 */
}

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

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