【问题标题】:char* gets corrupted on the final iteration of a loopchar* 在循环的最后一次迭代中被破坏
【发布时间】:2021-01-21 18:57:21
【问题描述】:

所以,我正在尝试构建一个 string_split 函数来根据分隔符拆分 c 样式的字符串。

这是函数的代码:

char** string_split(char* input, char delim)
{
    char** split_strings = malloc(sizeof(char*));
    char* charPtr;

    size_t split_idx = 0;
    int extend = 0;

    for(charPtr = input; *charPtr != '\0'; ++charPtr)
    {
        if(*charPtr == delim || *(charPtr+1) == '\0')
        {
            if(*(charPtr+1) == '\0') extend = 1; //extend the range by one for the null byte at the end
            char* string_element = calloc(1, sizeof(char));

            for(size_t i = 0; input != charPtr+extend; ++input, ++i)
            {
                if(string_element[i] == '\0')
                {
                    //allocate another char and add a null byte to the end
                    string_element = realloc(string_element, sizeof(char) * (sizeof(string_element)/sizeof(char) + 1));
                    string_element[i+1] = '\0';
                }
                string_element[i] = *input;
            }
            printf("string elem: %s\n", string_element);
            split_strings[split_idx++] = string_element;
            
            //allocate another c-string if we're not at the end of the input
            split_strings = realloc(split_strings, sizeof(char*) *(sizeof(split_strings)/sizeof(char*) + 1));    

            //skip over the delimiter 
            input++;
            extend = 0;
        }
    }
    free(charPtr);
    free(input);
    return split_strings;
}

本质上,它的工作方式是有两个char*inputcharPtrcharPtr 从输入字符串的开头向上计数分隔符的下一个实例,然后input 从分隔符的前一个实例(或输入字符串的开头)开始计数,并将每个 char 复制到一个新的char*。一旦构建了字符串,它就会被添加到 char** 数组中。

还有一些有趣的位用于跳过分隔符并处理输入字符串的端点。该函数是这样使用的:

int main()
{
    char* str = "mon,tue,wed,thur,fri";
    char delim = ',';
    char** split = string_split(str, delim);

    return 1;
}

无论如何,它在大多数情况下都有效,除了返回的 char** 数组中的第一个 char* 已损坏,并且只是被随机垃圾占据。

例如从main 打印split 的元素会产生:

split: α↨▓
split: tue
split: wed
split: thur
split: fri

奇怪的是split_strings[0] 的内容,char* 的数组返回所需的标记是mon,对于这个例子应该是这样,直到主 for 循环的最后一个循环在string_split 函数中,特别是它的行:

split_strings[split_idx++] = string_element;

将其内容从mon 变为垃圾。任何帮助表示赞赏,谢谢。

【问题讨论】:

  • sizeof(string_element) 只是指针的大小,而不是它指向的内存大小。
  • 调用realloc() 时不会增加string_elementsplit_string 数组的大小。
  • @Barmar 所以我想我可以将strlen 用于string_element,但是如何将split_strings 的大小扩大一个char*?感谢您到目前为止的说明。
  • @jf192210 不清楚该函数应返回什么:只是子字符串的位置或由分隔符分隔的子字符串的副本。
  • 你应该使用一个变量来保存每个指针的当前分配。然后递增该变量并在realloc() 中使用它。

标签: c split c-strings string-literals function-definition


【解决方案1】:

您的函数不正确,至少因为它试图释放传递的字符串

char** string_split(char* input, char delim)
{

    //...

    free(charPtr);
    free(input);
    return split_strings;
}

在你的程序中是字符串文字

char* str = "mon,tue,wed,thur,fri";
char delim = ',';
char** split = string_split(str, delim);

您不能释放字符串文字。

并且第一个参数应该有限定符const

您的函数中还有许多其他错误。

例如此语句中使用的表达式sizeof(string_element)/sizeof(char)

string_element = realloc(string_element, sizeof(char) * (sizeof(string_element)/sizeof(char) + 1));

