C: передать 2D-массив в качестве аргумента функции и получить результаты, сохраненные в адресе аргумента - PullRequest
0 голосов
/ 19 апреля 2020

Short : Как передать двумерный массив в качестве аргумента функции и получить результаты, сохраненные в адресе аргумента?

Longer : у меня есть код ( ниже) которая вызывает main() функцию инициализации, а затем печатает содержимое. Однако я ожидаю, что после сохранения указателей, заданных malloc, в переменную A результаты будут сохранены в аргументе, но это не так. При выполнении аналогичных действий с 1D-массивами это работает плавно, но не для 2D-массивов.

Для простоты в этом случае я делаю это 3x3, но я хочу иметь нефиксированный объем ГБ данных. Затем я должен использовать malloc и не могу объявить размер матрицы во время компиляции.

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

void print_matrix(int n, double **A) {
    int i, j;
    for(i=0; i<n; i++) {
        for(j=0;j<n;j++) {
            printf("%0.2f ",A[i][j]);
        }
        printf("\n");
    }
}

void init(int n, double **A) {
    int i;
    A = (double **)malloc(n * n * sizeof(double *)); 
    for(i=0; i<n; i++) {
         A[i] = (double *)malloc(n * sizeof(double)); 
    }
    A[0][0] = 9.0;
    A[0][1] = 6.0;
    A[0][2] = -3.0;
    A[1][0] = 6.0;
    A[1][1] = 13.0;
    A[1][2] = -5.0;
    A[2][0] = -3.0;
    A[2][1] = -5.0;
    A[2][2] = 18.0;
    print_matrix(n, A);
}

int main() {
    int i;
    int n=3;
    double **A;
    init(n, A);
    print_matrix(n, A);
    for(i=0; i<n; i++)
        free(A[i]);
    free(A);
    return 0;
}

После компиляции я могу печатать содержимое матрицы внутри функции init(), но не из main() без получения ошибки сегментации, что, конечно, означает, что A указывает на неназначенную память и что даже A[0][0] не указывает на root матрицы. Я использую G CC и стандартный C99.

