You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言ft_split函数内存分配释放错误与段错误问题求助

ft_split函数实现问题排查与修复

函数要求

使用malloc分配内存,返回以字符c为分隔符分割字符串s得到的字符串数组,数组必须以NULL指针结尾。

现存问题

  • 输入末尾带分隔符的测试字符串(" To be or not to be that is the question ")时触发段错误(Segmentation fault)
  • 输入测试字符串"Hello there"时,虽然能输出正确分割结果,但输出完成后同样触发段错误,输出表现为:

Hello
there
Segmentation fault

  • 不清楚如何正确释放函数内分配的内存

原实现代码

size_t  ft_strlen(const char *s)
{
    size_t  i;

    i = 0;
    while (s[i] != '\0')
    {
        i++;
    }
    return (i);
}

char    *ft_strcpy(char *dest, const char *src)
{
        int     i;

        i = 0;
        while (src[i] != '\0')
        {
                dest[i] = src[i];
                i++;
        }
        dest[i] = '\0';
        return (dest);
}

char    *string_length(char const *str, char c, int i)
{
    int     j;
    char    *string;

    j = 0;
    // 调试第一个测试字符串时,断点直接触发在这行
    string = (char *)malloc((ft_strlen(str)) * sizeof(*string));
    if(!string)
        return (NULL);
    while (str[i] == c)
        i++;
    while (str[i])
    {
        *string = str[i];
        if (str[i + 1] == c || str[i + 1] == '\0')
        {
            string++;
            j++;
            *string = '\0';
            return (string - j);
        }
        string++;
        j++;
        i++;
    }
    return (0);
}

int count_strings(char const *str, char c)
{
    int i;
    int count;
    int def;

    i = 0;
    count = 0;
    def = 1;
    while (str[i] == c)
        i++;
    while (str[i])
    {
        if (str[i] == c && def == 1)
        {
            i++;
            def = 0;
        }
        if (str[i] != c && def == 0)
        {
            count++;
            def = 1;
        }
        i++;
    }
    return (count);
}

int first_del(char const *str, char c, int i)
{
    while (str[i] == c)
        i++;
    return (i);

}
char    **ft_split(char const *s, char c)
{
    int     count;
    int     i;
    int     len;
    int     num_of_strings;
    char    **split;
    char    *string;
    char    **it;

    i = 0;
    count = 0;
    num_of_strings = count_strings(s, c);
    split = (char **)malloc((num_of_strings + 1) * sizeof(char *));
    if (!split)
        return (NULL);
    split[num_of_strings + 1] = NULL;
    while (i <= num_of_strings)
    {
        count = first_del(s, c, count);
        string = string_length(s, c, count);
        len = ft_strlen(string);
        split[i] = (char *)malloc(len + 1);
        ft_strcpy(split[i], string);
        count += len;
        i++;
        free(string);
        string = NULL;
    }
    return (split);
}
int main(void)
{
    char s[] = "   To    be    or    not    to    be    that     is   the question ";
    char **split_strings = ft_split(s, ' ');
    for (int i = 0; i < 10; i++)
        printf("%s\n", split_strings[i]);

    char s2[] = "Hello there";
    char **split_strings2 = ft_split(s2, ' ');
    for (int i = 0; i < 2; i++)
        printf("%s\n", split_strings2[i]);
} 

问题根因与修复

1. 字符串计数逻辑错误

count_strings函数返回值比实际分割结果少1:初始跳过前导分隔符后没有统计第一个有效字符串,只有遇到分隔符后再碰到非分隔符才计数,比如"Hello there"实际有2个分割结果,函数只会返回1。
修复逻辑:跳过前导分隔符后如果没到字符串结尾,先给计数加1;后续遍历遇到连续分隔符时直接跳过全部,跳过后还有有效字符则计数加1。

int count_strings(char const *str, char c)
{
    int i = 0;
    int count = 0;
    // 跳过前导分隔符
    while (str[i] == c)
        i++;
    // 第一个有效字符串直接计数
    if (str[i] != '\0')
        count++;
    while (str[i])
    {
        if (str[i] == c)
        {
            // 跳过所有连续分隔符
            while (str[i] == c)
                i++;
            // 跳过后还有有效字符则计数+1
            if (str[i] != '\0')
                count++;
            continue;
        }
        i++;
    }
    return count;
}

2. NULL终止位越界写入

