2009-10-18 24 views
12

Tengo un programa que se parece a lo siguiente:conversión de matrices multidimensionales a los punteros en C++

double[4][4] startMatrix; 
double[4][4] inverseMatrix; 
initialize(startMatrix) //this puts the information I want in startMatrix 

ahora quiero para calcular la inversa de startMatrix y ponerlo en inverseMatrix. Tengo una función de biblioteca para este propósito cuyo prototipo es el siguiente:

void MatrixInversion(double** A, int order, double** B) 

que se lleva a la inversa de A y lo pone en B. El problema es que necesito saber cómo convertir el doble [4] [ 4] en un doble ** para dar a la función. He intentado sólo hacer que la "forma obvia":

MatrixInversion((double**)startMatrix, 4, (double**)inverseMatrix)) 

pero eso no parece funcionar. ¿Es esa la forma correcta de hacerlo?

+0

& startMatrix, 4, & inverseMatrix –

+1

¿Por qué no haces una clase de matriz, más bien haciendo las cosas a la manera C? (Pasar objetos a funciones, en lugar de invocar métodos sobre objetos) – GManNickG

+2

@GMan: Como OP dijo que la función es "una función de biblioteca", lo más probable es que no tenga libertad para cambiar la interfaz. Clase o no clase, en algún momento tendrá que obtener el "doble **" apropiado para pasar a la función. – AnT

Respuesta

17

No, no hay una forma correcta de hacerlo específicamente. Una matriz double[4][4] no es convertible a un puntero double **. Estas son dos maneras alternativas e incompatibles de implementar una matriz 2D. Algo necesita ser cambiado: ya sea la interfaz de la función o la estructura de la matriz pasada como un argumento.

La forma más sencilla de hacer esto último, es decir, para hacer que su double[4][4] matriz existente compatible con la función, es la creación de "índice" matrices temporales de tipo double *[4] apuntando a los inicios de cada fila de cada matriz

double *startRows[4] = { startMatrix[0], startMatrix[1], startMatrix[2] , startMatrix[3] }; 
double *inverseRows[4] = { /* same thing here */ }; 

y pasar estas matrices "índice" en lugar

MatrixInversion(startRows, 4, inverseRows); 

Una vez terminada la función de trabajo, puede olvidarse de los startRows y inverseRows matrices, ya que el resultado será pl ingresado correctamente en su matriz original inverseMatrix.

1

Dos dimensiones matriz no es un puntero a puntero o algo similar. El tipo correcto para startMatrix es double (*)[4]. Para su función, la firma debe ser similar a:

MatrixInversion(double (*A)[4], int order, double (*B)[4]); 
+1

La función aparentemente está trabajando con matrices cuadradas de cualquier tamaño (orden). Restringirla a matrices 4x4 es difícilmente aceptable. Además, no tiene sentido pasar la 'orden' ahora. – AnT

+0

@AndreyT, solo le estaba mostrando cómo hacerlo. Si quisiera que fuera general, podría mostrarle cómo tener una clase que represente una matriz. – leiz

+0

En una matriz * cuadrada *, el conteo de filas es el mismo que el conteo de columnas. Has codificado tu recuento de columnas como 4. Ahora ya no tiene sentido pasar el recuento de filas; también debe ser 4 y solo 4. – AnT

-1

El problema es que una matriz bidimensional no es lo mismo que una matriz de punteros. Una matriz bidimensional almacena los elementos una fila tras otra —, por lo tanto, cuando pasa una matriz de este tipo, solo se proporciona un puntero al inicio. La función de recepción puede encontrar la forma de encontrar cualquier elemento de la matriz, pero solo si conoce la longitud de cada fila.

Por lo tanto, su función de recepción debe declararse como void MatrixInversion(double A[4][], int order, double B[4][]).

+1

Si conoce los tamaños de las matrices y sabe cómo se completaron, puede pasarlas como & A, pero tiene una gran oferta que necesita saber con anticipación, pero es posible hacerlo. –

+0

Esta no es una declaración correcta. Solo el primer tamaño se puede omitir en la declaración de parámetros de la matriz. Además, la fijación del tamaño de la matriz elimina la felxibilidad deseada de la función (es decir, su capacidad para procesar la matriz de cualquier tamaño). – AnT

+0

@James Black: Exactamente. Si vamos a arreglar el tamaño de la matriz cuadrada de todos modos, la forma correcta de declarar el parámetro sería 'double (& A) [4] [4]', no 'double A [] [4]'. Al menos, conservaría el tipo de matriz completa, en lugar de degradarlo a 'double (*) [4]'. – AnT

2

Dado que está utilizando C++, la forma correcta de hacer algo como esto sería con una clase personalizada y algunas plantillas. El siguiente ejemplo es bastante difícil, pero tiene el punto básico.

#include <iostream> 

using namespace std; 

template <int matrix_size> 
class SquareMatrix 
{ 
    public: 
     int size(void) { return matrix_size; } 
     double array[matrix_size][matrix_size]; 
     void copyInverse(const SquareMatrix<matrix_size> & src); 
     void print(void); 
}; 

template <int matrix_size> 
void SquareMatrix<matrix_size>::copyInverse(const SquareMatrix<matrix_size> & src) 
{ 
    int inv_x; 
    int inv_y; 

    for (int x = 0; x < matrix_size; x++) 
    { 
     inv_x = matrix_size - 1 - x; 
     for (int y = 0; y < matrix_size; y++) 
     { 
      inv_y = matrix_size - 1 - y; 
      array[x][y] = src.array[inv_x][inv_y]; 
     } 
    } 
} 

template <int matrix_size> 
void SquareMatrix<matrix_size>::print(void) 
{ 
    for (int y = 0; y < 4; y++) 
    { 
     for (int x = 0; x < 4; x++) 
     { 
      cout << array[x][y] << " "; 
     } 
     cout << endl; 
    } 
} 

template <int matrix_size> 
void Initialize(SquareMatrix<matrix_size> & matrix); 

int main(int argc, char * argList[]) 
{ 
    SquareMatrix<4> startMatrix; 
    SquareMatrix<4> inverseMatrix; 

    Initialize(startMatrix); 

    inverseMatrix.copyInverse(startMatrix); 

    cout << "Start:" << endl; 
    startMatrix.print(); 

    cout << "Inverse:" << endl; 
    inverseMatrix.print(); 

    return 0; 
} 

template <int matrix_size> 
void Initialize(SquareMatrix<matrix_size> & matrix) 
{ 
    for (int x = 0; x < matrix_size; x++) 
    { 
     for (int y = 0; y < matrix_size; y++) 
     { 
      matrix.array[x][y] = (x+1)*10+(y+1); 
     } 
    } 
} 
+0

me gusta su idea, la forma correcta de C++. un buen ejemplo independientemente de las obras o no. – Test

+0

También es bueno tener en una plantilla tipo T, que es lo que serán los elementos. Y probablemente haga que el tamaño no esté firmado, ya que los tamaños negativos no tienen sentido. – GManNickG

+0

Sin embargo, la implementación de una operación relativamente pesada (es inversa, por cierto, no transpuesta) como una función de plantilla parametrizada por tamaño de matriz podría dar como resultado la saturación del código, ya que el código se volverá a instanciar para cada tamaño particular. La técnica adecuada para tratar este problema es implementar la mayor parte de la funcionalidad mediante una función parametrizada con un tamaño de tiempo de ejecución (como en la pregunta original) y luego crear una plantilla "delgada" sobre esa función. Pero eso nos lleva de vuelta al problema original. – AnT

-2

por buen codificante si C++:

struct matrix { 
    double m[4][4]; 
}; 

matrix startMatrix; 
matrix inverseMatrix; 

lo que la interfaz habría

void MatrixInversion(matrix &A, int order, matrix &B); 

y utilizarlo

MatrixInversion(startMatrix, 4, inverseMatrix); 

El beneficio

  1. la interfaz es muy simple y clara.
  2. una vez que necesite modificar "m" de la matriz internamente, no necesita actualizar la interfaz.

O esta manera

struct matrix { 
    void Inversion(matrix &inv, int order) {...} 
protected: 
    double m[4][4]; 
}; 

matrix startMatrix; 
matrix inverseMatrix; 
... 

una manera fea en c

void MatrixInversion(void *A, int order, void *B); 
MatrixInversion((void*)startMatrix, 4, (void*)inverseMatrix); 

EDIT: código de referencia de MatrixInversion la que no fallará:

void MatrixInversion(void *A, int order, void *B) 
{ 
    double _a[4][4]; 
    double _b[4][4]; 

    memcpy(_a, A, sizeof _a); 
    memcpy(_b, B, sizeof _b); 
    // processing data here 

    // copy back after done 
    memcpy(B, _b, sizeof _b); 
} 
+0

El mismo error de diseño que en otras respuestas ... Has codificado el tamaño de la matriz fija en tu tipo de matriz. ¿Qué vas a hacer si necesitas una matriz de 5x5? 6x6? ¿Y cuál es el punto de pasar 4 a la función, cuando 4 ya está codificado en el tipo de matriz? – AnT

+0

Su "manera fea en C" simplemente bloqueará el programa. El OP ya lo intentó, si lo notó, y no funcionó (por razones obvias). – AnT

+0

regla de refactorización: mejore el diseño cuando sea necesario. ver la sección "El benefig". "4" es un ejemplo específico, es decir, un número entero, y su valor es 4. – Test

4

Por razón dada que matriz bidimensional (un bloque contiguo de memoria) y una matriz de punteros (no contiguos) son cosas muy diferentes, no puede pasar una matriz bidimensional a una función que funciona con puntero a puntero.

Una cosa que podría hacer: plantillas. Haga que el tamaño de la segunda dimensión sea un parámetro de plantilla.

#include <iostream> 

template <unsigned N> 
void print(double a[][N], unsigned order) 
{ 
    for (unsigned y = 0; y < order; ++y) { 
     for (unsigned x = 0; x < N; ++x) { 
      std::cout << a[y][x] << ' '; 
     } 
     std::cout << '\n'; 
    } 
} 

int main() 
{ 
    double arr[3][3] = {{1, 2.3, 4}, {2.5, 5, -1.0}, {0, 1.1, 0}}; 
    print(arr, 3); 
} 

Otro, un poco más torpe forma podría ser que la función acepta un puntero a una unidimensional matriz, y la anchura y la altura dada como argumentos, y calcular los índices en una representación en dos dimensiones a sí mismo.

#include <iostream> 

void print(double *a, unsigned height, unsigned width) 
{ 
    for (unsigned y = 0; y < height; ++y) { 
     for (unsigned x = 0; x < width; ++x) { 
      std::cout << a[y * width + x] << ' '; 
     } 
     std::cout << '\n'; 
    } 
} 

int main() 
{ 
    double arr[3][3] = {{1, 2.3, 4}, {2.5, 5, -1.0}, {0, 1.1, 0}}; 
    print(&arr[0][0], 3, 3); 
} 

Naturalmente, una matriz es algo que merece una clase propia (pero lo anterior aún puede ser relevante, si es necesario escribir funciones de ayuda).

0

Hay una solución usando el puntero a punto por bobobobo

William Sherif (bobobobo) utilizaron la versión C y sólo quiero mostrar C++ versión de la respuesta de bobobobo.

int numRows = 16 ; 
int numCols = 5 ; 
int **a ; 

a = new int*[ numRows* sizeof(int*) ]; 
for(int row = 0 ; row < numRows ; row++) 
{ 
    a[row] = new int[ numCols*sizeof(int) ]; 
} 

El resto del código es el mismo con bobobobo de.

Cuestiones relacionadas