Punteros como parámetros de funciones

Esto ya lo hemos dicho anteriormente, pero no está de más repetirlo: los punteros son objetos como cualquier otro en C++, por lo tanto, tienen las mismas propiedades y limitaciones que el resto de los objetos.

Cuando pasamos un puntero como parámetro de una función por valor pasa lo mismo que con cualquier otro objeto.

Dentro de la función trabajamos con una copia del parámetro, que en este caso es un puntero. Por lo tanto, igual que pasaba con el ejemplo anterior, las modificaciones en el valor del parámetro serán locales a la función y no se mantendrán después de retornar.

Sin embargo, no sucede lo mismo con el objeto apuntado por el puntero, puesto que en ambos casos será el mismo, ya que tanto el puntero como el parámetro tienen como valor la misma dirección de memoria. Por lo tanto, los cambios que hagamos en los objetos apuntados por el puntero se conservarán al abandonar la función.

Ejemplo:

#include <iostream>
using namespace std;
 
void funcion(int *q);
 
int main() { 
   int a; 
   int *p;
 
   a = 100; 
   p = &a; 
   // Llamamos a funcion con un puntero 
   funcion(p); // (1)
   cout << "Variable a: " << a << endl; 
   cout << "Variable *p: " << *p << endl; 
   // Llamada a funcion con la dirección de "a" (constante) 
   funcion(&a);  // (2)
   cout << "Variable a: " << a << endl; 
   cout << "Variable *p: " << *p << endl; 
   
   return 0; 
}
 
void funcion(int *q) { 
   // Cambiamos el valor de la variable apuntada por 
   // el puntero 
   *q += 50; 
   q++; 
}

Dentro de la función se modifica el valor apuntado por el puntero, y los cambios permanecen al abandonar la función. Sin embargo, los cambios en el propio puntero son locales, y no se conservan al regresar.

Analogamente a como lo hicimos antes al pasar una constante literal, podemos pasar punteros variables o constantes como parámetro a la función. En (1) usamos un variable de tipo puntero, en (2) usamos un puntero constante.

De modo que con este tipo de declaración de parámetro para función estamos pasando el puntero por valor. ¿Y cómo haríamos para pasar un puntero por referencia?:

void funcion(int* &q);

El operador de referencia siempre se pone junto al nombre de la variable.

En esta versión de la función, las modificaciones que se hagan para el valor del puntero pasado como parámetro, se mantendrán al regresar al punto de llamada.

Nota: En C no existen referencias de este tipo, y la forma de pasar parámetros por referencia es usar un puntero. Por supuesto, para pasar una referencia a un puntero se usa un puntero a puntero, etc.
La idea original de la implementación de referencias en C++ no es la de crear parámetros variables (algo que existe, por ejemplo, en PASCAL), sino ahorrar recursos a la hora de pasar como parámetros objetos de gran tamaño.
Por ejemplo, supongamos que necesitamos pasar como parámetro a una función un objeto que ocupe varios miles de bytes. Si se pasa por valor, en el momento de la llamada se debe copiar en la pila todo el objeto, y la función recupera ese objeto de la pila y se lo asigna al parámetro. Sin embargo, si se usa una referencia, este paso se limita a copiar una dirección de memoria.
En general, se considera mala práctica usar referencias en parámetros con el fin de modificar su valor en la función. La explicación es que es muy difícil, durante el análisis y depuración, encontrar errores si no estamos seguros del valor de los parámetros después de una llamada a función. Del mismo modo, se complica la actualización si los valores de ciertas variables pueden ser diferentes, dependiendo dónde se inserte nuevo código.

Arrays como parámetros de funciones

Cuando pasamos un array como parámetro en realidad estamos pasando un puntero al primer elemento del array, así que las modificaciones que hagamos en los elementos del array dentro de la función serán permanentes aún después de retornar.

Sin embargo, si sólo pasamos el nombre del array de más de una dimensión no podremos acceder a los elementos del array mediante subíndices, ya que la función no tendrá información sobre el tamaño de cada dimensión.

Para tener acceso a arrays de más de una dimensión dentro de la función se debe declarar el parámetro como un array. Ejemplo:

#include <iostream>
using namespace std;
 
#define N 10 
#define M 20
 
