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

分析存在内存泄漏风险的C语言结构体字符串拷贝代码

分析结构体字符串拷贝代码的内存问题

先把原代码摆出来方便咱们逐个排查:

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<string.h>
typedef struct nNameStruct{
    char* nName;
    //char* nName2;
} nNameStruct;
nNameStruct * NameCreate(char *buffer) {
    nNameStruct *pNew;
    pNew = (nNameStruct*) malloc(sizeof(nNameStruct*));
    pNew->nName = (char*) malloc((strlen(buffer)+1)*sizeof(char*));
    strcpy(pNew->nName,buffer);
    //pNew->nNam...
}

1. 结构体内存分配的致命错误

先看给结构体分配内存的那行:

pNew = (nNameStruct*) malloc(sizeof(nNameStruct*));

这是C新手常犯的经典错误——你给malloc传的是结构体指针的大小(sizeof(nNameStruct*),32位平台是4字节,64位是8字节),但咱们需要的是整个结构体的大小(sizeof(nNameStruct))。

可能你会说,当前结构体里只有一个char*成员,sizeof(nNameStruct)刚好等于sizeof(nNameStruct*),好像没毛病?但这纯粹是巧合!一旦你把注释里的nName2放开,或者给结构体加其他成员,这个错误会立刻导致内存分配不足,触发堆越界访问,轻则程序崩溃,重则破坏堆结构,出现各种莫名其妙的bug,排查起来头都大。而且从代码规范来说,这绝对是错误的写法,必须改掉。

2. 字符串内存的浪费型错误

再看给nName分配内存的代码:

pNew->nName = (char*) malloc((strlen(buffer)+1)*sizeof(char*));

这里的问题是用sizeof(char*)来计算单个字符的内存大小,但实际上我们需要的是sizeof(char)——而根据C标准,sizeof(char)固定为1,所以甚至可以直接写strlen(buffer)+1就行。

char*的大小是4或8字节,这意味着你给每个字符分配了4/8倍的内存,完全是不必要的浪费。比如buffer是10个字符,本来只需要11字节(加终止符),现在却分配了44或88字节,要是处理大量长字符串,内存开销会被放大好几倍,太不划算。

3. 潜在的内存泄漏风险

这段代码没有配套的释放函数,要是调用者在使用完nNameStruct后,忘记先释放pNew->nName再释放pNew,就会导致内存泄漏。正确的释放逻辑应该封装成一个函数,比如:

void NameDestroy(nNameStruct *p) {
    if (p != NULL) {
        free(p->nName);
        free(p);
    }
}

每次调用NameCreate创建对象后,用完必须调用这个函数释放,不然内存会一直被占着,直到程序退出。

4. 容易被忽略的边界检查问题

还有两个容易被漏掉的检查:

  • 检查malloc返回值:如果内存不足,malloc会返回NULL,直接用pNew或pNew->nName会触发空指针崩溃。必须添加检查,而且如果nName分配失败,要先释放已经分配的结构体,避免泄漏。
  • 检查输入buffer是否为NULL:如果传入NULL,strlen(buffer)会触发未定义行为,程序直接崩掉,开头必须先判断。

修正后的代码示例

把上面的问题都修复后,代码应该是这样的:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct nNameStruct{
    char* nName;
    //char* nName2;
} nNameStruct;

nNameStruct * NameCreate(char *buffer) {
    // 先检查输入合法性
    if (buffer == NULL) {
        return NULL;
    }

    // 分配结构体内存,检查是否成功
    nNameStruct *pNew = malloc(sizeof(nNameStruct));
    if (pNew == NULL) {
        return NULL;
    }

    // 分配字符串内存,检查是否成功
    size_t str_len = strlen(buffer);
    pNew->nName = malloc(str_len + 1); // sizeof(char)=1,可省略
    if (pNew->nName == NULL) {
        free(pNew); // 回滚已分配的内存
        return NULL;
    }

    // 拷贝字符串
    strcpy(pNew->nName, buffer);
    return pNew;
}

// 配套的释放函数
void NameDestroy(nNameStruct *p) {
    if (p != NULL) {
        free(p->nName);
        free(p);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:24:06