1

Necesito pasar un array bidimensional de integers a una función void para poder trabajar dentro de la función como si fuese un array unidimensional. Después, lo que haga con el array en esa función debe conservarse en el array bidimensinal. Tengo bastante claro que para eso se han de usar punteros, pero de momento soy muy nuevo y no se exactamente como van. Mi idea es poner aquí lo que he hecho y que alguien me diga (si es tan amable) porqué esta mal y como arreglarlo.

Esto seria el function.cpp

 #include<stdio.h>
 #include<string.h>
 #include<stdlib.h>
 #include<time.h>
 #include "header.h"
   void squared_map_creation(int* map, int length)
{
   int counter = 0;
   while (counter < length*length)
   {
    map[counter] = counter;
    counter++;
   }
}

Y esto el header.h

void squared_map_creation(int*, int length);

Al ejecutar el programa, el array pasa por la función y sale como si nada, no veo que modifique ningún valor en ningún momento, ni siquiera dentro del array. Mi objetivo es coseguir una función que me haga todos los elementos del array distintos entre sí, en el caso de la función que he creado serian del 0 al 99, y el array bidimensional deberia quedar así:

0   1  2  3  4  5  6  7  8  9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
............................
............................
.............................
...........................99

Muchas gracias :)

Este es el código que llama la función:

https://drive.google.com/open?id=0B8KEzejRagk8VWJLb182dkNpOGM

Skyway
  • 43
  • 6
  • Tu código es correcto. Estas pasando los argumentos correctos a tu función ?? – Trauma Jan 28 '17 at 04:47
  • Deberías añadir el código que llama a la función... Para poder ver también como es la matriz bidimensional y como llamas a la función – eferion Jan 28 '17 at 10:31
  • Vale, ahora edito la publicación y lo pongo – Skyway Jan 28 '17 at 14:01
  • Vale gente, ya descubrí el problema!! Al llamar la función, en lugar de poner el primer elemento del array ponia el último, simplemente tube que cambiar esto: quared_map_creation(&mapa[10][10], 10); por esto squared_map_creation(&mapa[0][0], 10); – Skyway Jan 28 '17 at 14:17

1 Answers1

3

En C++ (y seguro que en C también) los arreglos multi-dimensionales están alojados en memoria de manera anexa, así que la conversión de varias dimensiones a una dimensión es posible y segura.

No he encontrado la sección del estándar de C++ que indica mi afirmación anterior, pero queda implícitamente confirmado en la siguiente nota (la traducción es mía):

8.3.4 Arreglos

...

  1. Se sigue una norma consistente en los arreglos multidimensionales. Si E es un arreglo de n-dimensiones de rango i × j × ... × k, entonces E usado en una expresión sujeta a la conversión arreglo-a-puntero (4.2) será convertido a un puntero a un arreglo de (n-1) dimensiones con un rango j × ... × k. Si el operador * es aplicado a este puntero de manera implícita o explícita como resultado de sub-indizar, el resultado es un puntero al arreglo de (n-1) dimensiones, que a su vez será convertido inmediatamente a puntero.
  2. [Ejemplo: consideremos

    int x[3][5];
    

    Aquí x es un arreglo de 3 × 5 enteros. Cuando x aparecie en una expresión, es convertido a puntero a (los tres primeros) arreglo de enteros de cinco elementos. En la expresión x[i] que es equivalente a *(x+i), x es convertido en primer lugar a puntero tal y como se describió; entonces x+i es convertido al tipo de x, que implica multiplicar i por la longitud del objeto al que el puntero apunta, específicamente cinco objetos de tipo entero. El resultado se suma y las indirecciones se aplican para devolver un arreglo (de cinco enteros), que a su vez es convertido a un puntero al primero de los enteros. Si hay otra dimensión el mismo proceso se aplica de nuevo; en esta coasión el resultado es un entero. - fin del ejemplo]

Tal y como se describen las operaciones para acceder a los elementos de un arreglo multi-dimensional, se entiende que toda la memoria es anexa y en consecuencia un arreglo multidimensional puede ser tratado como uno de una sola dimensión.

Pasando arreglos a funciones.

Al ejecutar el programa, el array pasa por la función y sale como si nada, no veo que modifique ningún valor en ningún momento

Has cometido un error típico de principiante en el mundo de los punteros. Cuando una función recibe un puntero, lo que la función está recibiendo es una copia del puntero, así que los cambios se aplicarán sobre la copia, no sobre el original. Para modificar el puntero original debes pasar un puntero al puntero o una referencia al mismo:

void squared_map_creation_pointer_2_pointer(int** map, int length)
{
    int counter = 0;
    while (counter < length*length)
    {
        (*map)[counter] = counter;
        counter++;
    }
}

En la versión puntero-a-puntero, necesitamos des-referenciar el puntero map (usar el operador unario *) una vez para acceder al contenido original (no la copia), de ahí que se use la instrucción (*map)[counter] = counter;. Yo personalmente prefiero la versión con referencia:

void squared_map_creation_reference_2_pointer(int*& map, int length)
{
    int counter = 0;
    while (counter < length*length)
    {
        map[counter] = counter;
        counter++;
    }
}

En este caso no hay que des-referenciar el puntero, ya que tenemos una referencia al puntero original. Puedes usar estas funciones de la siguiente manera:

int main()
{
    int datos[10][10] {{}};
    int *begin = *datos;

    // La llamada puntero-a-puntero necesita obtener la direccion
    // del puntero al inicio del arreglo.
    squared_map_creation_pointer_2_pointer(&begin, 10);

    // En la llamada referencia-a-puntero se puede pasar el puntero
    // directamente, sin operaciones adicionales.
    squared_map_creation_reference_2_pointer(begin, 10);

    return 0;
}

Alternativa con plantillas.

Puedes usar plantillas de C++ para el mismo propósito:

template <std::size_t LENGTH>
void squared_map_creation(int (&map)[LENGTH][LENGTH])
{
    std::size_t counter = 0;
    int* begin = *map;
    while (counter < LENGTH*LENGTH)
    {
        begin[counter] = counter;
        counter++;
    }
}

En este caso la función puede ser llamada así:

int main()
{
    int datos[10][10] {{}};

    squared_map_creation(datos);

    return 0;
}

La función recibe una referencia a un arreglo de dos dimensiones, el tipo del parámetro map es int (&)[10][10] que como ya se ha mencionado es de tipo referencia (&) a arreglo de dos dimensiones, ambas de 10 elementos ([10][10]) de enteros (int); dado que es una referencia no se necesitan operadores adicionales para pasar el arreglo a la función; la plantilla se encarga de deducir el tamaño del arreglo y por eso tampoco es necesario pasar el tamaño a la función.

PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82
  • Wow, muchas gracias. Al final he usado algo similar a una plantilla, el caso es que ya me funciona bien, pero me parecen interesantes todas las demás opciones – Skyway Jan 31 '17 at 20:44