Как правильно управлять динамически выделяемыми массивами указателей в C++?

В настоящее время я работаю над микропроцессором с небольшим объемом оперативной памяти (128 МБ). Я запускаю несколько потоков выполнения для анализа производительности, но суть проблемы заключается в хранении больших объемов данных (поплавков и целых чисел) в динамическом пространстве, чтобы уменьшить возможность копирования/распределения данных, к которым склонны такие структуры данных, как векторы. полагаться в исполнении.

Я решил протестировать несколько возможных схем, как решить эту проблему в CLion. Я написал следующий код:

#include <cstdlib>
#include <cstdio>


int initial_capacity = 3;
int current_capacity = 3;
int current_count  = 0;

int*** initializeArray(){
    // initialize triple array space in memory
    int*** arr;
    /*
       3 items -> 3 data sets -> each data set starts with 3 spaces
       a "int arr[3][3][3]" but dynamically allocated
    */
    arr = (int***) calloc(current_capacity, sizeof(int**));
    for(int i = 0; i < current_capacity; i++){
        arr[i] = (int**) calloc(current_capacity, sizeof(int*));
        for(int j =0; j < current_capacity; j++){
            arr[i][j] = (int*) calloc(current_capacity,sizeof(int));
        }
    }
    return arr;
}


void resizeArray(int *** arr){
    auto max = (double) current_capacity;
    double percentage = current_count/max;
    if (percentage >= 0.5){
        for (int i = 0; i < initial_capacity; ++i) {
            for (int j = 0; j < initial_capacity; ++j) {
                current_capacity*=2;
                arr[i][j] = (int*) realloc(arr[i][j],(current_capacity)*sizeof(int));
            }
        }
    }
}

void deleteArrays(int *** arr){
    for (int i = 0; i < initial_capacity; ++i) {
        for (int j = 0; j < initial_capacity; ++j) {
            for (int k = 0; k < current_count; ++k) {
                arr[i][j][k] = NULL;
            }

        }
    }
    printf("Releasing allocated arrays 1\n");

    for (int i = 0; i < initial_capacity; ++i) {
        for (int j = 0; j < initial_capacity; ++j) {
            arr[i][j] = (int*) realloc(arr[i][j],sizeof(int));
            free(arr[i][j]);
        }
    }
    printf("Releasing allocated arrays 2\n");

    for (int i = 0;  i < initial_capacity; ++i) {
        free(arr[i]);
    }
    printf("Releasing allocated arrays 3\n");

    free(arr);
}


void printArrays(int *** arr){
    for (int i = 0; i < 3; ++i) {
        printf("Array[%d]\n", i);
        for (int j = 0; j < 3; ++j) {
            printf("Array[%d][%d]\nElements: ", i, j);
            for (int k = 0; k < current_count; ++k) {
                printf(" %d", arr[i][j][k]);
            }
            printf("\n");
        }
    }
    printf("\n");
}

int main() {
    int *** generated= initializeArray();
    int count  = 0;
    while (count < 21){
        if (count % 3 == 0) printArrays(generated);
        //verify
        resizeArray(generated);
        generated[0][0][current_count] = rand();
        generated[0][1][current_count] = rand();
        generated[0][2][current_count] = rand();
        generated[1][0][current_count] = rand();
        generated[1][1][current_count] = rand();
        generated[1][2][current_count] = rand();
        generated[2][0][current_count] = rand();
        generated[2][1][current_count] = rand();
        generated[2][2][current_count] = rand();

        current_count++;
        count++;
    }
    /* some operation with data collected */
    deleteArrays(generated);
    printf("Finished deleting dynamic arrays");
    return 0;
}

Я попытался сгенерировать тройной массив, чтобы сохранить тот же объем информации в последнем подмножестве, в то время как его размер и общая сумма продолжали увеличиваться. Однако процедура удаления массива для завершения динамического аспекта процесса всегда приводит к завершению процесса с кодом выхода -1073740940 (0xC0000374), что является ошибкой повреждения кучи. Я не очень хорошо знаком с этим, и любой отзыв об этом был бы полезен.

Вы пытались добавить операторы отладки для печати необработанных указателей каждого выделенного/перераспределенного/удаленного массива? Исходя из этого, вы должны быть в состоянии обнаружить любые плохие удаления. Если все в порядке, используйте инструмент статического анализа памяти, чтобы найти повреждение памяти.

Sam Varshavchik 22.12.2020 13:17

Не спрашивайте одновременно о C и C++, за исключением вопросов, касающихся различий или взаимодействия между языками. Поскольку в коде есть операторы #include, которые не будут работать в C, я удаляю тег C.

Eric Postpischil 22.12.2020 13:20

@SamVarshavchik Я просмотрел некоторые возможные операторы, но обычно мне удавалось использовать только те, которые относятся к первому элементу адреса, и во всех случаях он возвращает допустимый элемент. Не уверен, как проверить в остальных выделенных пространствах

AICS2000 22.12.2020 13:24

Действительно ли необходимо иметь трехмерный массив с тремя разными вызовами calloc? Есть ли веская причина не использовать только одномерный массив, а все остальное делать со сдвигом индекса? Иногда наличие одного большого блока распределения значительно облегчает вашу жизнь. Также вы можете воспользоваться некоторыми преимуществами локальности кеша, в зависимости от микропроцессора :)

hakononakani 22.12.2020 13:27

@hakononakani Это концепция, о которой я не знал. Обычно я управлял только n-массивами в итеративной последовательности. Я посмотрю на это!

AICS2000 22.12.2020 13:38
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
5
117
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Подпрограмма resizeArray удваивает current_capacity с current_capacity*=2; на каждой итерации своего внутреннего цикла. При первом вызове это приводит к тому, что arr[0][0] устанавливается на память для 6 int, в то время как циклы продолжаются и в конечном итоге оставляют current_capacity равным 1536. Для остальной части выполнения программы resizeArray никогда не выделяет больше памяти из-за того, что что его порог относительно current_capacity никогда не достигается.

Тем временем подпрограмма main продолжает записывать все больше и больше элементов в generated[0][0], увеличивая current_count до 20 и, следовательно, записывая за пределы выделенной памяти.

Эту ошибку можно исправить, переместив current_capacity*=2; из циклов внутрь блока «тогда» if (percentage >= 0.5).

Также обратите внимание, что метод реализации многомерных массивов в виде указателей на указатели или указателей на указатели на указатели неэффективен во времени и пространстве. «Погоня за указателем» плоха для спекулятивного выполнения инструкций процессором. Для многомерных массивов фиксированного размера это не используется в коде производственного качества. В этом случае, когда требуется переменное последнее измерение, выбор немного менее очевиден, но все же может быть предпочтительнее использовать одно непрерывное выделение памяти и использовать индексную арифметику для вычисления местоположений в массиве, а не поиск по указателю. (C делает это довольно просто с помощью массивов переменной длины, хотя их поддержка необязательна. Реализации C++ могут предоставлять массивы переменной длины в качестве расширения, но необходимые арифметические действия несложны и могут быть выполнены с помощью вспомогательных функций или классов.)

Другие вопросы по теме