void funcion(int tabla[][M]); 
// recuerda que el nombre de los parámetros en los 
// prototipos es opcional, la forma: 
// void funcion(int [][M]); 
// es válida también.
 
int main() { 
   int Tabla[N][M]; 
...
   funcion(Tabla); 
...
   return 0; 
}
 
void funcion(int tabla[][M]) { 
...
   cout << tabla[2][4] << endl;
...
}

Otro problema es que, a no ser que diseñemos nuestra función para que trabaje con un array de un tamaño fijo, en la función nunca nos será posible calcular el número de elementos del array.

En este último ejemplo, la tabla siempre será de NxM elementos, pero la misma función admite como parámetros arrays donde la primera dimensión puede tener cualquier valor. El problema es cómo averiguar cual es ese valor.

El operador sizeof no nos sirve en este caso, ya que nos devolverá siempre el tamaño de un puntero, y no el del array completo.

Por lo tanto, deberemos crear algún mecanismo para poder calcular ese tamaño. El más evidente es usar otro parámetro para eso. De hecho, debemos usar uno para cada dimensión. Pero de momento veamos cómo nos las arreglamos con una:

#include <iostream>
using namespace std;
 
#define N 10 
#define M 20
 
void funcion(int tabla[][M], int n); 
 
int main() { 
   int Tabla[N][M];
   int Tabla2[50][M];

   funcion(Tabla, N); 
   funcion(Tabla2, 50); 
   return 0; 
}
 
void funcion(int tabla[][M], int n) { 
   cout << n*M << endl;
}

Generalizando más, si queremos que nuestra función pueda trabajar con cualquier array de dos dimensiones, deberemos prescindir de la declaración como array, y declarar el parámetro como un puntero. Ahora, para acceder al array tendremos que tener en cuenta que los elementos se guardan en posiciones de memoria consecutivas, y que a dos índices consecutivos de la dimensión más a la derecha, le corresponden posiciones de memoria adyacentes.

Por ejemplo, en un array declarado como int tabla[3][4], las posiciones de tabla[1][2] y tabla[1][3] son consecutivas. En memoria se almacenan los valores de tabla[0][0] a tabla[0][3], a continuación los de tabla[1][0] a tabla[1][3] y finalmente los de tabla[2][0] a tabla[2][3].

Si sólo disponemos del puntero al primer elemento de la tabla, aún podemos acceder a cualquier elemento, pero tendremos que hacer nosotros las cuentas. Por ejemplo, si "t" es un puntero al primer elemento de tabla, para acceder al elemento tabla[1][2] usaremos la expresión t[1*4+2], y en general para acceder al elemento tabla[x][y], usaremos la expresión t[x*4+y].

El mismo razonamiento sirve para arrays de más dimensiones. En un array de cuatro, por ejemplo, int array[N][M][O][P];, para acceder al elemento array[n][m][o][p], siendo "a" un puntero al primer elemento, usaremos la expresión: a[p+o*P+m*O*P+n*M*O*P] o también a[p+P*(n+m+o)+O*(m+n)+M*n].

Por ejemplo:

#include <iostream>
using namespace std;
 
#define N 10 
#define M 20
#define O 25
#define P 40
 
void funcion(int *tabla, int n, int m, int o, int p); 
 
int main() { 
   int Tabla[N][M][O][P];
   
   Tabla[3][4][12][15] = 13;
   cout << "Tabla[3][4][12][15] = " << 
     Tabla[3][4][12][15] << endl;
   funcion((int*)Tabla, N, M, O, P); 
   return 0; 
}
 
void funcion(int *tabla, int n, int m, int o, int p) { 
   cout << "tabla[3][4][12][15] = " <<
     tabla[3*m*o*p+4*o*p+12*p+15] << endl;
}

Estructuras como parámetros de funciones

Las estructuras también pueden ser pasadas por valor y por referencia.

Las reglas se les aplican igual que a los tipos fundamentales: las estructuras pasadas por valor no conservarán sus cambios al retornar de la función. Las estructuras pasadas por referencia conservarán los cambios que se les hagan al retornar de la función.

