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

C语言内存分配出现Invalid write/read错误的原因与修复

分割字符串函数的内存错误分析与修复

我实现了一个split_text函数,接收char数组text,以指定token为分隔符将其分割为子数组,结果存储在指向子字符串的指针数组res中。代码运行可正常输出分割结果,但用valgrind检测时出现大量Invalid write of size 8和Invalid read of size 8错误。

原代码

int split_text(char * text, char * token, char ** res)
{
    int x =0;
    char * tmp = strtok(text , token);
    while (tmp != NULL)
    {
        res[x] = malloc(1024);
        strcpy(res[x], tmp);
        x++;
        *res = realloc(*res, sizeof(char*)*(x+1));
        tmp = strtok(NULL , token);
    }
    return x;
}

int main()
{
    char text[] = "Hello world this is elliot";

    char ** ptr = (char**)malloc(sizeof(char*));

    int x = split_text(text, " ", ptr);

    
    for (int i =0; i< x; i++)
        printf("%s\n", ptr[i]);
    
    return 0;
}

Valgrind检测输出

==74210== Memcheck, a memory error detector
==74210== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==74210== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==74210== Command: ./a.out
==74210== 
==74210== Invalid write of size 8
==74210==    at 0x1091D0: split_text (split.c:23)
==74210==    by 0x1092B2: main (split.c:38)
==74210==  Address 0x4a3f048 is 0 bytes after a block of size 8 alloc'd
==74210==    at 0x483F7B5: malloc (vg_replace_malloc.c:381)
==74210==    by 0x109294: main (split.c:36)
==74210== 
==74210== Invalid read of size 8
==74210==    at 0x1091E7: split_text (split.c:24)
==74210==    by 0x1092B2: main (split.c:38)
==74210==  Address 0x4a3f048 is 0 bytes after a block of size 8 alloc'd
==74210==    at 0x483F7B5: malloc (vg_replace_malloc.c:381)
==74210==    by 0x109294: main (split.c:36)
==74210== 
Hello
==74210== Invalid read of size 8
==74210==    at 0x1092D3: main (split.c:42)
==74210==  Address 0x4a3f048 is 0 bytes after a block of size 8 alloc'd
==74210==    at 0x483F7B5: malloc (vg_replace_malloc.c:381)
==74210==    by 0x109294: main (split.c:36)
==74210== 
world
this
is
elliot
==74210== 
==74210== HEAP SUMMARY:
==74210==     in use at exit: 4,152 bytes in 6 blocks
==74210==   total heap usage: 12 allocs, 6 frees, 6,312 bytes allocated
==74210== 
==74210== LEAK SUMMARY:
==74210==    definitely lost: 4,104 bytes in 5 blocks
==74210==    indirectly lost: 48 bytes in 1 blocks
==74210==      possibly lost: 0 bytes in 0 blocks
==74210==    still reachable: 0 bytes in 0 blocks
==74210==         suppressed: 0 bytes in 0 blocks
==74210== Rerun with --leak-check=full to see details of leaked memory
==74210== 
==74210== ERROR SUMMARY: 12 errors from 3 contexts (suppressed: 0 from 0)
==74210== 
==74210== 4 errors in context 1 of 3:
==74210== Invalid read of size 8
==74210==    at 0x1092D3: main (split.c:42)
==74210==  Address 0x4a3f048 is 0 bytes after a block of size 8 alloc'd
==74210==    at 0x483F7B5: malloc (vg_replace_malloc.c:381)
==74210==    by 0x109294: main (split.c:36)
==74210== 
==74210== 
==74210== 4 errors in context 2 of 3:
==74210== Invalid read of size 8
==74210==    at 0x1091E7: split_text (split.c:24)
==74210==    by 0x1092B2: main (split.c:38)
==74210==  Address 0x4a3f048 is 0 bytes after a block of size 8 alloc'd
==74210==    at 0x483F7B5: malloc (vg_replace_malloc.c:381)
==74210==    by 0x109294: main (split.c:36)
==74210== 
==74210== 
==74210== 4 errors in context 3 of 3:
==74210== Invalid write of size 8
==74210==    at 0x1091D0: split_text (split.c:23)
==74210==    by 0x1092B2: main (split.c:38)
==74210==  Address 0x4a3f048 is 0 bytes after a block of size 8 alloc'd
==74210==    at 0x483F7B5: malloc (vg_replace_malloc.c:381)
==74210==    by 0x109294: main (split.c:36)
==74210== 
==74210== ERROR SUMMARY: 12 errors from 3 contexts (suppressed: 0 from 0)

