unidimensionales una tridimensionales tipos programas matriz llenar ingresar elementos ejemplos dinamicos datos como bidimensionales arreglos arreglo c arrays dynamic

una - ¿Cómo trabajo con arreglos multidimensionales dinámicos en C?



programas en c arreglos bidimensionales (9)

¿Alguien sabe cómo puedo usar arrays multidimensionales dinámicamente asignados usando C? ¿Es eso posible?


Lo esencial

Las matrices en c se declaran y acceden con el operador [] . Así que eso

int ary1[5];

declara una matriz de 5 enteros. Los elementos se numeran desde cero, por lo que ary1[0] es el primer elemento, y ary1[4] es el último elemento. Nota 1: no hay inicialización predeterminada, por lo que la memoria ocupada por la matriz puede contener inicialmente algo . ary1[5] : ary1[5] accede a la memoria en un estado indefinido (que ni siquiera puede acceder a ella), ¡así que no lo haga!

Las matrices multidimensionales se implementan como una matriz de matrices (de matrices (de ...)). Asi que

float ary2[3][5];

declara una matriz de 3 matrices unidimensionales de 5 números de punto flotante cada una. Ahora ary2[0][0] es el primer elemento de la primera matriz, ary2[0][4] es el último elemento de la primera matriz, y ary2[2][4] es el último elemento de la última matriz. El estándar ''89 requiere que estos datos sean contiguos (sección A8.6.2 en la página 216 de mi K & R 2nd. Ed.) Pero parece ser agnóstico en el relleno.

Intentando ser dinámico en más de una dimensión

Si no conoce el tamaño de la matriz en tiempo de compilación, querrá asignar dinámicamente la matriz. Es tentador intentar

double *buf3; buf3 = malloc(3*5*sizeof(double)); /* error checking goes here */

que debería funcionar si el compilador no rellena la asignación (pegue espacio adicional entre las matrices unidimensionales). Puede ser más seguro ir con:

double *buf4; buf4 = malloc(sizeof(double[3][5])); /* error checking */

pero de cualquier forma el truco llega en el momento de desreferenciar. No puede escribir buf[i][j] porque buf tiene el tipo incorrecto. Tampoco puedes usar

double **hdl4 = (double**)buf; hdl4[2][3] = 0; /* Wrong! */

porque el compilador espera que hdl4 sea ​​la dirección de una dirección de un doble. Tampoco puedes usar double incomplete_ary4[][]; porque esto es un error;

¿Entonces que puedes hacer?

  • Haz la aritmética de filas y columnas tú mismo
  • Asignar y hacer el trabajo en una función
  • Utilice una matriz de punteros (el mecanismo qrdl está hablando)

Haz las matemáticas tú mismo

Simplemente calcule la compensación de memoria para cada elemento de esta manera:

for (i=0; i<3; ++i){ for(j=0; j<3; ++j){ buf3[i * 5 + j] = someValue(i,j); /* Don''t need to worry about padding in this case */ } }

Asignar y hacer el trabajo en una función

Defina una función que tome el tamaño necesario como argumento y proceda de la forma habitual

void dary(int x, int y){ double ary4[x][y]; ary4[2][3] = 5; }

Por supuesto, en este caso ary4 es una variable local y no puede devolverla: todo el trabajo con la matriz debe realizarse en la función a la que llama en las funciones que llama.

Una variedad de punteros

Considera esto:

double **hdl5 = malloc(3*sizeof(double*)); /* Error checking */ for (i=0; i<3; ++i){ hdl5[i] = malloc(5*sizeof(double)) /* Error checking */ }

Ahora hdl5 apunta a una matriz de punteros cada uno de los cuales apunta a una matriz de dobles. Lo mejor de todo es que puedes usar la notación de matriz bidimensional para acceder a esta estructura --- hdl5[0][2] obtiene el elemento medio de la primera fila --- pero esto no es menos un tipo diferente de objeto que una matriz bidimensional declarada por double ary[3][5]; .

Esta estructura es más flexible que una matriz bidimensional (porque las filas no necesitan tener la misma longitud), pero acceder a ella generalmente será más lento y requerirá más memoria (necesita un lugar para sostener los punteros intermedios).

Tenga en cuenta que dado que no he configurado ningún protector, tendrá que hacer un seguimiento del tamaño de todos los arreglos.

Aritmética

c no proporciona soporte para matemáticas vector, matriz o tensor, tendrá que implementarlo usted mismo o traer una biblioteca.

La multiplicación por un escalador y la suma y resta de arreglos del mismo rango son fáciles: simplemente recorra los elementos y realice la operación sobre la marcha. Los productos internos son igualmente sencillos.

Los productos externos significan más bucles.


¿Por qué, colegas, nadie reveló el hecho de que no existe una solución exacta en el reino de C? pero solo en C ++ (por exacto quiero decir que las soluciones anteriores y de otro sitio difieren de las verdaderas matrices C multidimensionales con: entidades adicionales y por lo tanto memoria adicional, etc.).

En C ++ debes implementar (solo varias líneas de código):

typedef double T; class Matrix2D { public: Matrix2D(int, int); ~Matrix2D(); T* operator[](int); private: T* const memory; const int rows; const int cols; }; Matrix2D::Matrix2D(int r, int c) : rows(r), cols(c), memory(new T[r*c]) {} Matrix2D::~Matrix2D() { delete[] memory; } T* Matrix2D::operator[](int row) { return memory + cols*row;}

Eso es todo para aquellos que usan código como este: a[i][j] .

Pero para una similitud exacta con matrices C multidimensionales, esa clase ( Matrix2D ) carece de aritmética de doble puntero, para ser utilizada así: (*(a+i))[j] . Eso no es difícil: implementar clss internos "doublePointer" con aritméticos y operadores de desreferencia. Pero preferiría implementar un tipo de iterador para dichos fines (pasar de una fila a otra).

Más de 2 dimensiones? Solo necesita clases internas que implementen el operator[]...[] correspondiente operator[]...[] de (n-1) -dimensión. Por desgracia, rutina ...


Aquí está el código de trabajo que define una subrutina make_3d_array para asignar una matriz 3D multidimensional con elementos N1 , N2 y N3 en cada dimensión, y luego la rellena con números aleatorios. Puede usar la notación A[i][j][k] para acceder a sus elementos.

#include <stdio.h> #include <stdlib.h> #include <time.h> // Method to allocate a 2D array of floats float*** make_3d_array(int nx, int ny, int nz) { float*** arr; int i,j; arr = (float ***) malloc(nx*sizeof(float**)); for (i = 0; i < nx; i++) { arr[i] = (float **) malloc(ny*sizeof(float*)); for(j = 0; j < ny; j++) { arr[i][j] = (float *) malloc(nz * sizeof(float)); } } return arr; } int main(int argc, char *argv[]) { int i, j, k; size_t N1=10,N2=20,N3=5; // allocates 3D array float ***ran = make_3d_array(N1, N2, N3); // initialize pseudo-random number generator srand(time(NULL)); // populates the array with random numbers for (i = 0; i < N1; i++){ for (j=0; j<N2; j++) { for (k=0; k<N3; k++) { ran[i][j][k] = ((float)rand()/(float)(RAND_MAX)); } } } // prints values for (i=0; i<N1; i++) { for (j=0; j<N2; j++) { for (k=0; k<N3; k++) { printf("A[%d][%d][%d] = %f /n", i,j,k,ran[i][j][k]); } } } free(ran); }


Con asignación dinámica, utilizando malloc:

int** x; x = malloc(dimension1_max * sizeof(int*)); for (int i = 0; i < dimension1_max; i++) { x[i] = malloc(dimension2_max * sizeof(int)); } [...] for (int i = 0; i < dimension1_max; i++) { free(x[i]); } free(x);

Esto asigna una matriz 2D de tamaño dimension1_max * dimension2_max . Entonces, por ejemplo, si quiere una matriz 640 * 480 (fe píxeles de una imagen), use dimension1_max = 640, dimension2_max = 480. Luego puede acceder a la matriz usando x[d1][d2] donde d1 = 0 .. 639, d2 = 0..479.

Pero una búsqueda en SO o Google también revela otras posibilidades, por ejemplo en esta pregunta SO