$ gcc --version
gcc (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008
$ gcc main.c -o main -std=c99
$ ./main
9.00 6.00 -3.00 
6.00 13.00 -5.00 
-3.00 -5.00 18.00 
Segmentation fault (core dumped)

Есть и другие полезные вопросы о передаче 2D-массивов, но они не решают мою проблему, поскольку не решают проблему получения результатов в аргумент.

Edit : это простой случай, но мне нужно было бы передать несколько матриц в качестве аргументов функции. Поэтому я не могу просто вернуть адрес матрицы A, потому что мне нужно было бы сохранить адреса нескольких матриц.

Ответы [ 2 ]

1 голос
/ 19 апреля 2020

Я бы сделал это так:

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

void print_matrix(int n, double **A) {
    int i, j;
    for(i=0; i<n; i++) {
        for(j=0;j<n;j++) {
            printf("%0.2f ",A[i][j]);
        }
        printf("\n");
    }
}

double ** init(int n) {
    int i;
    double **A;
    A = (double **)malloc(n * n * sizeof(double *));
    for(i=0; i<n; i++) {
         A[i] = (double *)malloc(n * sizeof(double));
    }
    A[0][0] = 9.0;
    A[0][1] = 6.0;
    A[0][2] = -3.0;
    A[1][0] = 6.0;
    A[1][1] = 13.0;
    A[1][2] = -5.0;
    A[2][0] = -3.0;
    A[2][1] = -5.0;
    A[2][2] = 18.0;
    print_matrix(n, A);
    return A;
}

int main() {
    int i;
    int n=3;
    double **A;
    A = init(n);
    print_matrix(n, A);
    for(i=0; i<n; i++)
        free(A[i]);
    free(A);
    return 0;
}

Выделите память в вашей функции init () и верните указатель.

[ronald@ocelot tmp]$ ./x
9.00 6.00 -3.00 
6.00 13.00 -5.00 
-3.00 -5.00 18.00 
9.00 6.00 -3.00 
6.00 13.00 -5.00 
-3.00 -5.00 18.00 

Ответ на оригинальный вопрос будет (только измененный код)

void init(int n, double ***A) {
    int i;
    *A = (double **)malloc(n * n * sizeof(double *));
    for(i=0; i<n; i++) {
         (*A)[i] = (double *)malloc(n * sizeof(double));
    }
    (*A)[0][0] = 9.0;
    (*A)[0][1] = 6.0;
    (*A)[0][2] = -3.0;
    (*A)[1][0] = 6.0;
    (*A)[1][1] = 13.0;
    (*A)[1][2] = -5.0;
    (*A)[2][0] = -3.0;
    (*A)[2][1] = -5.0;
    (*A)[2][2] = 18.0;
    print_matrix(n, *A);
}

int main() {
    int i;
    int n=3;
    double **A;
    init(n, &A);
    print_matrix(n, A);
    for(i=0; i<n; i++)
        free(A[i]);
    free(A);
    return 0;
}
0 голосов
/ 19 апреля 2020

Передать матрицу указателем. Поскольку никто не хочет быть трехзвездным программистом , создайте структуру для ваших данных и передайте указатель на структуру. Помните, что читабельность кода, правильная инкапсуляция и удобочитаемое объектно-ориентированное программирование помогут вам в дальнейшем. Помните об обработке ошибок, которую, я надеюсь, вы опустите в своем вопросе для краткости. Не разыгрывать результат Малло c. Используйте тип size_t для представления количества элементов в массиве.

Тем не менее, я бы сделал:

// Represents a n x n dynamically allocated matrix.
struct matrix_s {
   double **a;
   size_t n;
};

// Forward declaration for `matrix_init`.
void matrix_free(struct matrix_s *m);

int matrix_init(struct matrix_s *m, size_t n) {
    assert(n > 2); // as you seem to access indices at least 2
    m->a = calloc(n * n, sizeof(*m->a));
    if (m->a == NULL) {
        goto ERROR;
    }
    m->n = 0;
    for(size_t i = 0; i < n; i++) {
         m->a[i] = calloc(n, sizeof(*A[i])); 
         m->n++;
         if (m->a[i] == NULL) {
            goto ERROR;
         }
    }

    // fill the array

    // SUCCESS
    return 0;

    ERROR:
    matrix_free(m);
    return ENOMEM;
}

void matrix_print(struct matrix_s *m) {
   for (size_t i = 0; i < m->n; ++i) {
       printf(....);
   }
}

void matrix_free(struct matrix_s *m) {
   for (size_t i = 0; i < m->n; ++i) {
      free(m->a[i]);
   }
   free(m->a);
   m->a = NULL;
   m->n = 0;
}

int main() {
    struct matrix_s m;
    if (matrix_init(&m, 3) != 0) {
       abort();
    }
    matrix_print(&m);
    matrix_free(&m);
    return 0;
}

Тем не менее, если вы хотите, вы можете быть 3-звездочным запрограммируйте и передайте указатель A из main с помощью указателя практически без изменений:

void init(int n, double ***A0) {
    double **A = malloc(n * n * sizeof(*A)); 

    // same your code without any modification

    *A0 = A; // asing the outer pointer to A
}

int main() {
    double **A;
    init(3, &A); // pass `A` by a pointer
    // ...
}

Поскольку ваша матрица имеет одинаковый размер строки для каждой строки, при правильной инкапсуляции вы можете подумать просто выделив один блок памяти для n * n, удваивается и самостоятельно рассчитывает индексы для навигации. Вы также можете использовать массив переменной длины, например double (*array)[n], для доступа к вашим данным в виде массива. Но изменение размера массива может быть значительно сложнее.

struct matrix_s {
    double *a;
    size_t n;
};

double *matrix_getp(struct matrix_s *m, size_t i, size_t j) {
     return &m->a[i * m->n + j];
}

int matrix_init(struct matrix_s *m, size_t n) {
     m->a = malloc(n * n * sizeof(*m->a)); // only a single allocation
     // ...

     // access to matrix at A[0][0]  
     *matrix_getp(m, 0, 0) = 1;
     // or the same using a VLA variable:
     double (*a)[n] = (void*)m->a;
     a[0][0] = 1;

     // ...
}
...