некорректный многомерный массив в функции - PullRequest
2 голосов
/ 01 мая 2011

Я пытаюсь выделить 2d массив в C-программе. Он работает нормально в основной функции, как это (как объяснено здесь ):

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

int main(int argc, char ** argv)
{
    int ** grid;
    int i, nrows=10, ncols=10;
    grid = malloc( sizeof(int *) * nrows);

    if (grid == NULL){
        printf("ERROR: out of memory\n");
        return 1;
    }

    for (i=0;i<nrows;i++){
        grid[i] = malloc( sizeof(int) * ncols);
        if (grid[i] == NULL){
            printf("ERROR: out of memory\n");
            return 1;
        }
    }
    printf("Allocated!\n");

    grid[5][6] = 15;
    printf("%d\n", grid[5][6]);
    return 0;
}

Но так как мне приходилось делать это несколько раз с разными массивами, я пытался переместить код в отдельную функцию.

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

int malloc2d(int ** grid, int nrows, int ncols){
    int i;
    grid = malloc( sizeof(int *) * nrows);

    if (grid == NULL){
        printf("ERROR: out of memory\n");
        return 1;
    }

    for (i=0;i<nrows;i++){
        grid[i] = malloc( sizeof(int) * ncols);
        if (grid[i] == NULL){
            printf("ERROR: out of memory\n");
            return 1;
        }
    }
    printf("Allocated!\n");
    return 0;
}

int main(int argc, char ** argv)
{
    int ** grid;

    malloc2d(grid, 10, 10);
    grid[5][6] = 15;
    printf("%d\n", grid[5][6]);
    return 0;
}

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

Большое спасибо.

Ответы [ 7 ]

9 голосов
/ 01 мая 2011

Это не многомерный массив;это одномерный массив, содержащий указатели на одномерные массивы.Многомерные массивы не содержат указателей;они представляют собой отдельные блоки памяти.

Ваша проблема в том, что у вас есть указатель на указатель, и вы пытаетесь вернуть его из своей функции через параметр.Если вы собираетесь это сделать, вам понадобится указатель на указатель на указатель в качестве параметра, и вам придется передать адрес указателя на указатель на метод.Если вы этого не сделаете, вы не измените значение переменной grid в main - вы заменяете значение, скопированное в качестве параметра, на функцию malloc2d.Поскольку grid в main остается неинициализированным, вы получаете неопределенное поведение.

Вот пример того, что я имею в виду в качестве исправления:

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

int malloc2d(int *** grid, int nrows, int ncols){
    int i;
    *grid = malloc( sizeof(int *) * nrows);

    if (*grid == NULL){
        printf("ERROR: out of memory\n");
        return 1;
    }

    for (i=0;i<nrows;i++){
        (*grid)[i] = malloc( sizeof(int) * ncols);
        if ((*grid)[i] == NULL){
            printf("ERROR: out of memory\n");
            return 1;
        }
    }
    printf("Allocated!\n");
    return 0;
}

int main(int argc, char ** argv)
{
    int ** grid;

    malloc2d(&grid, 10, 10);
    grid[5][6] = 15;
    printf("%d\n", grid[5][6]);
    return 0;
}

Дополнительные примечания:

  • В случае сбоя одного выделения происходит утечка выделения для первого массива, а также выделения для всех предыдущих строк.Вам нужно вызвать free для тех, кто вернется, прежде чем вернуться.
  • Вы возвращаетесь через параметр, даже если вам это не нужно.Если бы я писал это, я бы сделал метод, возвращающий int **, и сообщал об ошибке, возвращая 0.
3 голосов
/ 01 мая 2011

Вот ваша фиксированная функция:

int malloc2d(int *** grid, int nrows, int ncols){
    int i;
    *grid = (int**)malloc( sizeof(int *) * nrows);

    if (*grid == NULL){
        printf("ERROR: out of memory\n");
        return 1;
    }

    for (i=0;i<nrows;i++){

        (*grid)[i] = (int*)malloc( sizeof(int) * ncols);
        if ((*grid)[i] == NULL){
            printf("ERROR: out of memory\n");
            return 1;
        }
    }
    printf("Allocated!\n");
    return 0;
}

int main(int argc, char ** argv)
{
    int ** grid;

    malloc2d(&grid, 10, 10);
    grid[5][6] = 15;
    printf("%d\n", grid[5][6]);
    return 0;
}

Обратите внимание, что функция теперь получает int***, и вы передаете адрес вашего int** функции.Затем функция разыменовывает int***, чтобы поместить в нее адрес выделенного блока памяти.

2 голосов
/ 01 мая 2011

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

void foo( int *temp )
{
     temp = malloc(sizeof(int)) ;
     // temp is assigned to point to new location but the actual variable
     // passed from main do not point to the location temp is pointing to.

     *temp = 10 ;
}

int main()
{
     int *ptr ;
     foo( ptr ) ;

     // ptr is still unintialized
     *ptr = 5 ; // Segmentation fault or Undefined behavior

     return 0;
}

Итак, вместо этого вы должны сделать -

void foo( int **temp )
{
    *temp = malloc(sizeof (int) );
    // ...
}

А теперь вызовите функцию как foo(&ptr); в функции main().

1 голос
/ 01 мая 2011

Если вы все еще хотите, чтобы malloc2d возвращал код состояния, параметр должен иметь тип int***:

int malloc2d(int *** grid, int nrows, int ncols){

И вам нужно использовать *grid для ссылки на предоставленный буфер:

    int i;
    *grid = malloc( sizeof(int *) * nrows);

    if (*grid == NULL){
        printf("ERROR: out of memory\n");
        return 1;
    }

    for (i=0;i<nrows;i++){
        (*grid)[i] = malloc( sizeof(int) * ncols);
        if ((*grid)[i] == NULL){
            printf("ERROR: out of memory\n");
            return 1;
        }
    }
    printf("Allocated!\n");
    return 0;
}

Затем при вызове malloc2d передайте ему адрес int** для заполнения:

int ** grid;

malloc2d(&grid, 10, 10);
0 голосов
/ 01 мая 2011

Короче должно быть ниже, со свободными (!) И инициализированными значениями для каждого элемента сетки:

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

#define NROWS 10
#define NCOLS 10

int main()
{
    int (* grid)[NCOLS] = calloc(NROWS,sizeof*grid);

    /* no more needed here malloc2d(grid, 10, 10); */
    grid[5][6] = 15;
    printf("%d\n", grid[5][6]);
    free(grid); /* every c/malloc need a free */
    return 0;
}
0 голосов
/ 01 мая 2011

Это не ваша проблема с ошибкой сегментации, но вы должны рассмотреть возможность использования одного вызова malloc для распределения всей необходимой памяти в сетке.

grid = malloc (nrows * ncols * sizeof(int *))

Посмотрите на ответ Bill ONeary относительно указателяуказатель указателя.

0 голосов
/ 01 мая 2011

C фактически всегда передается по значению, поэтому, когда вы передаете 'grid', вы передаете значение, и функция модифицирует свою собственную локальную копию.Попробуйте вместо этого ввести '& grid' и соответствующим образом изменить malloc2d.

...