Tenga en cuenta que su matriz no asignará una región contigua de memoria (640 * 480 bytes) en ese caso, lo que podría dar problemas con las funciones que asumen esto. Entonces, para que la matriz satisfaga la condición, reemplace el bloque malloc anterior con esto:

int** x; int* temp; x = malloc(dimension1_max * sizeof(int*)); temp = malloc(dimension1_max * dimension2_max * sizeof(int)); for (int i = 0; i < dimension1_max; i++) { x[i] = temp + (i * dimension2_max); } [...] free(temp); free(x);


Desde C99, 13 años ahora, C tiene matrices 2D con límites dinámicos. Si desea evitar que dicha bestia se asigne en la pila (lo cual debería hacer), puede asignarlos fácilmente de una vez como el siguiente

double (*A)[n] = malloc(sizeof(double[n][n]));

y eso es. A continuación, puede usarlo fácilmente ya que se usa para arreglos 2D con algo como A[i][j] . Y no olvides eso al final

free(A);

Randy Meyers escribió una serie de artículos que explican arreglos de longitud variable (VLA) .



No hay forma de asignar todo de una vez. En su lugar, cree una matriz de punteros, luego, para cada puntero, cree la memoria para ello. Por ejemplo:

int** array; array = (int**)malloc(sizeof(int*) * 50); for(int i = 0; i < 50; i++) array[i] = (int*)malloc(sizeof(int) * 50);

Por supuesto, también puede declarar el conjunto como int* array[50] y omitir el primer malloc, pero el segundo conjunto es necesario para asignar dinámicamente el almacenamiento requerido.

Es posible hackear una forma de asignarlo en un solo paso, pero requeriría una función de búsqueda personalizada, pero escribir eso de manera que siempre funcione puede ser molesto. Un ejemplo podría ser L(arr,x,y,max_x) arr[(y)*(max_x) + (x)] , luego malloc un bloque de 50 * 50 ints o lo que sea y acceder utilizando esa macro L , por ejemplo

#define L(arr,x,y,max_x) arr[(y)*(max_x) + (x)] int dim_x = 50; int dim_y = 50; int* array = malloc(dim_x*dim_y*sizeof(int)); int foo = L(array, 4, 6, dim_x);

Pero eso es mucho peor a menos que sepa los efectos de lo que está haciendo con la macro del preprocesador.


Si conoce el número de columnas en tiempo de compilación, es bastante simple:

#define COLS ... ... size_t rows; // get number of rows T (*ap)[COLS] = malloc(sizeof *ap * rows); // ap is a *pointer to an array* of T

Puede tratar ap como cualquier matriz 2D:

ap[i][j] = x;

Cuando hayas terminado, desasignalo como

free(ap);

Si no conoce el número de columnas en tiempo de compilación, pero está trabajando con un compilador C99 o un compilador C2011 que admita matrices de longitud variable, sigue siendo bastante simple:

size_t rows; size_t cols; // get rows and cols T (*ap)[cols] = malloc(sizeof *ap * rows); ... ap[i][j] = x; ... free(ap);

Si no conoce el número de columnas en tiempo de compilación y está trabajando con una versión de C que no admite matrices de longitud variable, tendrá que hacer algo diferente. Si necesita asignar todos los elementos en un fragmento contiguo (como una matriz regular), puede asignar la memoria como una matriz 1D y calcular una compensación 1D:

size_t rows, cols; // get rows and columns T *ap = malloc(sizeof *ap * rows * cols); ... ap[i * rows + j] = x; ... free(ap);

Si no necesita que la memoria sea contigua, puede seguir un método de asignación de dos pasos:

size_t rows, cols; // get rows and cols T **ap = malloc(sizeof *ap * rows); if (ap) { size_t i = 0; for (i = 0; i < cols; i++) { ap[i] = malloc(sizeof *ap[i] * cols); } } ap[i][j] = x;

Dado que la asignación fue un proceso de dos pasos, la desasignación también debe ser un proceso de dos pasos:

for (i = 0; i < cols; i++) free(ap[i]); free(ap);


int rows, columns; /* initialize rows and columns to the desired value */ arr = (int**)malloc(rows*sizeof(int*)); for(i=0;i<rows;i++) { arr[i] = (int*)malloc(cols*sizeof(int)); }