Пытаюсь написать динамический массив со строками произвольной длинны, выдает следующую ошибку: Segmentation fault (core dumped)

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

int main()
{
    int** array2;
    int* length;
    int rows;
    printf("put number of rows\n");
    scanf("%d", &rows);
    array2 = calloc(rows, sizeof(int*));
    length = calloc(rows, sizeof(int));
    for (int i = 0; i < rows; i = i + 1)
    {
        printf("put length %d row\n", i);
        scanf("%d", &length[i]);
        for (int j = 0; j < length[i]; j = j + 1)
        {
            printf("put %d number in %d row\n", j + 1, i + 1);
            scanf("%d", &array2[i][j]);
        }
    }
    printf("\n");
    for (int i = 0; i < rows; i = i + 1)
    {
        for (int j = 0; j < length[i]; j = j + 1)
        {
            printf("%d\t", array2[i][j]);
        }
        free(array2[i]);
        printf("\n");
    }
    free(array2);
    free(length);
}

Не могу понять в чем тут ошибка, пытаюсь освоить динамическую память, а спросить некого


Ответы (1 шт):

Автор решения: Solt

Во-первых пропущено выделение памяти под каждую строку (очистка, кстати не забыта). А во-вторых предлагаю заменить calloc на malloc, зачем нам терять время на инициализацию нулями, если все равно предстоит заполнять данными.

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

А если они ещё и однотипные и с одинаковой длиной - позволяет в коде манипулировать адресами, а не работать с отдельными переменными.

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

int main(){
    int** array2;
    int* length;
    int rows;
    printf("put number of rows\n");
    scanf("%d", &rows);
    
    //Мы знаем размеры массивов, так зачем выделять две области?
    //Выделим одну и поделим на две части. И очищать будем одну.
    length = (int*)malloc(rows*sizeof(int)+rows*sizeof(int*));
    //Указатель на следующий после length элемент становится
    //указателем на array2
    array2 = (int**)&length[rows];
    //или так: array2 = (int**)(length+rows);
    //или так: array2 = (int**)((char*)length+rows*sizeof(int));

    for (int i = 0; i < rows; i++){
        printf("put length %d row\n", i);
        scanf("%d", &length[i]);
        array2[i] = (int*)malloc(length[i]*sizeof(int));
        for (int j = 0; j < length[i]; j = j + 1){
            printf("put %d number in %d row\n", j + 1, i + 1);
            scanf("%d", &array2[i][j]);
        }
    }
    printf("\n");
    for (int i = 0; i < rows; i = i + 1){
        for (int j = 0; j < length[i]; j = j + 1){
            printf("%d\t", array2[i][j]);
        }
        free(array2[i]);
        printf("\n");
    }
    //free(array2);
    free(length);
    return 0;
}

https://godbolt.org/z/PGax9z76W

Кроме того, если объемы данных небольшие, и вы уверены в том, что не переполните стек, можно для первых двух массивов выделить место прям в стеке, по месту так сказать:

int main(){
    int rows;
    printf("put number of rows\n");
    scanf("%d", &rows);

    int *array2[rows];
    int length[rows];
...

Выделение памяти в стеке намного быстрее и обращение тоже чуть-чуть быстрее. Кроме того, можно не освобождать выделенную память. Только надо помнить, что для функции main() - это выделение памяти на всё время работы программы и пользы опять таки не очень то и много, а для мелких функций, которые поработали и вернулись - самое то.

Можно пойти ещё дальше, использовать alloca(), динамически выделяющую память в стеке, но тут нужен некоторый опыт и понимание чем это грозит.

→ Ссылка