En el caso de las estructuras, los objetos pueden ser muy grandes, ocupando mucha memoria. Es por eso que es frecuente enviar referencias como parámetros, aunque no se vayan a modificar los valores de la estructura. Esto evita que el valor del objeto deba ser depositado en la pila para ser recuperado por la función posteriormente.

Funciones que devuelven referencias

También es posible devolver referencias desde una función, para ello basta con declarar el valor de retorno como una referencia.

Sintaxis:

<tipo> &<identificador_función>(<lista_parámetros>);

Esto nos permite que la llamada a una función se comporte como un objeto, ya que una referencia se comporta exactamente igual que el objeto al que referencia, y podremos hacer cosas como usar esa llamada en expresiones de asignación. Veamos un ejemplo:

#include <iostream>
using namespace std;

int &Acceso(int*, int);

int main() {
   int array[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
   
   Acceso(array, 3)++;
   Acceso(array, 6) = Acceso(array, 4) + 10;
   
   cout << "Valor de array[3]: " << array[3] << endl;
   cout << "Valor de array[6]: " << array[6] << endl;
   
   return 0;
}

int &Acceso(int* vector, int indice) {
   return vector[indice];
}

Este uso de las referencias es una herramienta muy potente y útil que, como veremos más adelente, tiene múltiples aplicaciones.

Por ejemplo, veremos en el capítulo sobre sobrecarga que este mecanismo es imprescindible.

Comentarios de los usuarios (25)

Jorge Luis Sala
2011-02-02 10:14:51

Hola Amigo. Primero agradecer por la excelente ayuda que brindas de manera desinteresada en el sitio. Es de verdad invaluable y digno de reconocer el esfuerzo. Ahora me gustaría si pudieras aclararme una duda (de entre algunas que tengo) con este asunto de los punteros.

void funcion(int *tabla, int n, int m, int o, int p);

Aquí declaramos el prototipo de una función que recibe cinco parámetros: un puntero a un entero y otros cuatro enteros. Si mal no seguí la explicación el puntero almacenará la dirección de memoria del primer elemento de un array de enteros, y el resto de los parámetros indicarán el tamaño de cada una de las cuatro dimensiones del mismo ¿Correcto?

Entonces, si habíamos visto que un puntero no contiene más que una dirección de memoria de un tipo de objeto específico:

int n = 0;
int *pInt = NULL;

pInt = &n; //Asignamos al puntero la dirección del objeto
*pInt = 15; //Asignamos al objeto apuntado un valor

No me queda clara esta llamada de la función:

funcion((int*)Tabla, N, M, O, P);

Sabiendo que Tabla es un objeto array de enteros de cuatro dimensiones ¿Por qué se realiza un cast del objeto en lugar de pasarle la dirección de memoria del mismo? De esta forma:

funcion(&Tabla, N, M, O, P);

¿Y qué significado tiene el siguiente código?

int vector[5] = {1,2,3,4,5};
int *pVector;
int *pInt;

pVector = &vector; //El puntero guarda la dirección de memoria que contiene el valor 1

pVector++; //Aquí pasaría a apuntar a vector[1] o al valor 2

pInt = (int*)vector; //¿Y en este caso qué almacenaría?

Con este ejemplo particular tengo dudas concretas (con anteriores meras sospechas por ahora XD ).

Eso es todo, vuelvo a agradecer todo el tiempo invertido en el sitio y los diferentes cursos. Espero nunca pierdan el entusiasmo.

Un abrazo grande desde Argentina.

Steven
2011-02-02 11:26:52

Hola Jorge Luis,

Tu primer comentario es correcto acerca de 'funcion()'.

En cuanto a tu primera duda, necesitamos hacer un cásting, porque el tipo del primer parámetro en la invocación de 'funcion()' no coincide con el tipo del primero del prototipo. El primer parámetro de 'funcion()' espera un puntero a 'int', mientras que en la invocación de 'funcion()' el primer parámetro es de tipo 'int[10][20][25][40]'.

Sin embargo, como ya sabemos, hay una correlación entre arrays y punteros, ya que involucran direcciones de memoria. Como un array ES la dirección de memoria de su primero elemento, entonces lo único que tenemos que hacer es indicar al compilador que lo trate como un puntero. Por eso, hacemos un cásting. Si lo prefieres, puedes hacer esto:

funcion( &Tabla[0][0][0][0], N, M, O, P);

La siguiente invocación que hiciste:

funcion(&Tabla, N, M, O, P);

no tiene sentido si tienes en cuenta lo que te acabo de comentar: 'Tabla' ES una dirección de memoria. Además, este tipo y el esperado siguen sin concordar. Recuerda que C++ es un lenguaje FUERTEMENTE tipificado.

Veamos las otras dudas:

pVector = &vector; //El puntero guarda la dirección de memoria que contiene el valor 1

Correcto.

pVector++; //Aquí pasaría a apuntar a vector[1] o al valor 2

Correcto.

pInt = (int*)vector; //¿Y en este caso qué almacenaría?

Esto es parecido al asunto anterior. Como 'vector' es un array y un array ES una dirección de memoria, entonces 'pInt' apunta al primer elemento de 'vector' - el número 1.

Espero haber aclarado todas las dudas.

Steven

Jorge Luis Sala
2011-02-02 22:45:32

Muchas gracias por la pronta respuesta amigo Steven.

Ahora me queda clarísimo el asunto. Mi confusión residía en que yo no había entendido la diferencia entre un objeto "int" y otro objeto "array" pensando que se trataban de la misma manera. En algún capítulo anterior había visto un ejemplo por el estilo:

int lista[5] = {1,2,3,4};
int *pLista = NULL;

pLista = &lista[0];
pLista = lista; //Igual al anterior

A esas altas horas de la madrugada no había reparado al leer que no era "&lista" la última línea sino simplemente "lista", sin el operador, y ahí caí en la confusión de no comprender que los arrays son esencialmente un puntero. Ahora, una pequeña duda que me surgió al repasar este ejemplo es por qué en este caso no se hizo un casting al asignar el array int al puntero int. Sólo eso.

Gracias de nuevo y hasta pronto.

Steven
2011-02-02 23:13:39

Hola Jorge Luis,

No hay de qué; para eso estamos :)

