C语言 使用strtok多次拆分一个字符串会导致意外行为

fslejnso  于 2023-10-16  发布在  其他
关注(0)|答案(3)|浏览(120)

我遇到了一个问题,我需要有人来解释发生了什么。
我正在解析一个像a=1&b=2&c=3这样的字符串,我想把它解析成["a=1","b=2","c=3"],然后拆分每个x=y
代码如下:

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

void f2(char *src)
{
    char *dest = (char *) calloc(strlen(src)+1,sizeof(char));    
   
    strcpy(dest,src); // I copy src to dest to guard src from being messed up with strtok

   // when I comment out the below line, src address doesn't change
   // but why is it changing the src address? I have copied the src to dest!
    char *token = strtok(dest, "="); 
    printf("dest addr: %p token addr: %p \n",dest,token);
}
void f1(char *src)
{
    char *token = strtok(src, "&");
    while (token)
    {
        printf("src addr: %p ", token);
        f2(token);
        token = strtok(NULL, "&");
    }
}

我运行代码如下:

TEST(CopyPointer, CopyStrTok)
{
    char str[]="a=1&b=2&c=3";
    f1(str);
}

结果如下:

src addr: 0x7ffd4a00ec0c dest addr: 0x558a755d3350 token addr: 0x558a755d3350 // it's fine 
src addr: 0x558a755d3352 dest addr: 0x558a755d3370 token addr: 0x558a755d3370 
//               ^                         ^    
// now src addr is changed and it's pointing to the second character of dest

我不能解释为什么src是由f2操作的,而我已经将src复制到另一个名为dest的变量?

更正:

正如其中一个答案中提到的,src地址没有改变,只是令牌地址改变了!

jyztefdp

jyztefdp1#

strtok函数使用静态内部数据来跟踪它的位置。
因此,当你在f1中调用strtok时,它与该函数中的src相关联(这与测试函数中的str相同),但当你在f2中再次调用它并将dest作为第一个参数时,它现在与f2中的dest关联。然后,当你在f1中再次调用strtok,并将NULL作为第一个参数时,它使用了一个内部指针,指向一个不再在作用域中的dest成员。这会触发undefined behavior
如果你想使用多个级别的strtok,你应该使用strtok_r,它允许用户传入一个附加参数来存储它的状态。

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

void f2(char *src)
{
    char *p = NULL;
    char *token = strtok_r(src, "=", &p);
    printf("token a=%s\n", token);
    token = strtok_r(NULL, "=", &p);
    printf("token b=%s\n", token);
}
void f1(char *src)
{
    char *p = NULL;
    char *token = strtok_r(src, "&", &p);
    while (token)
    {
        f2(token);
        token = strtok_r(NULL, "&", &p);
    }
}

int main()
{
    char str[] = "a=1&b=2&c=3";
    f1(str);
    return 0;
}

输出量:

token a=a
token b=1
token a=b
token b=2
token a=c
token b=3
clj7thdc

clj7thdc2#

为什么要搞得这么复杂?目标是提取字符串的 * 对 *:

int main( void ) {
    char str[] = "a=1&b=2&c=3";

    char *p1, *p2;
    for( p1 = str; ( p1 = strtok( p1, "&=") ) != NULL; p1 = NULL ) {
        p2 = strtok( NULL, "&=" );

        printf( "%s ... %s\n", p1, p2 );
    }
    return 0;
}

输出量:

a ... 1
b ... 2
c ... 3
sqserrrh

sqserrrh3#

src没有改变,你看到的变化,因为你不打印src只有token(但在你的格式字符串是src而不是令牌)
校正版本

void f1(char *src)
{
    char *token = strtok(src, "&");
    while (token)
    {
        printf("src addr: %p token addr: %p ", (void *)src, (void*)token);
        f2(token);
        token = strtok(NULL, "&");
    }
}

相关问题