错误原因

  • 参数传递逻辑错误:split_text需要修改main中指针数组的内存地址(realloc可能会迁移内存块),但当前参数是char** res,传值调用导致函数内的*res = realloc(...)操作实际修改的是指针数组的第一个元素,而非指针数组本身的地址。
  • 内存越界访问:main中初始只给指针数组分配了1个char*的空间,函数内未正确扩容指针数组,后续访问res[x](x≥1)时超出了初始分配的内存范围,触发非法读写。
  • 内存泄漏:未释放分配的子字符串内存和指针数组内存,导致程序退出时内存未回收。

修复方案

修正后的代码

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

int split_text(char *text, char *token, char ***res)
{
    int x = 0;
    // 初始化指针数组,初始容量设为1
    *res = malloc(sizeof(char*));
    if (*res == NULL) {
        return -1;
    }
    char *tmp = strtok(text, token);
    while (tmp != NULL)
    {
        // 扩容指针数组:先分配新空间,避免realloc失败丢失原数据
        char **temp_ptr = realloc(*res, sizeof(char*) * (x + 1));
        if (temp_ptr == NULL) {
            // 内存分配失败,清理已分配的内存
            for (int i = 0; i < x; i++) {
                free((*res)[i]);
            }
            free(*res);
            return -1;
        }
        *res = temp_ptr;
        // 按实际长度分配子字符串内存,减少浪费
        (*res)[x] = malloc(strlen(tmp) + 1);
        if ((*res)[x] == NULL) {
            // 内存分配失败,清理已分配的内存
            for (int i = 0; i < x; i++) {
                free((*res)[i]);
            }
            free(*res);
            return -1;
        }
        strcpy((*res)[x], tmp);
        x++;
        tmp = strtok(NULL, token);
    }
    // 扩容添加NULL标记,方便后续遍历
    char **temp_ptr = realloc(*res, sizeof(char*) * (x + 1));
    if (temp_ptr != NULL) {
        *res = temp_ptr;
        (*res)[x] = NULL;
    }
    return x;
}

int main()
{
    char text[] = "Hello world this is elliot";
    char **ptr = NULL;

    int count = split_text(text, " ", &ptr);
    if (count == -1) {
        fprintf(stderr, "内存分配失败\n");
        return 1;
    }

    for (int i = 0; i < count; i++) {
        printf("%s\n", ptr[i]);
    }

    // 释放所有分配的内存,避免泄漏
    for (int i = 0; i < count; i++) {
        free(ptr[i]);
    }
    free(ptr);

    return 0;
}

关键修改点

  • 参数类型调整:将split_text的第三个参数改为char*** res,传入指针数组的地址(&ptr),确保函数内可以修改main中的ptr变量。
  • 正确扩容指针数组:使用*res指向指针数组,realloc操作针对*res进行,确保指针数组的地址被正确更新。
  • 内存安全检查:增加realloc和malloc的失败检查,避免内存分配失败导致的崩溃或泄漏,并在失败时清理已分配的内存。
  • 优化内存使用:子字符串内存按实际长度分配(strlen(tmp)+1),而非固定1024字节,减少内存浪费。
  • 内存释放逻辑:在main中添加内存释放步骤,回收所有分配的堆内存,消除内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 15:45:30