给split数组分配了num_of_strings + 1个元素(索引范围0到num_of_strings),但原代码写的是split[num_of_strings + 1] = NULL;,写入位置超出了已分配内存的范围,属于越界写,是段错误的核心诱因。
修复:改为split[num_of_strings] = NULL;

3. 循环次数多跑一轮

原循环条件为i <= num_of_strings,当i等于num_of_strings时,已经到了要存储NULL的终止位,此时还调用string_length取子串,会访问字符串结束后的非法内存,触发段错误。
修复:循环条件改为i < num_of_strings,循环结束后再手动设置终止位NULL。

4. 临时子串逻辑冗余+空指针风险

string_length函数每次分配和原串等长的内存存临时子串,完全没有必要;且当遍历到字符串末尾无有效子串时会返回NULL,后续传给ft_strlen会直接触发空指针访问错误。
修复:去掉临时子串的分配逻辑,直接从当前位置计算子串长度即可,省掉不必要的malloc/free开销,也避免空指针问题。

5. 内存释放逻辑错误

ft_split返回的是二级指针,释放时需要先遍历数组,逐个释放每个分割出来的字符串(一级指针),最后再释放二级指针本身。原代码在ft_split内部释放临时字符串的逻辑是对的,但main函数中没有释放最终返回的数组,会造成内存泄漏。
正确释放示例:

int j = 0;
while (split_strings[j])
{
    free(split_strings[j]);
    j++;
}
free(split_strings);

6. main函数遍历越界

原代码写死循环10次、2次打印结果,一旦实际分割数量和写死的值不匹配,就会访问越界内存触发段错误。
修复:遍历到数组元素为NULL时停止即可:

for (int i = 0; split_strings[i] != NULL; i++)
    printf("%s\n", split_strings[i]);

修复后完整可运行版本

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

size_t  ft_strlen(const char *s)
{
    size_t  i = 0;
    while (s[i] != '\0')
        i++;
    return (i);
}

char    *ft_strncpy(char *dest, const char *src, unsigned int n)
{
    unsigned int i = 0;
    while (i < n && src[i] != '\0')
    {
        dest[i] = src[i];
        i++;
    }
    while (i < n)
    {
        dest[i] = '\0';
        i++;
    }
    return (dest);
}

int count_strings(char const *str, char c)
{
    int i = 0;
    int count = 0;
    while (str[i] == c)
        i++;
    if (str[i] != '\0')
        count++;
    while (str[i])
    {
        if (str[i] == c)
        {
            while (str[i] == c)
                i++;
            if (str[i] != '\0')
                count++;
            continue;
        }
        i++;
    }
    return count;
}

int first_del(char const *str, char c, int i)
{
    while (str[i] == c)
        i++;
    return (i);
}

char    **ft_split(char const *s, char c)
{
    int     i = 0;
    int     pos = 0;
    int     sub_len;
    int     num_of_strings;
    char    **split;

    num_of_strings = count_strings(s, c);
    split = (char **)malloc((num_of_strings + 1) * sizeof(char *));
    if (!split)
        return (NULL);
    pos = first_del(s, c, pos);
    while (i < num_of_strings)
    {
        sub_len = 0;
        while (s[pos + sub_len] && s[pos + sub_len] != c)
            sub_len++;
        split[i] = (char *)malloc(sub_len + 1);
        if (!split[i])
            return (NULL);
        ft_strncpy(split[i], s + pos, sub_len);
        split[i][sub_len] = '\0';
        pos += sub_len;
        pos = first_del(s, c, pos);
        i++;
    }
    split[num_of_strings] = NULL;
    return (split);
}

int main(void)
{
    char s[] = "   To    be    or    not    to    be    that     is   the question ";
    char **split_strings = ft_split(s, ' ');
    for (int i = 0; split_strings[i] != NULL; i++)
        printf("%s\n", split_strings[i]);
    // 释放内存
    int j = 0;
    while (split_strings[j])
    {
        free(split_strings[j]);
        j++;
    }
    free(split_strings);

    printf("--------\n");
    char s2[] = "Hello there";
    char **split_strings2 = ft_split(s2, ' ');
    for (int i = 0; split_strings2[i] != NULL; i++)
        printf("%s\n", split_strings2[i]);
    // 释放内存
    j = 0;
    while (split_strings2[j])
    {
        free(split_strings2[j]);
        j++;
    }
    free(split_strings2);
    return 0;
}

内容的提问来源于stack exchange,提问作者Nizz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 14:09:15