Puntero a arreglos 2D en C

6 minutos de lectura

avatar de usuario
crisotribax

Sé que hay varias preguntas sobre eso que brinda soluciones buenas (y funcionales), pero ninguna en mi humilde opinión que diga claramente cuál es la mejor manera de lograr esto. Entonces, supongamos que tenemos una matriz 2D:

int tab1[100][280];

Queremos hacer un puntero que apunte a esta matriz 2D. Para lograr esto, podemos hacer:

int (*pointer)[280]; // pointer creation
pointer = tab1; //assignation
pointer[5][12] = 517; // use
int myint = pointer[5][12]; // use

o alternativamente :

int (*pointer)[100][280]; // pointer creation
pointer = &tab1; //assignation
(*pointer)[5][12] = 517; // use
int myint = (*pointer)[5][12]; // use 

OK, ambos parecen funcionar bien. Ahora quisiera saber:

  • ¿Cuál es la mejor manera, la 1ra o la 2da?
  • son ambos iguales para el compilador? (velocidad, rendimiento…)
  • ¿Una de estas soluciones consume más memoria que la otra?
  • ¿Cuál es el más utilizado por los desarrolladores?

  • Recuerde que una matriz de punteros (como en int *pointer[280];) no es lo mismo que una matriz de matrices.

    – Un tipo programador

    11 de febrero de 2013 a las 9:11

  • hola, fernando. He corregido la primera solución con paréntesis. se disculpa

    – Crisotribax

    11 de febrero de 2013 a las 9:25


avatar de usuario
oHo

//defines an array of 280 pointers (1120 or 2240 bytes)
int  *pointer1 [280];

//defines a pointer (4 or 8 bytes depending on 32/64 bits platform)
int (*pointer2)[280];      //pointer to an array of 280 integers
int (*pointer3)[100][280]; //pointer to an 2D array of 100*280 integers

Utilizando pointer2 o pointer3 producir el mismo binario excepto manipulaciones como ++pointer2 como lo señaló WhozCraig.

recomiendo usar typedef (produciendo el mismo código binario que el anterior pointer3)

typedef int myType[100][280];
myType *pointer3;

Nota: Desde C++ 11, también puede usar la palabra clave using en lugar de typedef

using myType = int[100][280];
myType *pointer3;

en tu ejemplo:

myType *pointer;                // pointer creation
pointer = &tab1;                // assignation
(*pointer)[5][12] = 517;        // set (write)
int myint = (*pointer)[5][12];  // get (read)

Nota: Si la matriz tab1 se usa dentro del cuerpo de una función => esta matriz se colocará dentro de la memoria de la pila de llamadas. Pero el tamaño de la pila es limitado. El uso de arreglos más grandes que la pila de memoria libre produce un desbordamiento de pila choque.

El fragmento completo se puede compilar en línea en gcc.godbolt.org

int main()
{
    //defines an array of 280 pointers (1120 or 2240 bytes)
    int  *pointer1 [280];
    static_assert( sizeof(pointer1) == 2240, "" );

    //defines a pointer (4 or 8 bytes depending on 32/64 bits platform)
    int (*pointer2)[280];      //pointer to an array of 280 integers
    int (*pointer3)[100][280]; //pointer to an 2D array of 100*280 integers  
    static_assert( sizeof(pointer2) == 8, "" );
    static_assert( sizeof(pointer3) == 8, "" );

    // Use 'typedef' (or 'using' if you use a modern C++ compiler)
    typedef int myType[100][280];
    //using myType = int[100][280];

    int tab1[100][280];

    myType *pointer;                // pointer creation
    pointer = &tab1;                // assignation
    (*pointer)[5][12] = 517;        // set (write)
    int myint = (*pointer)[5][12];  // get (read)

    return myint;
}

  • hola, revise mi primera solución: anteriormente olvidé los paréntesis en la creación del puntero. se disculpa

    – Crisotribax

    11 de febrero de 2013 a las 9:28

  • @user216993 También escribiste (pointer*) en lugar de (*pointer) en la segunda versión. 😉 Ok, actualizo mi respuesta. Salud

    – oho

    11 de febrero de 2013 a las 9:31


  • Gracias, olibre por su clara respuesta. (y se disculpa de nuevo por los errores tipográficos…).

    – Crisotribax

    11 de febrero de 2013 a las 10:02

  • “Utilizando pointer2 o pointer3 producir el mismo binario” – Eso es no verdadero cuando se realizan matemáticas de puntero. ++pointer2 avanzará el puntero una “fila”. ++pointer3 avanzará el puntero un entero matriz. son de diferentes tipos

    – WhozCraig

    09/04/2014 a las 23:22

  • Tienes razón @WhozCraig, mi oración es incorrecta. Gracias por los comentarios 🙂 Lo cambiaré. Salud 😉

    – oHo

    10 de abril de 2014 a las 8:10

