Макет двумерной пирамиды Malloc в C

T. Bone спросил: 28 апреля 2018 в 09:51 в: c

У меня возникла проблема с созданием 2D-массива, где каждая "строка" будет представлять собой разный массив размеров, с целым числом (2 строки) +1, начиная с строки = 0.

То, что меня беспокоит что код работает на некоторые небольшие входы (1,2,3), но затем падает на 4, если я попытаюсь освободить структуру после печати или на 5, если я не освобожу память.

typedef struct{
    int nrow;
    int** numbers;
} pyramid;pyramid* create_pyramid(int nrow){
    //Allocate memory for pyramid
    pyramid *p = (pyramid *) malloc (sizeof(pyramid));
    if(p == NULL) exit(1);    p->nrow = nrow;    //Allocate memory for numbers
    p->numbers = (int**) malloc (sizeof(int*));
    if(p->numbers == NULL) exit(1);    //Alocate memory for numbers[i]
    int i, nums;
    for(i = 0; i < nrow; i++){
        nums = (i*2) + 1;        p->numbers[i] = (int*) malloc (nums * sizeof(int));
        if(p->numbers[i] == NULL) exit(1);
    }    return p;
}void print_pyramid(pyramid *p){
    int i, j, nums, spaces;
    for(i = 0; i < p->nrow; i++){
        nums = (2*i)+1;
        spaces = p->nrow - (i+1);
        for(j = 0; j < spaces; j++) printf(" ");
        for(j = 0; j < nums; j++){
            printf("%d",p->numbers[i][j]);
        }
        printf("\n");
    }
}void free_pyramid(pyramid *p){
    int i;
    for(i = 0; i < p->nrow; i++){
        free(p->numbers[i]);
    }
    free(p->numbers);
    free(p);
}int main(int argc, char *argv[]){
    if(argc<2) exit(1);    int nrow; sscanf(argv[1], "%d", &nrow);
    pyramid *p = create_pyramid(nrow);
    print_pyramid(p);
    free_pyramid(p);
    exit(0);
}

Это то, что я получаю при запуске этого кода с аргументами командной строки 0-4:

userT@userT-VirtualBox:~/Desktop/pyr$ ./a.out 1
0
userT@userT-VirtualBox:~/Desktop/pyr$ ./a.out 2
 0
000
userT@userT-VirtualBox:~/Desktop/pyr$ ./a.out 3
  0
 000
00000
userT@userT-VirtualBox:~/Desktop/pyr$ ./a.out 4
   0
  000
 00000
0000000
*** Error in `./a.out': double free or corruption (out): 0x0000000001441050 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fcc5a32d7e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x7fe0a)[0x7fcc5a335e0a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7fcc5a33998c]
./a.out[0x400887]
./a.out[0x40092a]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fcc5a2d6830]
./a.out[0x4005e9]
======= Memory map: ========
00400000-00401000 r-xp 00000000 08:01 38534                              /home/userT/Desktop/pyr/a.out
00600000-00601000 r--p 00000000 08:01 38534                              /home/userT/Desktop/pyr/a.out
00601000-00602000 rw-p 00001000 08:01 38534                              /home/userT/Desktop/pyr/a.out
01441000-01462000 rw-p 00000000 00:00 0                                  [heap]
7fcc54000000-7fcc54021000 rw-p 00000000 00:00 0 
7fcc54021000-7fcc58000000 ---p 00000000 00:00 0 
7fcc5a0a0000-7fcc5a0b6000 r-xp 00000000 08:01 7136                       /lib/x86_64-linux-gnu/libgcc_s.so.1
7fcc5a0b6000-7fcc5a2b5000 ---p 00016000 08:01 7136                       /lib/x86_64-linux-gnu/libgcc_s.so.1
7fcc5a2b5000-7fcc5a2b6000 rw-p 00015000 08:01 7136                       /lib/x86_64-linux-gnu/libgcc_s.so.1
7fcc5a2b6000-7fcc5a475000 r-xp 00000000 08:01 7098                       /lib/x86_64-linux-gnu/libc-2.23.so
7fcc5a475000-7fcc5a675000 ---p 001bf000 08:01 7098                       /lib/x86_64-linux-gnu/libc-2.23.so
7fcc5a675000-7fcc5a679000 r--p 001bf000 08:01 7098                       /lib/x86_64-linux-gnu/libc-2.23.so
7fcc5a679000-7fcc5a67b000 rw-p 001c3000 08:01 7098                       /lib/x86_64-linux-gnu/libc-2.23.so
7fcc5a67b000-7fcc5a67f000 rw-p 00000000 00:00 0 
7fcc5a67f000-7fcc5a6a5000 r-xp 00000000 08:01 7070                       /lib/x86_64-linux-gnu/ld-2.23.so
7fcc5a887000-7fcc5a88a000 rw-p 00000000 00:00 0 
7fcc5a8a1000-7fcc5a8a4000 rw-p 00000000 00:00 0 
7fcc5a8a4000-7fcc5a8a5000 r--p 00025000 08:01 7070                       /lib/x86_64-linux-gnu/ld-2.23.so
7fcc5a8a5000-7fcc5a8a6000 rw-p 00026000 08:01 7070                       /lib/x86_64-linux-gnu/ld-2.23.so
7fcc5a8a6000-7fcc5a8a7000 rw-p 00000000 00:00 0 
7ffc634cc000-7ffc634ed000 rw-p 00000000 00:00 0                          [stack]
7ffc635d6000-7ffc635d8000 r--p 00000000 00:00 0                          [vvar]
7ffc635d8000-7ffc635da000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted (core dumped)

И это, если я получу, если не вызову Функция free_pyramid в конце main:

userT@userT-VirtualBox:~/Desktop/pyr$ ./a.out
userT@userT-VirtualBox:~/Desktop/pyr$ ./a.out 1
0
userT@userT-VirtualBox:~/Desktop/pyr$ ./a.out 2
 0
000
userT@userT-VirtualBox:~/Desktop/pyr$ ./a.out 3
  0
 000
00000
userT@userT-VirtualBox:~/Desktop/pyr$ ./a.out 4
   0
  000
 00000
0000000
userT@userT-VirtualBox:~/Desktop/pyr$ ./a.out 5
    39268576
   000
  00000
 0000000
000000000

3 ответа

Есть решение
ktalik ответил: 28 апреля 2018 в 10:42

Как и в

p->numbers[i] = (int*) malloc (nums * sizeof(int));

, вам нужно выделить память для массива переменных, а не только одну переменную. Итак, эта строка:

p->numbers = (int**) malloc (sizeof(int*));

должна быть

p->numbers = (int**) malloc (nrow * sizeof(int*));

В обоих случаях (int*) и (int**) не нужны.

ryyker ответил: 29 апреля 2018 в 10:43

Механизм правильного распределения памяти уже рассмотрен, но в отношении вашего вопроса:

То, что беспокоит меня, что код работайте на небольшие входы (1,2,3), но затем выходите на 4 , если я попытаюсь освободить структуру после печати или на 5, если я не освобожу память.

Даже если вы еще не владеете им, ваша программа может работать нормально, когда вы пытаетесь записать ее в папку, которая не была должным образом распределена. Это называется неопределенным поведением (a) . Похоже, что ваша программа работает нормально, а затем внезапно, без видимых причин, это не так.

Позже, относительно освобождения памяти, оператор free только отмечает память местоположения , чтобы позволить операционной системе использовать, когда она им нужна. Он не сразу очищает содержимое этой памяти. Таким образом, если вы попытаетесь получить доступ к этой же ячейке памяти после освобождения, и она еще не использовалась операционной системой для чего-то еще, у вас может получиться успех при просмотре контента. Это снова является неопределенным поведением (b) .

Опять же, реальный вопрос о том, как распределить память, был рассмотрен, но потому, что есть хорошее последовательное числовое соотношение столбцов к строк в вашей конструкции пирамиды, ваша функция create_pyramid может быть упрощена. (В этом примере не используется структура и заполняется каждой строкой n с помощью номера n.)

int ** create_pyramid(int r);int main(void)
{
    int r = 5;
    int **p = Create2D(r);
    for(int i=0;i<r;i++)
    {
        for(int j=0;j<(2*i)+1;j++)
        {
            p[i][j] = i; //populate with numeral representing row.
        }
    }
    return 0;
}int ** create_pyramid(int r)
{   
    int **arr;
    int c = 2*r-1;
    int    y = 0;    arr   = calloc(c, sizeof(int *));
    for(y=0;y<c;y++)
    {
        arr[y] = calloc((2*y)+1, sizeof(int));  
    }
    return arr;
}

Иллюстрация созданная память: (показывая только первые несколько строк , чтобы сохранить пространство)

Lundin ответил: 28 апреля 2018 в 10:06
p->numbers = (int**) malloc (sizeof(int*));

должен быть

p->numbers = malloc ( sizeof(int*[nrow]) );

Приведение в int** является излишним, и вам нужно выделить массив указателей, а не один указатель.

chux ответил: 28 апреля 2018 в 10:41
Неясно, что такое sizeof(int*[nrow]). Предложить p->numbers = malloc (sizeof p->numbers[0] * nrow);
Lundin ответил: 28 апреля 2018 в 11:07
@chux int* - это указатель на целое число. int* [n] - это тип, используемый для массива указателей n для целых чисел. sizeof() с круглой скобкой задает размер типа - здесь оценивается во время выполнения, так как передаваемый массив является VLA.
chux ответил: 28 апреля 2018 в 12:38
Интересно, формируя размер VLA, чтобы построить размер для выделения p->numbers. Этот подход был бы более применимым, если бы p->numbers был указателем на VLA.
Lundin ответил: 28 апреля 2018 в 12:41
@chux Наиболее формально правильный тип для p->numbers на самом деле был int(*)[n], так как он является указателем на массив. Но вместо этого OP указывает на первый элемент массива, который тоже хорошо и, вероятно, проще использовать на практике.