Как правильно передать указатель на функцию для удаления? - PullRequest
2 голосов
/ 19 августа 2011

У меня есть матрица, объявленная как int **matrix, и я знаю, что правильный способ передачи ее функции для выделения памяти должен быть следующим:

void AllocMat(int ***mat, int size);

Но теперь мне нужно удалить эту память в другой функции, и я не уверен, что передать:

void DeallocMat(int **mat, int size);

или

void DeallocMat(int ***mat, int size);

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

Ответы [ 7 ]

4 голосов
/ 19 августа 2011

Сначала правильно.Но ваша настоящая проблема в том, что вы используете указатели, когда есть лучшие альтернативы.Для двумерной матрицы вы должны использовать вектор векторов

#include <vector>

typedef std::vector<std::vector<int> > Matrix;

Matix m;

Теперь нет необходимости что-либо удалять, так что еще одна вещь не так.

4 голосов
/ 19 августа 2011

Вопрос помечен C ++, но в ответах используется только подмножество C ...

Ну, во-первых, я бы рекомендовал против всего этого.Создайте класс, который инкапсулирует вашу матрицу, и разместите его в одном блоке, предложите operator()(int,int), чтобы получить доступ к элементам ...

Но вернемся к проблеме.В C ++ вы должны использовать ссылки, а не указатели, чтобы позволить функции изменять аргумент, поэтому ваша оригинальная сигнатура размещения должна быть:

void AllocMat(int **&mat, int size);

И вызывать ее следующим образом:

int **matrix = 0;
AllocMat( matrix, 5 );

Илилучше просто вернуть указатель:

int **AllocMat( int size );
int **matrix = AllocMat( 5 );

Для функции освобождения, так как вам не нужно изменять внешний указатель, вы можете просто использовать:

void DeallocMat( int**mat, int size ); // size might be required to release the 
                                       // internal pointers

Теперь для эскиза решения C ++:

template <typename T>                   // no need to limit this to int
class square_matrix {
   const unsigned size;
   T * data;
public:
   square_matrix( unsigned size ) : size(size), data( new T[size*size]() ) {}
   square_matrix( matrix const & m ) : size( m.size ), data( new T[m.size*m.size] ) {
      std::copy( m.data, m.data+size*size, data );
   }
   ~matrix() {
      delete [] data;
   }
   T const & operator()( unsigned x, unsigned y ) const {
      // optional range check and throw exception
      return data[ x + y*size ];
   }
   void set( unsigned x, unsigned y, T const & value ) {
      // optional range check and throw exception
      data[ x + y*size ] = value;
   }
};
3 голосов
/ 19 августа 2011

void DeallocMat(int **mat, int size) - позволяет освободить память (поскольку вы передали значение mat, позволяющее только освободить память, но не изменять mat)

void DeallocMat(int ***mat, int size) - позволяет освободить памятьи измените значение mat на NULL (так как теперь вы передали указатель на mat, позволяющий изменить его значение)

1 голос
/ 19 августа 2011

Дополнительный «*» просто обрабатывает указатель, который будет вести себя как вызов по ссылке .Если вы хотите получить вывод из вашей функции, вам нужно добавить «*» в вашем объявлении.В этом случае вы должны передать ссылку вашего указателя (используя &) на эти функции.

0 голосов
/ 19 августа 2011

Что меня смущает в вашем вопросе, так это то, что большинство людей не объявляют матрицу int **. Причина этого заключается в том, что вы будете вынуждены распределить его в цикле. Для вашей функции размещения потребуются два параметра, которые являются размерами массива следующим образом:

void AllocMat(int *** mat, int n, int m) {
    int ** result = new int * [ n ];
    for (int x=0; x<n; x++) {
        result[x] = new int [ m ];
    }
    *mat = result;
}

Если бы это было так, соответствующая функция освобождения потребовала бы знания размера n следующим образом:

void DeallocMat(int *** mat, int n) {
    if (mat == NULL || *mat == NULL) return;
    int ** tmp = *mat;
    for (int x=0; x<n; x++) {
        if (tmp[x] != NULL) delete [] tmp[x];
    }
    delete [] tmp;
    *mat = NULL;
}

При таком подходе вы можете получить доступ к своей матрице следующим образом:

int ** mat = NULL;
AllocMat(&mat, n, m);

for (int x=0; x<n; x++) {
    for (int y=0; y<m; y++) {
        mat[x][y] = 1;
    }
}

DeallocMat(&mat, n);

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

void AllocMat2(int ** mat, int n, int m) {
        *mat = new int [ n * m ];
}

И соответствующая функция освобождения, как это:

void DeallocMat2(int ** mat) {
    if (mat != NULL && *mat != NULL) {
        delete [] *mat;
        *mat = NULL;
    }
}

И вы получите к нему следующее:

int * mat2 = NULL;
AllocMat2(&mat2, n, m);

for (int x=0; x<n; x++) {
    for (int y=0; y<m; y++) {
        mat2[x * n + y] = 1;
    }
}

DeallocMat2(&mat2);
0 голосов
/ 19 августа 2011

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

void Foo(int * a)
{
   a = new int[10];
}
int main()
{
   int *a = 0;
   Foo( a );
}

Теперь память будет выделена, но указатель A не будетобновить, потому что значение указателя A просто копируется в другую переменную указателя, которая является параметром Foo.Как только Foo возвращается, a останется 0. Чтобы отразить это, вы должны написать код, подобный следующему

void Foo(int ** a)
{
   *a = new int[10];
}
int main()
{
   int *a = 0;
   Foo( &a );
}

Здесь вы передаете адрес указателя.Это означает, что значение, содержащееся в указателе, будет обновлено из функции Foo. Вы можете выполнить отладку и посмотреть, как это работает.

Если вы уверены, что больше не будете обращаться к указателю, используйте первый тип.В противном случае используйте второй.Убедитесь, что вы установили указатель на NULL, чтобы избежать дальнейшего повреждения памяти или висячих указателей.

0 голосов
/ 19 августа 2011

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

void DeallocMat(int **mat)
{
    delete[] mat;
}

void DeallocMat(int ***mat)
{
    delete[] *mat;
    *mat = NULL;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...