Как правильно выделить место в двумерном массиве, избегая при этом повреждения памяти?

Для алгоритма я хотел выделять пространство для двумерного массива каждый раз, когда это было необходимо, вместо этого я получаю эту ошибку

`main.run: malloc.c:2406: sysmalloc: Assertion `(old_top ==   initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
Aborted`

Я пробовал выходы ведьм Valgrind:

==2903== Memcheck, a memory error detector
==2903== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==2903== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==2903== Command: ./main.run
==2903== 
==2903== Invalid write of size 4
==2903==    at 0x1087B7: main (main.c:18)
==2903==  Address 0x51d70e4 is 0 bytes after a block of size 4 alloc'd
==2903==    at 0x4C2BBAF: malloc (vg_replace_malloc.c:299)
==2903==    by 0x108787: main (main.c:17)
==2903== 

valgrind: m_mallocfree.c:303 (get_bszB_as_is): Assertion 'bszB_lo == bszB_hi' failed.
valgrind: Heap block lo/hi size mismatch: lo = 12, hi = 368578837618884608.
This is probably caused by your program erroneously writing past the
end of a heap block and corrupting heap metadata.  If you fix any
invalid writes reported by Memcheck, this assertion failure will
probably go away.  Please try that before reporting this as a bug.


host stacktrace:
==2903==    at 0x38083828: show_sched_status_wrk (m_libcassert.c:343)
==2903==    by 0x38083944: report_and_quit (m_libcassert.c:419)
==2903==    by 0x38083AD1: vgPlain_assert_fail (m_libcassert.c:485)
==2903==    by 0x38091882: get_bszB_as_is (m_mallocfree.c:301)
==2903==    by 0x38091882: get_bszB (m_mallocfree.c:311)
==2903==    by 0x38091882: vgPlain_arena_malloc (m_mallocfree.c:1734)
==2903==    by 0x3804FAD4: vgMemCheck_new_block (mc_malloc_wrappers.c:350)
==2903==    by 0x3804FCA6: vgMemCheck_malloc (mc_malloc_wrappers.c:385)
==2903==    by 0x380D7B53: do_client_request (scheduler.c:1866)
==2903==    by 0x380D7B53: vgPlain_scheduler (scheduler.c:1425)
==2903==    by 0x380E6416: thread_wrapper (syswrap-linux.c:103)
==2903==    by 0x380E6416: run_a_thread_NORETURN (syswrap-linux.c:156)

sched status:
  running_tid=1

Thread 1: status = VgTs_Runnable (lwpid 2903)
==2903==    at 0x4C2BBAF: malloc (vg_replace_malloc.c:299)
==2903==    by 0x108787: main (main.c:17)

Это код, который вызывает проблему:

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

#define cats 3
#define loops 30

int main() {
    int **a;
    int i,j;

    a = (int **)malloc(sizeof(int *));

    for (i = 0; i < cats; i++)
        for (j = 0; j < loops; j++) {
            a[i] = (int *)malloc(sizeof(int));
            a[i][j] = i + j;
        }

    for (i = 0; i < cats; i++) {
        for (j = 0; j < loops; j++)
            printf("%d ", a[i][j]);
        printf("\n");
    }

    return 0;
}

Чем вызвана ошибка и как ее избежать?

Является ли этот тип распределения памяти плохой практикой?

Проблема, как указывают другие, в том, что вы не спрашиваете «сколько?» так как вам нужны строки кошки элементов петли. Почему вы не задали себе этот вопрос?

Paul Ogilvie 07.04.2019 13:07

@PaulOgilvie этот код является примером, я забыл упомянуть в вопросе, что, в отличие от примера, я не могу знать, сколько места мне понадобится

Nick Gkloumpos 07.04.2019 13:09

Я с трудом могу тебе поверить... Тогда почему ты думаешь, что a[i][j] существует?

Paul Ogilvie 07.04.2019 13:12
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
124
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

несколько проблем в вашем коде

a = (int **)malloc(sizeof(int *));

должно быть

a = (int **)malloc(sizeof(int *)*cats); /* size for cats pointers rather than just 1 */

а также

for (i=0; i<cats; i++)
   for (j=0; j<loops; j++) {
       a[i] = (int *)malloc(sizeof(int));
       a[i][j] = i+j;
   }

должно быть

for (i=0; i<cats; i++) { /* '{' added */
    a[i] = (int *)malloc(sizeof(int) * loops); /* moved and loops int rather than 1 */
    for (j=0; j<loops; j++) {
        a[i][j] = i+j;
    }
} /* '}' added */

С этим исправлением компиляция и выполнение под валгринд :

pi@raspberrypi:/tmp $ gcc -g -pedantic -Wextra -Wall c.c
pi@raspberrypi:/tmp $ valgrind ./a.out
==5795== Memcheck, a memory error detector
==5795== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5795== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==5795== Command: ./a.out
==5795== 
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 
==5795== 
==5795== HEAP SUMMARY:
==5795==     in use at exit: 372 bytes in 4 blocks
==5795==   total heap usage: 5 allocs, 1 frees, 1,396 bytes allocated
==5795== 
==5795== LEAK SUMMARY:
==5795==    definitely lost: 12 bytes in 1 blocks
==5795==    indirectly lost: 360 bytes in 3 blocks
==5795==      possibly lost: 0 bytes in 0 blocks
==5795==    still reachable: 0 bytes in 0 blocks
==5795==         suppressed: 0 bytes in 0 blocks
==5795== Rerun with --leak-check=full to see details of leaked memory
==5795== 
==5795== For counts of detected and suppressed errors, rerun with: -v
==5795== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)
p

конечно у тебя утечки памяти

Привет бруно! Вы действительно запускаете свои образцы SO на Raspberry Pi? такой милый!

chqrlie 07.04.2019 12:52

@chqrlie да дома малиной почти все время сейчас пользуюсь, Делл запускаю очень редко. Я получил raspberry (и nano), чтобы производить для них BoUML по запросам пользователей. У малины есть все для работы, я использую на ней raspbian

bruno 07.04.2019 12:53

Следующим шагом будет использование встроенной ОС на плоском экране. Некоторые из них используют Linux только для экранного меню...

chqrlie 07.04.2019 12:57

a = (int **)malloc(sizeof(int *)); динамически выделяет место для указателя один и возвращает указатель на него. Вы не можете получить доступ к a[i] для любого i > 0, потому что он выйдет за пределы. Чтобы выделить место для нескольких указателей, вам нужно передать соответствующий размер в malloc.

Либо...

a = malloc(sizeof*a * cats);

... или...

a = malloc(sizeof (int*[cats]));

... Сделаю. В качестве альтернативы вы можете отказаться от зубчатого массива, поскольку все ваши экстенты равны, и разместить все это в одном блоке:

int (*a)[loops] = malloc(sizeof (int[cats][loops]));

Меньше выделений означает меньше шансов ошибиться.

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

Существуют разные способы размещения 2D-матрицы int:

  • Вы можете выделить массив массивов int.

  • Вы можете выделить массив указателей на массивы int и выделить разные массивы int для каждого из этих указателей. Это то, что вы пробовали, но размеры выделения неверны.

массив указателей на массивы int должен иметь размер cats * sizeof(int *). и каждый массив int должен быть размещен во внешнем цикле размером loops * sizeof(int), а не во внутреннем цикле, как вы это сделали.

Кроме того, вы должны освободить эти объекты перед выходом из программы, чтобы Valgrind мог видеть чистую пластину.

Вот исправленная версия:

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

#define cats 3
#define loops 30

int main() {
    int **a;
    int i, j;

    a = malloc(cats * sizeof(int *));

    for (i = 0; i < cats; i++) {
        a[i] = malloc(loops * sizeof(int));
        for (j = 0; j < loops; j++) {
            a[i][j] = i + j;
        }
    }

    for (i = 0; i < cats; i++) {
        for (j = 0; j < loops; j++)
            printf("%d ", a[i][j]);
        printf("\n");
    }

    for (i = 0; i < cats; i++)
        free(a[i]);
    free(a);

    return 0;
}

Этот стиль непрямая 2D-матрица обычно осуждается, потому что:

  • его сложнее и медленнее выделять, особенно если вы хотите избежать утечек памяти при сбое частичного выделения, чего здесь не делается.
  • доступ обычно менее эффективен, потому что компилятор генерирует 2 чтения памяти для чтения значения вместо потенциального умножения и одного чтения.
  • его сложнее освободить.

Есть некоторые преимущества, ни один из которых здесь не нужен:

  • можно не выделять некоторые строки
  • можно разделить одинаковые строки (за счет усложнения освобождения)
  • линии могут иметь разный размер, но это уже не матрица
  • линии могут быть заменены местами эффективно.

Другой подход, который считается единственным реальным 2D-матрица, использует одно выделение и несколько менее очевидный тип для указателя матрицы:

    int (*a)[loops] = malloc(sizeof(int) * loops * cats);

Что можно написать:

    int (*a)[loops] = malloc(sizeof(*a) * cats);

Или, возможно, более читаемо:

    int (*a)[loops] = malloc(sizeof(int[cats][loops]));

a указывает на массив cats массивов loopsint.

Такой подход был возможен только в ранних версиях C, если loops было константным выражением, как в вашей программе, но это ограничение было снято в C99.

Вот упрощенная версия вашей программы с таким подходом:

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

#define cats 3
#define loops 30

int main() {
    int (*a)[loops] = malloc(sizeof(*a) * loops);

    if (a != NULL) {
        for (i = 0; i < cats; i++) {
            for (j = 0; j < loops; j++)
                a[i][j] = i + j;
        }
        for (i = 0; i < cats; i++) {
            for (j = 0; j < loops; j++)
                printf("%d ", a[i][j]);
            printf("\n");
        }
        free(a);
    }
    return 0;
}

Забавно, мне любопытно, почему ОП принял ваш ответ, а не мой (сделано почти на 30 минут раньше), я разочарован, если честно ... так что нас, вероятно, двое ^^

bruno 09.04.2019 19:37

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