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

使用C.free释放C API分配内存致Go程序崩溃求助

问题:cgo调用MSVC编译的C库时,释放内存触发崩溃(错误码0xc0000374)

我通过Go的cgo调用MSVC编译的main.dll,C API返回的ApiResponse指针由C侧分配内存,但调用C.free释放时程序崩溃,崩溃日志显示exit status 0xc0000374。

相关代码如下:

main.go

package main

/*
#cgo CFLAGS: -g -Wall
#cgo CFLAGS: -I./
#cgo LDFLAGS: -L./ -lmain
#include <stdlib.h>
#include "main.h"
*/
import "C"
import (
    "fmt"
    "os"
    "unsafe"
)

func main() {

    var apiResponse *C.ApiResponse
    var instanceID *C.char

    property := C.CString(".\\properties")
    defer C.free(unsafe.Pointer(property))

    apiResponse = C.create_instance(property, &instanceID)

    id := C.GoString(instanceID)

    // 可以正常访问apiResponse的数据
    stat := int32(apiResponse.status)
    fmt.Println("status : ", stat)
    fmt.Println("response : ", C.GoString(apiResponse.response))
    fmt.Println("instanceID : ", id)

    fmt.Println("Before call to free apiResponse")
    // 调用free时崩溃
    C.free(unsafe.Pointer(apiResponse))
    fmt.Println("After call to free apiResponse")

    os.Exit(0)
}

main.c

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

void GetInstaceID(char ** instanceID)
{
    char * temp = NULL;
    
    if(instanceID) {
        
        temp = (char *) malloc(sizeof(char) * MAX_LEN_ID);
        if(temp){
            memset(temp, 0, MAX_LEN_ID);
            strncpy(temp, "ABC-XYX-1234", MAX_LEN_ID-1);
        }
        
        *instanceID = temp;
    }
}

ApiResponsePtr create_instance(char * property, char ** instanceID)
{

    ApiResponsePtr ptr = NULL;
    ptr = (ApiResponse *) calloc(1, sizeof(ApiResponse));

    // 为instanceID分配内存并填充内容
    GetInstaceID(instanceID);

    // 其他操作

    ptr->status = 0;

    
    ptr->response = (char *)malloc(MAX_STATUS_LEN * sizeof(char));
    if(ptr->response) {
        strncpy(ptr->response, "Success", MAX_STATUS_LEN-1); // 硬编码响应内容
    }

    return ptr;
}

main.h

#ifndef MAIN_H_
#define MAIN_H_

#define MAX_LEN_ID      30
#define MAX_STATUS_LEN  64

typedef struct api_response_t
{
    int     status;         /*!< \details API状态码 */
    char    * response;     /*!< \details API响应消息 */
} ApiResponse, *ApiResponsePtr;

ApiResponsePtr create_instance(char * property, char ** instanceID);

#endif

问题原因及解决方案

原因分析

错误码0xc0000374表示堆损坏,核心问题是内存分配与释放的CRT环境不一致:

  • Go的cgo默认使用MinGW的libc作为C运行时,而你用MSVC编译的main.dll使用的是MSVC的CRT。
  • 不同CRT实现的堆完全独立,用MinGW的free去释放MSVCmalloc/calloc分配的内存,会直接破坏MSVC的堆结构,触发崩溃。

解决方案

最稳妥的做法是让C库自身提供内存释放函数,因为只有分配内存的CRT才能正确管理和释放对应的内存。具体修改如下:

1. 修改main.h,添加释放函数声明

#ifndef MAIN_H_
#define MAIN_H_

#define MAX_LEN_ID      30
#define MAX_STATUS_LEN  64

typedef struct api_response_t
{
    int     status;         /*!< \details API状态码 */
    char    * response;     /*!< \details API响应消息 */
} ApiResponse, *ApiResponsePtr;

ApiResponsePtr create_instance(char * property, char ** instanceID);
// 新增:释放ApiResponse及关联内存的函数
void free_api_response(ApiResponsePtr ptr, char *instance_id);

#endif

2. 修改main.c,实现释放函数

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

// ... 保留原有GetInstaceID和create_instance函数 ...

// 实现释放函数:负责释放所有C侧分配的内存
void free_api_response(ApiResponsePtr ptr, char *instance_id)
{
    if (ptr != NULL) {
        free(ptr->response); // 先释放结构体内部的动态内存
        free(ptr);           // 再释放结构体本身
    }
    if (instance_id != NULL) {
        free(instance_id);   // 释放instanceID指向的内存
    }
}

3. 修改main.go,调用C库的释放函数

将原来的C.free(unsafe.Pointer(apiResponse))替换为调用C库提供的释放函数:

// ... 保留原有代码 ...

fmt.Println("Before call to free apiResponse")
// 调用C库的释放函数,而非直接用C.free
C.free_api_response(apiResponse, instanceID)
fmt.Println("After call to free apiResponse")

os.Exit(0)

补充说明

如果一定要手动释放,也可以统一CRT环境:

  • 要么用MinGW编译你的C库,和Go的cgo默认CRT保持一致;
  • 要么使用Go的windows/amd64-msvc版本编译Go代码,该版本会使用MSVC的CRT,和你的DLL环境匹配。

但相比之下,让C库提供释放函数的做法兼容性更好,不受编译环境限制。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 12:20:32