¿Cómo usar un array bidimensional como variable de una función en C?

publicado por: Anonymous

Estoy usando un método que tiene como parámetro de entrada un array bidimensional en C. Para el test del método uso arrays bidimensionales de diferente tamaño.
¿Cómo puedo puedo implementarlo?

Mi cabecera sería:

bool problema1(int m[][])

Obtengo el siguiente error en la cabecera:

array type has incomplete element type ‘int[]’.

solución

¿Cómo usar un array bidimensional como variable de una función en C?

Si vas a usar un parámetro como matriz, debes especificar de forma obligatoria, el tamaño de columnas de la matriz.

Ejemplo del error:

bool problema1(int m[][])
{
  //code
}

Dará error de compilación, porqué el parámetro m de la función problema1 no se ha especificado el tamaño de columnas de la matriz.

Solución:

bool problema1(int m[][4])
{
  //code
}

Ya no debería dar error de compilación, porqué hemos especificado el valor de la segunda dimensión.

Sin embargo, esta función solamente me servirá para matrices que tengan como máximo 4 columnas.

Es decir, si llegara hacer esto:

void imprimir(int filas, int columnas, int p[][4])
{
    for(int i = 0; i != filas; ++i)
    {
        for(int j = 0; j != columnas; ++j)
            printf("%dn", p[i][j]);
    }   
}

int main(void)
{
    int m[2][2] =
    {
        {1, 2},
        {3, 4}
    };
    int m2[2][4] =
    {
        {10, 11, 12, 13},
        {14, 15, 16, 17}
    };
    imprimir(2, 2, m);
    imprimir(2, 4, m2);
    return 0;
}

No me dará error de compilación, pero tendré problemas en tiempo de ejecución.

Si compilamos el código de arriba y lo ejecutamos, me sale el siguiente resultado:

1
2
-342343
-243423
10
11
12
13
14
15
16
17

Mi programa no dejó de funcionar, sin embargo, dos elementos de la matriz m (de la segunda fila) no se han mostrado correctamente.

¿Por qué sucedió este error?

La respuesta es sencilla. Pues como la función imprimir tiene un parámetro que es de tipo matriz, solo podrá recibir matrices que tengan como máximo 4 columnas. La matriz que hemos pasado como referencia al momento de llamar a la función, ha tenido como tamaño 2 columnas y debería ser 4.

¿Cómo comprobamos este error?

Imaginemos que las direcciones de memoria de cada elemento de las matrices m y m2 son:

-> Matriz m:
|0x4 |0x8|
|0x12|0x16|

-> Matriz m2
|0x32|0x36|0x40|0x44|
|0x48|0x52|0x56|0x60|

Ahora, a partir del gráfico de arriba, analicemos esta línea de código:

printf("%dn", p[i][j]);

Primero que nada, la expresión p[i][j] en realidad el compilador lo entiende de esta manera:

*( (p + i*4*N) + (j* 4))

Es decir, para imprimir el dato de X fila con respecto a X columna, necesitamos calcular su dirección de memoria y esto se logra, con la expresión aritmética de arriba. Donde p en realidad es un puntero base que guarda la dirección de memoria del primer elemento de la primera fila de la matriz m (en este caso) y N sería el tamaño de columnas de dicha matriz (en nuestro caso es 4, porqué en el parámetro de la función imprimir lo hemos especificado de esa forma).

Entonces, comencemos a evaluar esa expresión aritmética, cuando i = 1; j = 0, la expresión estaría evaluada de esta manera:

p = 0x4
*( (0x4 + 1*4*4) + (0* 4))
//Da como resultado:
*(0x20)

O sea, con el asterisco * estaríamos accediendo a la dirección de memoria 0x20, pero luego nos damos cuenta, que esa NO ES LA DIRECCIÓN DE MEMORIA del primer elemento de la segunda fila de la matriz m, ya que, en realidad debería ser 0x12. Entonces, con esto concluimos que nos imprime un valor basura porqué el dato real está en la dirección 0x12. A esto se lo conoce como desbordamiento de búfer, hemos desbordado la matriz y por ende, estaríamos accediendo a otro espacio de memoria del propio programa (sino fuera del mismo programa, hace rato la aplicación dejaría de funcionar, el sistema operativo no dejará que accedas a una dirección que al programa no le pertenece) y por esa razón, nos imprime ese resultado.

¿Hay alguna solución a este problema?

Si la hay. Debes usar una expresión aritmética de puntero para poder lograr que la función pueda manejar matrices estáticas de diferentes tamaño.

En vez de usar como parámetro int p[][4], lo cambiamos a: int* p.

Ejemplo:

void imprimir(int filas, int columnas, int* p)
{
    for(int i = 0; i != filas; ++i)
    {
        for(int j = 0; j != columnas; ++j)
            printf("%dn", *( (p + i* columnas) + j));
    }   
}

Básicamente el parámetro p recibirá la dirección base de una matriz y con eso podemos calcular cualquier dirección de memoria de algún elemento de una matriz.

Al momento de llamar la función, lo debemos hacer de esta manera:

imprimir_version2(2, 2, m[0]);
imprimir_version2(2, 4, m2[0]);

Y con esta solución, podremos imprimir matrices de tipo Integer con diferentes tamaños.

Ahora, si ves la expresión muy tediosa para estar escribiéndola a cada rato, podrías hacer uso de una macro:

#define ma(i, j) *( (p + i* columnas) + j)

Y la usamos de esta manera:

printf("%dn", ma(i, j));

El código completo, quedaría de esta forma:

#define ma(i, j) *( (p + i* columnas) + j)

void imprimir(int filas, int columnas, int* p)
{
    for(int i = 0; i != filas; ++i)
    {
        for(int j = 0; j != columnas; ++j)
            printf("%dn", ma(i,j));
    }   
}

int main(void)
{
    int m[2][2] =
    {
        {1, 2},
        {3, 4}
    };
    int m2[2][4] =
    {
        {10, 11, 12, 13},
        {14, 15, 16, 17}
    };
    imprimir(2, 2, m);
    imprimir(2, 4, m2);
    return 0;
}
Respondido por: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *