返回指向 char* 的指针会破坏数据

Returning pointer to char* ruins data

我正在尝试编写一个函数,它将沿 space (' ') 拆分字符串而不更改原始字符串,将所有标记放入数组中,然后 return那个数组。我 运行 遇到的问题是 return 指针。下面是我的代码。

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

char **split_line(char *ln) {
    char **tokens, *tok, line[256];
    int j;

    strcpy(line, ln);

    tokens = calloc(64, sizeof(char*));
    for (int i = 0; i < 64; i++)
        tokens[i] = calloc(64, sizeof(char));

    tokens[0] = strtok(line, " ");
    for (j = 1; (tok = strtok(NULL, " ")) != NULL && j < 63; j++) 
        tokens[j] = tok;
    tokens[j] = NULL;

    return tokens;
}

int main(void) {
    char **cut_ln, *input;

    input = "Each word in this string should be an element in cut_ln.";
    cut_ln = split_line(input);

    printf("`%s`\n", input);
    for (int i = 0; cut_ln[i] != NULL; i++)
        printf("[%d]: `%s`\n", i, cut_ln[i]);

    return 0;
}

当 运行 时,这给出:

`This word in this string should be an element in cut_ln.`
[0]: `This`
[1]: `wo1`
[2]: `�={G+s`
[3]: `G+s`
[4]: `string`
[5]: ``
[6]: `0����`
[7]: `��`
[8]: ``
[9]: ``
[10]: ``

当我尝试在 split_line 函数中打印 tokens 的内容时,它给出了预期的结果。但是,当 tokens 被 returned 并分配给一个变量,然后打印时,它会给出上面显示的结果。我做错了什么?

当您 return tokens 时,它包含从 strtok 编辑的指针 return,这些指针指向 line。但是line此时已经不存在了。

您分配了内存并使 tokens 的各种元素指向分配的内存。不要用 strtok.

中的 return 值覆盖这些值

对于初学者来说,函数的声明至少应该像

char ** split_line( const char *ln );

因为传递的字符串在函数内没有被改变。

如果声明将指定分隔符的第二个参数,该函数将更加灵活。

char ** split_line( const char *ln, const char *delim );

其次,使用像 64256 这样的幻数是个坏主意。如果传递的字符串包含超过 63 个标记,或者当字符串包含至少一个长度大于 63.

的标记时,该函数将不起作用

您在此循环中动态分配了 64 个数组

for (int i = 0; i < 64; i++)
    tokens[i] = calloc(64, sizeof(char));

并将它们的地址分配给变量tokens指向的数组元素。但是您立即将指针重新分配给本地数组 line.

内的地址
tokens[0] = strtok(line, " ");
for (j = 1; (tok = strtok(NULL, " ")) != NULL && j < 63; j++) 
    tokens[j] = tok;

因此该函数会产生大量内存泄漏。并且返回的指针数组将包含无效指针,因为退出函数后局部数组line将不存在。

还有这个说法

tokens[j] = NULL;

是多余的。使用 calloc 您最初已经将所有指针设置为 NULL.

函数如下面的演示程序所示。

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

char ** split_line( const char *s, const char *delim )
{
    size_t n = 0;
    
    for ( const char *p = s; *p; )
    {
        p += strspn( p, delim );
        
        if ( *p )
        {
            ++n;
            p += strcspn( p, delim );
        }
    }
    
    char **tokens = calloc( n + 1, sizeof( char * ) );
    
    if ( tokens )
    {
        size_t i = 0;
        int success = 1;
        
        for ( const char *p = s; success && *p; i += success )
        {
            p += strspn( p, delim );
        
            if ( *p )
            {
                const char *q = p;
                p += strcspn( p, delim );
                
                tokens[i] = malloc( p - q + 1 );
                
                if ( ( success = tokens[i] != NULL ) )
                {
                    memcpy( tokens[i], q, p - q );
                    tokens[i][p-q] = '[=15=]';
                }
            }
        }
        
        if ( !success )
        {
            for ( char **p = tokens; *p; ++p )
            {
                free( *p );
            }
            
            free( tokens );
        }
    }
    
    return tokens;
}

int main(void) 
{
    const char *s = "Each word in this string should be an element in cut_ln.";
    
    char **tokens = split_line( s, " " );
    
    if ( tokens )
    {
        for ( char **p = tokens; *p; ++p )
        {
            puts( *p );
        }
        
        for ( char **p = tokens; *p; ++p )
        {
            free( *p );
        }
    }
    
    free( tokens );

    return 0;
}

程序输出为

Each
word
in
this
string
should
be
an
element
in
cut_ln.