Como estás aprendiendo, sugiero no pensar que un array es esencialmente un puntero, sino más bien que un array es una dirección de memoria al igual que el valor de un puntero. Por esta razón, existe una correlación entre arrays y punteros: son esencialmente direcciones de memoria.

Una vez que tengas claros estos conceptos, entonces lo que sí puedes decir y pensar es que un array es TRATADO como un puntero constante. Por esto, no podemos modificar un array (pero sí su contenido). Por ejemplo,

pLista = lista;  // Correcto
lista = pLista;  // Incorrecto: lista es un array (puntero constante)

En cuanto a tu duda, no es necesario un cásting, porque el lenguaje permite la asignación de un array a un puntero (del mismo tipo) porque, como he dicho antes, existe una relación estrecha entre arrays y punteros, ya que involucran direcciones de memoria. De igual manera, puedes decir que un array es - tratado como - un puntero constante a algo. En el ejemplo anterior,

pLista = lista;

Los tipos no concuerdan exactamente, pero son MUY parecidos. Semánticamente, tenemos lo siguiente:

<int *> = <int [5]>

Nuevamente, como un array puede ser tratado como un puntero constante, entonces tenemos el siguiente esquema semántico:

<int *> = <int *>

Obviamente, esto funciona porque tanto el puntero como el array trata de elementos de tipo 'int'. Si esto no fuere el caso, entonces sí tendríamos que ser explícitos con la conversión.

Esta conversión implícita es parecida a la que existe con los tipos numéricos. Por ejemplo,

short int num1 = 25;
int num2 = 30;

num2 = num1;

Los tipos no concuerdan, pero existe una correlación entre 'int' y 'short int'. No habría ningún problema al convertir de 'short int' a 'int', ya que 'int' ocupa más espacio en memoria que 'int' y por tanto no hay pérdida de datos en la conversión; por ello se llama "promoción". Eso sí, yendo de 'int' a 'short int', sería problemático, por lo que no existe una conversión implícita.

Espero que vayamos aclarando las dudas.

Steven

Jorge Luis Sala
2011-02-02 23:28:33

Como decimos por acá: Más claro, échale agua.

Gracias por la paciencia y el tiempo, seguiré con el curso que todavía me faltan unos 30 capítulos.

Saludos.

Esteban López
2011-02-18 17:59:04

En primer lugar muchísimas gracias por su sitio web, tan útil y bien gestionado.

Me gustaría hacer una consulta general acerca de las ventajas de los punteros frente a los arrays. Voy a trabajar en simulaciones de ingeniería y básicamente trabajamos con matrices de datos (unidimensionales o bidimensionales) que exportamos o importamos a distintas funciones.

En los programas de mis colegas siempre he visto que esto lo solucionan con punteros con lo que, hasta este tema del curso, creí a que era debido a que las funciones no admitían una matriz como parámetro. No obstante en este capítulo 5 se muestra como esto no es así, lo que hace cuestionarme las ventajas reales de usar punteros frente a arrays.

Muchas gracias

Steven
2011-02-18 19:13:33

Hola Esteban,

A la hora de pasar un array como parámetro a una función, ésta acepta una dirección de memoria, ya que un array técnicamente ES una dirección de memoria. Desde el punto de vista de la función, un parámetro que se indica como array realmente es un puntero; de hecho, lo trata como un puntero constante. Por ejemplo,

void func( int lista[] );

y

void func( int *lista );

vienen a ser lo mismo. En el caso del array, C++ lo trata como un puntero que no puede ser modificado; o sea, apunta siempre al mismo elemento. Esto se hace para que el comportamiento del puntero sea igual al del array; no puedes modificar un array, pero sí su contenido.

Ahora bien, en el caso de pasar arrays multidimensionales, entonces no podemos intercambiarlos por punteros en la lista de parámetros de una función. Esto es porque un array multidimensional técnicamente es un bloque de memoria contigua y por tanto todos sus elementos aparecen contiguos en memoria. Esto implica que un array multidimensional se organiza de igual manera que un array unidimensional. Debido a esta organización, sí podemos pasar un array multidimensional a una función que acepte un puntero (unidimensional). Por ejemplo,

void func( double *pDatos );
...
double tabla[100][200][300];

func( &tabla[0][0][0] );

No podríamos usar un trible puntero en este caso, porque la información guardada en 'tabla' no tiene que ver con direcciones de memoria, sino con números de coma flotante (de tipo 'double'). Es decir, 'tabla' no contiene punteros a punteros a punteros, como un triple puntero. Lo siguiente no funcionaría:

void func( double ***pDatos );
...
double tabla[100][200][300];

func( tabla );  // error

Lo correcto sería esto:

void func( double datos[][200][300] );
...
double tabla[100][200][300];

func( tabla );

Como la primera dimensión de 'datos' no es necesaria, podríamos optar por usar un puntero; esto es,

void func( double *pDatos[100][200] );
...
double tabla[100][200][300];

func( tabla );

El problema de todo esto surge cuando queremos un array multidimensional de dimensiones (cantidades) variables. Esto suele implicar el uso de memoria dinámicamente adjudicada. En este caso, es imprescindible usar punteros. Por lo tanto, la función que acepte tal array dinámico multidimensional debe entender la organización de la información - los elementos. Puedes consultar el capctulo 17 para más información acerca de este tema: http://c.conclase.net/curso/?cap=017#inicio

Espero haber aclarado las dudas.

Steven

Sebastián
2011-10-09 05:19:32

Agradezco nuevamente este excelente tutorial!

Creía tenerlo todo claro hasta que leí la consulta de Jorge Luis. Me queda una duda en la linea del comentario:

int vector[5] = {1,2,3,4,5};
int *pVector;
int *pInt;

pVector = &vector; //El puntero guarda la dirección de memoria que contiene el valor 1

Según lo que entendí, los arrays contienen la dirección de memoria del primer elemento, es decir, 'vector' guarda la dirección de memoria que contiene el valor 1, pero '&vector' no sería algo así como la dirección de la dirección que contiene el valor 1?. Ese '&' me produce la confusión.

Muchas gracias de antemano!.

Steven R. Davidson
2011-10-09 12:22:00

Hola Sebastián,

Tienes toda la razón del mundo. Cometí un error en mi comentario. Efectivamente, debería ser:

pVector = vector;

Como bien dices, 'vector' es un array lo cual ya ES una dirección de memoria, la cual se asigna a 'pVector'. Obviamente, no tiene sentido escribir:

&vector

Menos mal que lo aclaraste :)

Disculpen por la confusión que pudiere provocar.

Steven

Sebastián
2011-10-09 18:38:50

Muchas gracias Steven por la pronta respuesta!

Ahora si me a quedado claro. Agradezco que estés siempre dispuesto a resolver nuestras dudas, es de gran ayuda para todos los que estamos empezando en esto. Muchas gracias! :D

Carlos
2012-01-27 18:40:17

Hola, en la parte de usar solo un apuntador para apuntar un elemento en un array de dos dimensiones aparece esta forma de apuntarlo t[x*4+y]. No entiendo por que el 4. Yo diria que es t[x*y+y]. Bueno es muy probable que este equivocado, pero solo quiero saber el porque del 4.

Gracias y en verdad muy buena labor la que realizan.

Steven R. Davidson
2012-01-27 19:18:38

Hola Carlos,

El 4 aparece porque se trata de una tabla de 3 filas x 4 columnas representada así,

int tabla[3][4];

que realmente se trata de un array de 3 elementos, que cada uno es un array de 4 elementos, los cuales son enteros de tipo 'int'. Por lo tanto, tenemos que "saltar" cada array de 4 'int'. Consecuentemente, escribimos:

int *t = &tabla[0][0];
...
t[x*4 + y];

que en general el cálculo del índice sería, "x*MAX_COLS + y".

Espero haber aclarado la duda.

Steven

Carlos
2012-01-30 16:04:17

Si, muchas gracias ahora quedo claro

Salvador
2012-04-19 23:31:00

Quisiera ver un ejemplo de una función con arrays... Pero que este pueda recocer las primeras 6 letras del abecedario y que lo traduzca a clave morse... Desde ya se los agradezco!!

Patricia
2012-10-24 04:44:37

Buenas noches,

Al autor del blog tengo que felicitarlo, de verdad gracias por compartir sus valioso conocimientos, a pesar que esta publicación tiene mas de 1 año.

Tengo la siguiente duda con pase de arreglo de mas de 1 dimensión por la referencia, no se como leerlos desde 1 "for" en la función que recibo a través de punteros.

Este es mi codigo:

#include <iostream>

using namespace std

void ordenar(int *tabla, int n, int y);

int main(){

int arr[1][2];

int n = 1;

int y = 2;

cout << "Por favor introduzca 12 numeros"<<endl;

for(int i=0; i<(n+1); i++){

for(int j=0; j<(y+1); j++){

cout << "Posicion: "<<i<<" - "<<j<<endl;

cin >> arr[i][j];

}

}

ordenar((int*) arr,1,2);

}

void ordenar(int *parr, int n, int y){

cout <<parr[1*4+2]<<endl;

for(int i=0; i<(n+1); i++){

for(int j=0; j<(y+1); j++){

//cout <<parr[n*M]<<endl;

cout << "Posicion: "<<i<<" - "<<j<<endl;

}

}

}

Por favor si me pueden guiar para saber que estoy haciendo mal, gracias.

Héctor
2012-11-27 02:57:35

Hola Patricia, me tomé la molestia de corregir tu código. Verás, tu arreglo lo estabas declarando de tamaño [1][2] y en tus for's estabas haciendo ciclos mayores a ese tamaño, así que cambié el "<(n+1)" y el "<(y+1)" por solo "n" y "y", así lo hará del tamaño que debe.

Otro detalle es que tu programa pide 12 números pero en un arreglo de [1][2] solamente caben 2 elementos. En el código corregido cambié el tamaño por [2][4] (para que tuviera un total de 8 elementos).

En tu función "ordenar", mandas el array, pero al mostrarlo con COUT colocabas parr[1*4+2], eso también estaba mal pues el '4' representa la cantidad total de columnas y tenías solo 2, pero como lo corregí ahora el total sí son 4 y lo generalicé con variables así: parr[i*4+j].

Perdona que te cambiaré un poco la sangría, trabajo mejor así como lo puse; también agregué otros COUT's para ver lo que ocurre.

#include <iostream>

using namespace std;

void ordenar(int*, int n, int y);