avatar de usuario
Ferdinand Beyer

Ambos ejemplos son equivalentes. Sin embargo, el primero es menos obvio y más “hacky”, mientras que el segundo establece claramente tu intención.

int (*pointer)[280];
pointer = tab1;

pointer apunta a una matriz 1D de 280 enteros. En su tarea, en realidad asigna la primera fila de tab1. Esto funciona ya que implícitamente puede convertir matrices en punteros (al primer elemento).

cuando estas usando pointer[5][12]C trata pointer como una matriz de matrices (pointer[5] es de tipo int[280]), entonces hay otro implícito cast aquí (al menos semánticamente).

En su segundo ejemplo, crea explícitamente un puntero a una matriz 2D:

int (*pointer)[100][280];
pointer = &tab1;

La semántica es más clara aquí: *pointer es una matriz 2D, por lo que debe acceder a ella usando (*pointer)[i][j].

Ambas soluciones usan la misma cantidad de memoria (1 puntero) y lo más probable es que se ejecuten igual de rápido. Debajo del capó, ambos punteros incluso apuntarán a la misma ubicación de memoria (el primer elemento del tab1 matriz), y es posible que su compilador incluso genere el mismo código.

La primera solución es “más avanzada”, ya que se necesita una comprensión bastante profunda de cómo funcionan las matrices y los punteros en C para comprender lo que está sucediendo. La segunda es más explícita.

  • Gracias por tu respuesta, Fernando. Estoy de acuerdo contigo: la segunda solución parece más explícita. Continuaré usando este.

    – Crisotribax

    11 de febrero de 2013 a las 10:16

avatar de usuario
Jeyaram

int *pointer[280]; //Crea 280 punteros de tipo int.

En OS de 32 bits, 4 bytes para cada puntero. entonces 4 * 280 = 1120 bytes.

int (*pointer)[100][280]; // Crea solo un puntero que se usa para apuntar una matriz de [100][280] enteros

Aquí sólo 4 bytes.

Llegando a tu pregunta, int (*pointer)[280]; y int (*pointer)[100][280]; son diferentes aunque apunta a la misma matriz 2D de [100][280].

Porque si int (*pointer)[280]; se incrementa, luego apuntará a la siguiente matriz 1D, pero donde como int (*pointer)[100][280]; cruza toda la matriz 2D y apunta al siguiente byte. Acceder a ese byte puede causar problemas si esa memoria no pertenece a su proceso.

  • hola de nuevo, revise mi primera solución: anteriormente olvidé los paréntesis en la creación del puntero. se disculpa

    – Crisotribax

    11 de febrero de 2013 a las 9:29

Ok, esta es en realidad cuatro preguntas diferentes. Los abordaré uno por uno:

son ambos iguales para el compilador? (velocidad, rendimiento…)

Si. La desreferenciación del puntero y el decaimiento del tipo int (*)[100][280] para int (*)[280] siempre es un noop para su CPU. De todos modos, no dejaría pasar un mal compilador para generar código falso, pero un buen compilador de optimización debería compilar ambos ejemplos exactamente con el mismo código.

¿Una de estas soluciones consume más memoria que la otra?

Como corolario a mi primera respuesta, no.

¿Cuál es el más utilizado por los desarrolladores?

Definitivamente la variante sin el extra. (*pointer) desreferenciación Para los programadores de C, es una segunda naturaleza asumir que cualquier puntero puede ser en realidad un puntero al primer elemento de una matriz.

¿Cuál es la mejor manera, la 1ª o la 2ª?

Eso depende de lo que optimice para:

  • El código idiomático usa la variante 1. A la declaración le falta la dimensión exterior, pero todos los usos son exactamente como un programador de C espera que sean.

  • Si desea que quede explícito que está apuntando a una matriz, puede usar la variante 2. Sin embargo, muchos programadores de C experimentados pensarán que hay una tercera dimensión escondida detrás de la más interna. *. Al no tener una dimensión de matriz allí, la mayoría de los programadores se sentirán extraños.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad