manipular - Como trabalhar em uma sub-matriz em uma matriz por ponteiro?



matriz de tamanho variavel em c (2)

Esta não é uma resposta para a pergunta declarada, mas é uma resposta para o problema subjacente: gerenciamento de matrizes e visualizações de matrizes com o mínimo esforço.

Isso angariará votos negativos, mas tem sido muito útil para resolver os problemas subjacentes sempre que o tipo de pergunta que o OP faz foi solicitado, acho que vale a pena mostrar essa abordagem alternativa aqui. Não é interessante para matrizes pequenas de tamanho fixo, pois os recursos só mostram seus benefícios quando os tamanhos são maiores ou variam.

Eu uso as duas estruturas a seguir para descrever matrizes. Deixarei de fora o suporte ao conjunto de memórias (que permite gerenciar um conjunto de matrizes como um conjunto, liberando-os todos de uma vez, sem precisar gerenciar cada matriz separadamente) e tudo relacionado à operação multithread e segurança de threads, para simplificar.

O código pode conter erros de digitação; se você notar algum, deixe um comentário e eu os corrigirei.

typedef  int  data_t;        /* Matrix element data type */

struct owner {
    long          refcount;  /* Number of referenced to this data */
    size_t        size;      /* Number of elements in data[] */
    data_t        data[];    /* C99 flexible array member */
};

typedef struct {
    int           rows;      /* Number of rows in this matrix */
    int           cols;      /* Number of columns in this matrix */
    long          rowstride;
    long          colstride;
    data_t       *origin;    /* Pointer to element at row 0, col 0 */
    struct owner *owner;     /* Owner structure origin points to */
} matrix;

#define MATRIX_INIT { 0, 0, 0L, 0L, NULL, NULL }

O elemento da matriz m na linha r , coluna c , é m.origin[r * m.rowstride + c * m.colstride] , assumindo 0 <= r && r < m.rows e 0 <= c < m.cols .

Matrizes são normalmente declaradas como variáveis ​​locais, não como ponteiros. Você precisa se lembrar de liberar cada matriz individual depois que não precisar mais dela. (O mecanismo de pool omitido permite evitar isso, pois todas as matrizes em um pool são liberadas ao mesmo tempo.)

Cada matriz refere-se exatamente a uma estrutura de proprietário. As estruturas do proprietário registram o número de referências (o número de matrizes referentes aos dados nessa estrutura) e são liberadas quando a contagem de referências cai para zero:

void matrix_free(matrix *const m)
{
    if (m != NULL) {
        if (m->owner != NULL && --(m->owner.refcount) < 1L) {
            m->owner.size = 0;
            free(m->owner);
        }
        m->rows = 0;
        m->cols = 0;
        m->rowstride = 0L;
        m->colstride = 0L;
        m->origin = NULL;
        m->owner = NULL;
    }
}

Sempre que uma nova matriz é criada, a estrutura do proprietário correspondente é criada:

int matrix_new(matrix *const m, const int rows, const int cols)
{
    const size_t  size = (size_t)rows * (size_t)cols;
    struct owner *o;

    if (m == NULL)
        return errno = EINVAL;

    m->rows = 0;
    m->cols = 0;
    m->rowstride = 0L;
    m->colstride = 0L;
    m->origin = NULL;
    m->owner = NULL;

    if (rows < 1 || cols < 1)
        return errno = EINVAL;

    o = malloc(sizeof (struct owner) + size * sizeof (data_t));
    if (o == NULL) {
        return errno = ENOMEM;

    o->refcount = 1L;
    o->size = size;

    m->rows = rows;
    m->cols = cols;
    m->origin = o->data;
    m->owner = o;
#if DEFAULT_COLUMN_MAJOR > 0
    /* Default to column-major element order */
    m->rowstride = 1L;
    m->colstride = (long)rows;
#else
    /* Default to row-major element order */
    m->rowstride = (long)cols;
    m->colstride = 1L;
#endif
    return m;
}

Observe que o acima não inicializa os elementos da matriz para nenhum valor, portanto eles contêm inicialmente lixo.

A transposição de matriz é uma operação rápida e trivial:

void matrix_transpose(matrix *const m)
{
    if (m->rows > 0 && m->cols > 0) {
        const int  rows = m->rows;
        const int  cols = m->cols;
        const long rowstride = m->rowstride;
        const long colstride = m->colstride;
        m->rows = cols;
        m->cols = rows;
        m->rowstride = colstride;
        m->colstride = rowstride;
    }
}

Da mesma forma, você pode girar e espelhar matrizes, lembre-se de modificar também o membro de origin nesses casos.

Os casos interessantes e úteis estão sendo capazes de criar "visualizações" em outras matrizes. Os dados referenciados são exatamente os mesmos - a modificação de um é imediatamente visível no (s) outro (s); isso é verdadeiro alias - não é necessária cópia de memória. Diferentemente da maioria das bibliotecas (como GSL, GNU Scientific Library), essas "visualizações" são matrizes perfeitamente comuns. aqui estão alguns exemplos:

int matrix_submatrix_from(matrix *const m, const matrix *const src,
                          const int firstrow, const int firstcol,
                          const int rows, const int cols)
{
    if (m == NULL || m == src)
        return errno = EINVAL;

    m->rows = 0;
    m->cols = 0;
    m->rowstride = 0L;
    m->colstride = 0L;
    m->origin = NULL;
    m->owner = NULL;

    if (firstrow + rows > src->rows ||
        firstcol + cols > src->cols)
        return errno = EINVAL;

    if (src == NULL || src->owner == NULL)
        return errno = EINVAL;

    if (src->owner.refcount < 1L || src->owner.size == 0)
        return errno = EINVAL;
    else {
        ++(src->owner.refcount);
        m->owner = src->owner;
    }

    m->origin = src->origin + src->rowstride * firstrow
                            + src->colstride * firstcol;
    m->rows = rows;
    m->cols = cols;
    m->rowstride = src->rowstride;
    m->colstride = src->colstride;

    return 0;
}

int matrix_transposed_from(matrix *const m, const matrix *const src)
{
    if (m == NULL || m == src)
        return errno = EINVAL;

    m->rows = 0;
    m->cols = 0;
    m->rowstride = 0L;
    m->colstride = 0L;
    m->origin = NULL;
    m->owner = NULL;

    if (src == NULL || src->owner == NULL)
        return errno = EINVAL;

    if (src->owner.refcount < 1L || src->owner.size == 0)
        return errno = EINVAL;
    else {
        ++(src->owner.refcount);
        m->owner = src->owner;
    }
    m->origin = src->origin;

    m->rows = src->cols;
    m->cols = src->rows;
    m->rowstride = src->colstride;
    m->colstride = src->rowstride;

    return 0;
}

Usando código semelhante ao acima, você pode criar visualizações de matriz de uma linha ou coluna descrevendo qualquer linha, coluna ou diagonal. (As diagonais são especialmente úteis em determinadas situações.) As submatrizes podem ser espelhadas ou giradas, e assim por diante. Você pode liberar com segurança uma matriz da qual precisa apenas de uma submatriz ou outra visualização, pois a contagem de referência da estrutura do proprietário controla quando os dados podem ser descartados com segurança.

A multiplicação de matrizes e outras operações complexas semelhantes para matrizes maiores são muito sensíveis a problemas de localidade em cache. Isso significa que é melhor copiar os dados da matriz de origem em matrizes compactas (com as matrizes devidamente alinhadas e em elementos na ordem correta para esse operando). A sobrecarga causada por linhas e colunas terem um passo separado (em vez de apenas um, como é típico) é realmente mínima; nos meus próprios testes, insignificante.

A melhor característica dessa abordagem, no entanto, é que ela permite escrever código eficiente sem se preocupar com o que é uma matriz "real", o que é uma "visualização" e como os dados subjacentes reais são armazenados em uma matriz, a menos que você se importe . Finalmente, é simples o suficiente para quem entende o gerenciamento básico de memória dinâmica em C para entender completamente.

https://ffff65535.com

Eu tenho uma matriz de tamanho n. Veja um exemplo:

Minha função recursiva processa os elementos que estão na borda da matriz. Agora eu quero chamá-lo (a chamada recursiva) na matriz quadrada interna:

Este é o protótipo da minha função recursiva:

void rotate(int** mat, size_t n);

Eu sei que uma matriz 2D é uma matriz dentro de uma matriz. Eu sei que *(mat+1) + 1) fornecerá o endereço de memória que deve ser o endereço base da minha nova matriz. Isto é o que eu tentei:

rotate((int **)(*(mat+1) + 1), n-2)

Mas não funciona, e recebo um segfault quando tento acessá-lo com [][] .


Não tenho certeza do seu aplicativo, mas gostaria de saber se o uso de #define para o tamanho da sua matriz ajudaria ....

#define X_SIZE 4
#define Y_SIZE 4

ou mesmo

#define N_SIZE 4

... porque então você pode usar X_SIZE e Y_SIZE (OR N_SIZE) em sua função sem precisar passá-los explicitamente.

em principal você pode colocar

 int matrix[X_SIZE * Y_SIZE];

ou

int matrix2[N_SIZE * N_SIZE];

você pode chamar o i-ésima linha e o j-ésimo elemento

*(pmatrix + X_SIZE*j + i) 

ou

matrix[X_SIZE*j + i] 

ou

*(pmatrix2 + N_SIZE*j + i)

ou

matrix2[N_SIZE*j + i]

onde pmatrix e pmatrix2 são ponteiros para matrix e matrix2.

Tenho certeza de que não há truque inteligente para passar facilmente a matriz 2x2 do quadrado interno para uma função, a menos que você copie os elementos do centro de sua matriz para uma nova matriz e depois copie o resultado posteriormente.





matrix