int main()
{
	int arr[2][4];
	int n = 2;
	int y = 4;

	cout << "Por favor introduzca 8 numeros"<<endl;
	for(int i=0; i<n; i++)
	{
		for(int j=0; j<y; j++)
		{
			cout << "Posicion: "<< i << " - " << j <<endl;
			cin >> arr[i][j];
		}
	}

	ordenar((int*)arr,2,4);

	cout<<"\n*arr:\n";
	for(int i=0; i<n; i++)
	{
		for(int j=0; j<y; j++)
		{
			cout << "Posicion: "<< i << " - " << j << " Valor: " << arr[i][j]<<endl;
		}
	}

	system("pause");
}

void ordenar(int *parr, int n, int y)
{
	cout<<"\n*parr:\n";
	for(int i=0; i<n; i++)
	{
		for(int j=0; j<y; j++)
		{
			cout << "Posicion: "<< i << " - " << j << " Valor: " ;
			cout << parr[i*4+j]<<endl;
		}
	}
	cout<<"\n";
}

Si tienes alguna duda lo comentas.

Saludos

Vanessa
2013-11-08 15:06:04

Buenos dias, desde hace unos dias estoy intentando resolver un error que obtengo en mi código:

Error 1 error C2664: 'simplx' : no se puede convertir el parámetro 1 de 'double **' a 'double [][50]' .

No se como solucionarlo, talvez me puede ayudar con alguna sugerencia?

Steven R. Davidson
2013-11-11 04:09:59

Hola Vanessa,

Primeramente, deberías darnos algo de código fuente para entender mejor el problema que tienes. Sin embargo, en este caso, el mensaje del error nos da suficientes indicios al problema.

Básicamente estás intentando asignar o convertir un puntero doble a un array de dos dimensiones. Esto no es correcto, porque un puntero a un puntero a 'double' no representa la misma organización de datos que un array de un array de 50 'double'.

Un array de dos dimensiones guarda todos sus elementos contiguamente en memoria. De hecho, podríamos tratar este array de dos dimensiones como un array de una sola dimensión. Por ejemplo,

double matriz[10][50];

double *ptr = matriz[0];

y ahora 'ptr' es un puntero a un array de 500 (10x50) elementos. También podríamos hacer esto:

double *ptr = (double *)matriz;

Obviamente, deberíamos tener cuidado a la hora de tratar 'ptr' porque realmente los elementos de 'matriz' deberían ser tratados como una tabla - de dos dimensiones. El cálculo sería sencillamente:

ptr[fila*50 + col]

o directamente,

matriz[fila][col]

En tu caso, quieres hacer la conversión inversa: de un doble puntero a una matriz. Esto depende de cómo organizaste los elementos apuntados por este doble puntero. Si se trata de un array dinámico, entonces debes asegurar que todos los elementos estén agrupados en un solo bloque en memoria. Por ejemplo,

double **pTabla = new double *[10];  // "Directorio" de las filas
pTabla[0] = new double[10*50];  // Creamos todos los elementos en un solo bloque de memoria

// Construimos el acceso 2D a la tabla
for( int i=1; i<10; i++ )
  pTabla[i] = pTabla[i-1] + 50;

Ahora podemos pasar este puntero doble a una matriz. Sin embargo, no existe una conversión directa de 'double *' a 'double [][50]'; el lenguaje no lo permite. La solución es "engañar" a C++ cambiando el tipo apuntado para que concuerde con el tipo esperado del parámetro de la función. Esto es,

void func( double matriz[][50] );
...
func( reinterpret_cast<double (*)[50]>( pTabla[0] ) );

Lo que hemos hecho es convertir un puntero a 'double' a un puntero a 'double [50]'.

Espero que esto te ayude.

Steven

Milton Parra
2014-01-12 23:11:09

Saludos,

Este código devuelve la misma dirección

   cout <<"Tabla: " <<Tabla <<endl; //Direccion1
   cout <<"Tabla[0][0][0][0]: " 
        <<&Tabla[0][0][0][0] <<endl; //Direccion2

Por qué la segunda llamada es un error?

    funcion(&Tabla[0][0][0][0], N, M, O, P); //Caso1 O.K
   //funcion(Tabla, N, M, O, P);            //Caso2 ERROR
Steven R. Davidson
2014-01-13 00:48:38

Hola Milton,