不会产生早期分配给指针string_element 指向的数组的字符数。并且为每个新字符重新分配数组没有太大意义。

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

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

char ** string_split( const char *s, char delim )
{
    size_t n = 1;
    char **a = calloc( n, sizeof( char * ) );
    
    while ( *s )
    {
        const char *p = strchr( s, delim );
        
        if ( p == NULL ) p = s + strlen( s );
        
        if (  p != s )
        {
            char *t = malloc( p - s + 1 );
            
            if ( t != NULL ) 
            {
                memcpy( t, s, p - s );
                t[p-s] = '\0';
            }
            
            char **tmp = realloc( a, ( n + 1 ) * sizeof( char * ) );
            
            if ( tmp == NULL )
            {
                free( t );              
                break;
            }
            
            a = tmp;
            
            a[n-1] = t;
            a[n] = NULL;
            ++n;
        }
        
        s = p + ( *p != '\0' ); 
    }
    
    return a;
}

int main(void) 
{
    char* str = "mon,tue,wed,thur,fri";
    char delim = ',';

    char **split = string_split( str, delim );
    
    for ( char **p = split; *p != NULL; ++p )
    {
        puts( *p );
    }
    
    for ( char **p = split; *p != NULL; ++p ) free( *p );
    free( split );
    
    return 0;
}

程序输出是

mon
tue
wed
thur
fri

【讨论】:

  • 好吧,也许我对free 的使用是错误的,但除此之外我的函数工作正常。我将实施对free 的更改。我也喜欢你的实现,但个人很难理解这么短的变量名。
  • @jf192210 甚至你修改的函数也是错误的。例如它返回未初始化的指针 char** split_strings = malloc(sizeof(char*));在用户传递空字符串的情况下。
  • @jf192210 指针 split_strings 指向的数组中的最后一个指针也不等于 NULL。
  • 我认为将其设置为 NULL 是个好主意。我已经更新了我对这个问题的回复,你有没有机会检查一下是否有错误?谢谢。
  • @jf192210 我已经写了对你问题的回答。:) 我没有时间定期回到这个问题。:)
【解决方案2】:

最终的功能对于任何想知道的人来说都应该是非常简单的。

char** string_split(char* input, char delim, bool skip_delim)
{
    assert(*input != '\0');

    char** split_strings = malloc(sizeof(char*));
    char* charPtr;

    size_t split_idx             = 0;
    size_t num_allocated_strings = 1;
    size_t extend                = 0;

    for(charPtr = input; *charPtr != '\0'; ++charPtr)
    {
        if(*charPtr == delim || *(charPtr+1) == '\0')
        {
            if(*(charPtr+1) == '\0') extend = 1; //extend the range by one for the null byte at the end
            char* string_element = calloc(1, sizeof(char));

            for(size_t i = 0; input != charPtr+extend; ++input, ++i)
            {
            if(string_element[i] == '\0')
            {
                //allocate another char and add a null byte to the end
                char* temp = realloc(string_element, sizeof(char) * (strlen(string_element) + 1));
                if(temp == NULL)
                {
                    free(string_element);
                    free(split_strings);
                    break;
                }
                string_element = temp;
                string_element[i+1] = '\0';
            }
            string_element[i] = *input;
            }
            split_strings[split_idx++] = string_element;
            num_allocated_strings++;
            
            //allocate another c-string
            char** temp = realloc(split_strings, sizeof(char*) * num_allocated_strings);    
            if(temp == NULL)
            {
                free(string_element);
                free(split_strings);
                break;
            }
            split_strings = temp;

            //skip over the delimiter if required
            if(skip_delim) input++;
            extend = 0;
        }
    }
    split_strings[num_allocated_strings-1] = NULL;
    return split_strings;
}

【讨论】:

    猜你喜欢
    • 2013-03-18
    • 2014-06-21
    • 2011-07-11
    • 1970-01-01
    • 2013-05-17
    • 2013-07-29
    • 2018-02-06
    • 2017-12-31
    • 2010-09-22
    相关资源
    最近更新 更多