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

如何从C++释放Ada堆分配Tagged Record?遇双释放错误

从C++释放Ada堆分配Tagged Record时的double free错误解决方案

问题概述

运行代码时触发以下错误:

20
double free or corruption (out)

raised PROGRAM_ERROR : unhandled signal

目标是构建插件架构,以Ada动态库作为特定类型工厂(类似boost.dll),需解决跨语言释放Ada堆分配Tagged Record的问题,确认是否必须依赖Ada的释放机制。

代码示例

main.cpp

#include <iostream>
#include "animal.h"

extern "C" {
    void adainit (void);
    void adafinal (void);
    Animal* new_animal();
    void del_animal(Animal *);
}

int main(void) {
    adainit();
    Animal* A = new_animal();
    std::cout << A->age() << std::endl;
    //delete A;   
    del_animal(A);
    adafinal();
    return 0;
};

alib.ads

with Interfaces.C;

package ALib is

    type Animal is tagged record
        The_Age : Interfaces.C.int;
    end record;
    pragma Convention (CPP, Animal);

    type Animal_Class_Access is access Animal'Class;

    function New_Animal return access Animal'Class;
    pragma Export(CPP, New_Animal);

    procedure Del_Animal (this : in out Animal_Class_Access);
    pragma Export(CPP, Del_Animal);

    function Age(X : Animal) return Interfaces.C.int;
    pragma Export(CPP, Age);

end ALib;

alib.adb

with ada.unchecked_deallocation;

package body ALib is

    function New_Animal
        return access Animal'Class is
    begin
        return new Animal'(The_Age => 20);
    end New_Animal;

    procedure Del_Animal (this : in out Animal_Class_Access) is
        procedure Free is new ada.unchecked_deallocation(Animal'Class, Animal_Class_Access);
    begin
        Free(this);
        --null;
    end Del_Animal;

    function Age(X : Animal)
        return Interfaces.C.int is
    begin
        return X.The_Age;
    end Age;

end ALib;

已尝试操作

  • 为创建Free过程测试多种类型与访问类型组合:
    • Animal + type Animal_Access is access Animal
    • Animal'Class + type Animal_Class_Access is access Animal'Class
    • Animal + type Animal_Access is access Animal'Class
  • 考虑使用System.Address作为New_Animal返回值和Del_Animal参数

解决方案

必须使用Ada的内存释放机制,核心原因是Ada与C的内存分配器独立,且Ada类访问类型(Animal'Class)的内存布局与C对象不兼容,跨语言直接释放必然触发内存错误。

具体修改步骤

  1. 修正Ada端接口类型匹配
    将Ada的访问类型改为与C++指针兼容的类型,显式指定导出名称避免命名规则冲突:

    with Interfaces.C;
    
    package ALib is
    
        type Animal is tagged record
            The_Age : Interfaces.C.int;
        end record;
        pragma Convention (CPP, Animal);
    
        type Animal_Access is access Animal;
        pragma Convention (CPP, Animal_Access); -- 确保与C++指针布局兼容
    
        function New_Animal return Animal_Access;
        pragma Export(CPP, New_Animal, "new_animal");
    
        procedure Del_Animal (This : Animal_Access);
        pragma Export(CPP, Del_Animal, "del_animal");
    
        function Age(X : Animal) return Interfaces.C.int;
        pragma Export(CPP, Age, "age");
    
    end ALib;
    
  2. 修正Ada端释放逻辑
    使用匹配的访问类型创建Unchecked_Deallocation过程,确保内存释放正确:

    with Ada.Unchecked_Deallocation;
    
    package body ALib is
    
        function New_Animal return Animal_Access is
        begin
            return new Animal'(The_Age => 20);
        end New_Animal;
    
        procedure Free_Animal is new Ada.Unchecked_Deallocation(Animal, Animal_Access);
    
        procedure Del_Animal (This : Animal_Access) is
            Temp : Animal_Access := This;
        begin
            Free_Animal(Temp);
        end Del_Animal;
    
        function Age(X : Animal) return Interfaces.C.int is
        begin
            return X.The_Age;
        end Age;
    
    end ALib;
    
  3. C++端保持接口一致性
    确保animal.h的声明与Ada导出接口匹配:

    class Animal {
    public:
        int age() {
            // 对应Ada导出的age函数
            extern "C" int age(Animal&);
            return age(*this);
        }
    };
    

插件架构最佳实践

  • 所有Ada堆分配对象,必须通过Ada导出的释放函数清理,禁止在C++中直接操作内存。
  • 多态插件类型建议用System.Address传递句柄,在Ada内部转换为类访问类型,兼顾类型安全与跨语言兼容性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 04:01:25