Es un error porque el tipo de dato de 'Tabla' no concuerda con el tipo de dato del primer parámetro del prototipo 'funcion()', que es,

void funcion( int *tabla, int n, int m, int o, int p );

El tipo de 'Tabla' es 'int [][20][25][40]' mientras que el tipo del parámetro local, 'tabla', es 'int *'. Técnicamente, ambos tipos de datos involucran direcciones de memoria, por lo que podemos relacionar ambas variables pero tenemos que forzar una concordancia entre tipos de datos. Para forzar esta concordancia, aplicamos la operación de "cásting".

Espero que esto aclare la duda.

Steven

Milton Parra
2014-01-14 21:02:37

Gracias por la aclaración. Fué clara y precisa.

Emanuel R
2014-04-20 07:33:21

Hola! Me sumo a los elogios por la ayuda tan util que nos das a todos! Tengo un problemita... desarmé mi programa en varios archivos (main, header para una funcion, y codigo fuente de la funcion) Creo haber seguido bien las instrucciones de como crear los archivos. El problema surge cuando quiero pasar por referencia las variables del main a la función (que se encuentra en otro archivo) me sale: [Linker error] main.o:main.cpp:(.text+0x13f): undefined reference to `varSwap(double, double, double)'

mis archivos son: main.cpp:

#include <iostream>
#include "funcTest1.h"
using namespace std;


int main()
{
int a(0),b(0),c(0);

cout<<"ingrese a: ";
cin>>a;
cout<<"ingrese b: ";
cin>>b;
cout<<"ingrese c: ";
cin>>c;

varSwap(a,b,c);

cout<<"orig  a = "<<a<<" b = "<<b<<" c = "<<c<<endl;
	
    return(0);
}

luego, funcTest1.cpp:

#include <iostream>
using namespace std;
void varSwap(double &a, double &b, double &c)
{
	double interim_a(0),interim_b(0);
	interim_a=a;
	a=c;
	interim_b=b;
	b=interim_a;
	c=interim_b;
	
	cout<<"nuevas a = "<<a<<" b = "<<b<<" c = "<<c<<endl;
}

y por ultimo el header: funcTest1.h:

#ifndef FUNCTEST1_H_INCLUDED
#define FUNCTEST1_H_INCLUDED

void varSwap(double a, double b, double c);

#endif

si en funcTest1.cpp declaro a la funcion sin referencia, funciona bien el programa. El objetivo de este codigo es simplemente ver la diferencia entre el uso y no uso de referencias.

Desde ya muchas gracias por ayudarme.

Steven R. Davidson
2014-04-20 18:14:07

Hola Emanuel,

La separación en archivos es correcta. El mensaje del error proviene del enlazador, que significa que no encuentra la implementación de 'varSwap()' que acepta tres parámetros de tipo 'double'. Has creado dos funciones sobrecargadas. Básicamente, tienes dos funciones diferentes con el mismo nombre, 'varSwap()'. Hablamos del tema de la sobrecarga en el capítulo 21.

La solución al problema es cambiar el prototipo en el fichero de cabecera para que coincida con la implementación de la función en su fichero fuente. O sea,

// funcTest1.h

void varSwap( double &a, double &b, double &c );

// funcTest1.cpp

void varSwap( double &a, double &b, double &c )
{
  ...
}

Espero que esto aclare la duda.

Steven

Emanuel R
2014-04-21 00:00:40

Ese era exactamente el problema! Muchísimas gracias!

JuniorF
2014-08-29 17:13:17

Hola, la información de tu pagina me ha resultado muy útil, aunque por ahora no encuentro información sobre

Como pasar la estructura de un nodo a una función como referencia, y poder utilizar la variable referenciada para solicitar asignación de memoria con NEW y crear nuevos nodos.

Eso es lo que quiero hacer en esta función:

Tnodo inicia (Tnodo &nodo){
	static Tnodo *aux=NULL;
	static int i=0;
nodo= new Tnodo;          //### AQUI APARECE EL ERROR##??
	cout<<"INGRESE UN NUMERO "<<endl;
	cin>>nodo->dato;
	nodo->sig=aux;
	aux=nodo;
	i++;
	if (i<10)
	return inicia (nodo);
	return nodo;

Saludos, Gracias por tus esfuerzos. Excelente Pagina!