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

C++神经网络嵌套指针初始化后非确定性内存错误排查

C++神经网络4D数组初始化与梯度计算错误排查

问题背景

正在开发一个C++神经网络,需初始化一个4D数组,维度顺序为:神经元计算关键值(如线性函数、激活函数)→层内神经元数组→网络层数组→时间步数组。后续梯度计算阶段频繁出现内存访问错误,同时伴随低频的堆损坏问题。


初始化代码

外层4D数组初始化

double**** execution_results = new double*** [real_t_count];

for (size_t t = 0; t < real_t_count; t++)
{
    std::tuple<double***, double**> inference_execution_results = ExecuteStore(X[t]);
    execution_results[t] = std::get<0>(inference_execution_results);
}

NN::ExecuteStore(double*)方法实现

double*** execution_results = new double** [shape_length];

// 输入层未实例化,故设为NULL
execution_results[0] = NULL;
for (size_t i = 1; i < shape_length; i++)
{
    size_t layer_length = shape[i];
    ILayer* current_layer = layers[i - 1];
    current_layer_execution_results = execution_results[i] = new double* [layer_length];
    for (size_t j = 0; j < layer_length; j++)
    {
        INeuron* current_neuron = current_layer->neurons[j];
        current_layer_execution_results[j] = current_neuron->ExecuteStore(network_activations);
        
    }

}
return std::tuple<double***, double**>(execution_results, network_activations);

INeuron::ExecuteStore(double**)返回关键值数组,目前未发现该方法存在问题。


梯度计算错误代码

层反向遍历代码

for (int layer_i = shape_length - 1; layer_i >= 1; layer_i--)
{
    size_t layer_length = shape[layer_i];
    ILayer* current_layer = layers[layer_i - 1];
    if (layer_i == 2)
        int x = 0;
    for (size_t i = 0; i < layer_length; i++)
    {
        INeuron* current_neuron = current_layer->neurons[i]; // 次频发错误位置

        current_neuron->GetGradients(execution_results, real_t_count, gradients, network_costs, network_activations);
    }

INeuron::GetGradients方法错误位置

double linear_function_gradient = neuron_cost * Derivatives::DerivativeOf(execution_results[0], this->activation_function); // 最频发错误位置

错误信息

  • 最频发:抛出读取访问违规异常,execution_results值为0xFFFFFFFFFFFFFFFF
  • 低频错误:数组删除时出错、堆损坏检测(两台测试电脑均出现)

网络与层构造代码

测试环境中的网络构造

size_t image_resolution = 2;
size_t fake_image_count = 3;

double** fake_images = new double* [fake_image_count];
double** Y = new double*[fake_image_count];
// fake_images和Y已完成初始化

size_t shape_length = 7;
size_t* shape = new size_t[shape_length];
shape[0] = image_resolution * image_resolution;
/* 该配置下无错误:
shape[1] = 2;
shape[2] = 1;*/
shape[1] = image_resolution * image_resolution * fake_image_count;
shape[2] = (image_resolution * image_resolution * fake_image_count) / 1.2;
shape[3] = 128;
shape[4] = (image_resolution * image_resolution * fake_image_count) / 1.5;
shape[5] = fake_image_count;
shape[6] = 1;

ILayer** layers = new ILayer* [shape_length - 1];
for (size_t i = 1; i < shape_length; i++)
{
    layers[i - 1] = (ILayer*)new DenseNeuronLayer(shape, i, ActivationFunctions::Sigmoid);
}

NN* n = new NN(layers, shape, shape_length);

NN类构造函数

public:
    NN(ILayer** layers_not_including_input_layer, size_t* shape_including_input_layer, size_t shape_length)
    {
        this->layers = layers_not_including_input_layer;
        this->shape = shape_including_input_layer;

        this->shape_length = shape_length;
    }

DenseNeuronLayer类实现

class DenseNeuronLayer : public ILayer
{
public:
    DenseNeuronLayer(size_t* network_shape, size_t layer_i, ActivationFunctions::ActivationFunction activation_function)
    {
        this->layer_length = network_shape[layer_i];
        this->neurons = new INeuron*[layer_length];
        for (size_t i = 0; i < layer_length; i++)
        {
            DenseConnections* connections = new DenseConnections(layer_i, i, network_shape);
            Neuron* neuron = new Neuron(connections, 1, activation_function);
            this->neurons[i] = neuron;
        }
    }
};

已排查情况

尝试排查层实例化问题,发现获取current_neuron时会触发异常,但无法定位具体原因。


可能的原因与解决步骤

  1. 维度计算的整数截断问题

    • 问题:构造shape数组时使用浮点数除法(如/1.2、/1.5),赋值给size_t类型会直接截断小数部分,导致实际创建的神经元数量与预期不符,后续访问数组越界。
    • 解决:将浮点数除法改为带向上取整的整数运算,确保每层长度为正整数:
      shape[2] = static_cast<size_t>(ceil(static_cast<double>(image_resolution * image_resolution * fake_image_count) / 1.2));
      shape[4] = static_cast<size_t>(ceil(static_cast<double>(image_resolution * image_resolution * fake_image_count) / 1.5));
      
    • 验证:打印shape数组所有值,确认每层长度与实际创建的神经元数量一致。
  2. 4D数组维度访问顺序错误

    • 问题:定义的维度顺序是「关键值→神经元→层→时间步」,但代码中execution_results[0]实际访问的是时间步维度的第0个元素,而非关键值维度,导致访问错误内存地址。
    • 解决:修正访问顺序为execution_results[时间步][层][神经元][关键值],在GetGradients中先定位到当前时间步、层、神经元,再取关键值:
      // 需传入当前时间步t、层索引layer_idx、神经元索引neuron_idx
      double* key_values = execution_results[t][layer_idx][neuron_idx];
      double linear_function_gradient = neuron_cost * Derivatives::DerivativeOf(key_values[0], this->activation_function);
      
  3. 悬空指针与内存提前释放

    • 问题:execution_results或其子数组被提前释放,导致后续访问时出现悬空指针(0xFFFFFFFFFFFFFFFF通常是已释放堆内存的标记)。
    • 解决:
      • 检查所有delete/delete[]操作,确保梯度计算完成后再释放相关内存;
      • 使用std::unique_ptr、std::shared_ptr等智能指针替代裸指针,避免手动内存管理出错。
  4. 数组越界访问

    • 问题:遍历层或神经元时索引超出数组实际长度,破坏堆结构导致堆损坏。
    • 解决:
      • 在所有数组访问处添加边界检查,例如:
        if (i >= layer_length) { /* 打印日志或抛出异常,终止程序避免堆损坏 */ }
        
      • 使用调试工具(如Visual Studio内存检测、Valgrind)定位越界的具体位置。

内容的提问来源于stack exchange,提问作者Germán Gasset

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 11:57:11