17 Tipos de variables VI: Punteros 2

Ya hemos visto que los arrays pueden ser una potente herramienta para el almacenamiento y tratamiento de información, pero tienen un inconveniente: hay que definir su tamaño durante el diseño del programa, y después no puede ser modificado.

La gran similitud de comportamiento de los punteros y los arrays nos permiten crear arrays durante la ejecución, y en este caso además el tamaño puede ser variable. Usaremos un puntero normal para crear vectores dinámicos, uno doble para tablas, etc.

Por ejemplo, crearemos una tabla dinámicamente. Para ello se usan los punteros a punteros.

Veamos la declaración de un puntero a puntero:

int **tabla;

"tabla" es un puntero que apunta a un objeto de tipo puntero a int.

Sabemos que un puntero se comporta casi igual que un array, por lo tanto nada nos impide que "tabla" apunte al primer elemento de un array de punteros:

int n = 134;
tabla = new int*[n];

Ahora estamos en un caso similar, "tabla" apunta a un array de punteros a int, cada elemento de este array puede ser a su vez un puntero al primer elemento de otro array:

int m = 231; 
for(int i = 0; i < n; i++) 
   tabla[i] = new int[m];

Ahora tabla apunta a un array de dos dimensiones de n * m, podemos acceder a cada elemento igual que accedemos a los elementos de los arrays normales:

tabla[21][33] = 123;

Otra diferencia con los arrays normales es que antes de abandonar el programa hay que liberar la memoria dinámica usada, primero la asociada a cada uno de los punteros de "tabla[i]":

for(int i = 0; i < n; i++) delete[] tabla[i];

Y después la del array de punteros a int, "tabla":

delete[] tabla;

Veamos el código de un ejemplo completo:

#include <iostream>
using namespace std;
 
int main() { 
   int **tabla; 
   int n = 134; 
   int m = 231;
   int i;
 
   // Array de punteros a int: 
   tabla = new int*[n]; 
   // n arrays de m ints 
   for(i = 0; i < n; i++) 
      tabla[i] = new int[m]; 
   tabla[21][33] = 123; 
   cout << tabla[21][33] << endl; 
   // Liberar memoria: 
   for(i = 0; i < n; i++) delete[] tabla[i]; 
   delete[] tabla; 
   
   return 0; 
}

Pero no tenemos por qué limitarnos a arrays de dos dimensiones, con un puntero de este tipo:

int ***array;

Podemos crear un array dinámico de tres dimensiones, usando un método análogo.

Y generalizando, podemos crear arrays de cualquier dimensión.

Tampoco tenemos que limitarnos a arrays regulares.

Veamos un ejemplo de tabla triangular:

Crear una tabla para almacenar las distancias entre un conjunto de ciudades, igual que hacen los mapas de carreteras.

Para que sea más sencillo usaremos sólo cinco ciudades:

Ciudad A 0        
Ciudad B 154 0      
Ciudad C 254 354 0    
Ciudad D 54 125 152 0  
Ciudad E 452 133 232 110 0
Distancias Ciudad A Ciudad B Ciudad C Ciudad D Ciudad E

Evidentemente, la distancia de la Ciudad A a la Ciudad B es la misma que la de la Ciudad B a la Ciudad A, así que no hace falta almacenar ambas. Igualmente, la distancia de una ciudad a sí misma es siempre 0, otro valor que no necesitamos.

Si tenemos n ciudades y usamos un array para almacenar las distancias necesitamos:

n*n = 5*5 = 25 casillas.

Sin embargo, si usamos un array triangular:

n*(n-1)/2 = 5*4/2 = 10 casillas.

Veamos cómo implementar esta tabla:

#include <iostream>
using namespace std;
 
#define NCIUDADES 5 
#define CIUDAD_A 0 
#define CIUDAD_B 1 
#define CIUDAD_C 2 
#define CIUDAD_D 3 
#define CIUDAD_E 4
 
// Variable global para tabla de distancias: 
int **tabla; 
// Prototipo para  calcular la distancia entre dos ciudades: 
int Distancia(int A, int B);
 
int main() { 
   int i;
 
   // Primer subíndice de A a D 
   tabla = new int*[NCIUDADES-1]; 
   // Segundo subíndice de B a E, 
   // define 4 arrays de 4, 3, 2 y 1 elemento: 
   for(i = 0; i < NCIUDADES-1; i++) 
      tabla[i] = new int[NCIUDADES-1-i]; // 4, 3, 2, 1 
   // Inicialización: 
   tabla[CIUDAD_A][CIUDAD_B-CIUDAD_A-1] = 154; 
   tabla[CIUDAD_A][CIUDAD_C-CIUDAD_A-1] = 245; 
   tabla[CIUDAD_A][CIUDAD_D-CIUDAD_A-1] = 54; 
   tabla[CIUDAD_A][CIUDAD_E-CIUDAD_A-1] = 452; 
   tabla[CIUDAD_B][CIUDAD_C-CIUDAD_B-1] = 354; 
   tabla[CIUDAD_B][CIUDAD_D-CIUDAD_B-1] = 125; 
   tabla[CIUDAD_B][CIUDAD_E-CIUDAD_B-1] = 133; 
   tabla[CIUDAD_C][CIUDAD_D-CIUDAD_C-1] = 152; 
   tabla[CIUDAD_C][CIUDAD_E-CIUDAD_C-1] = 232; 
   tabla[CIUDAD_D][CIUDAD_E-CIUDAD_D-1] = 110; 

   // Ejemplos: 
   cout << "Distancia A-D: " 
        << Distancia(CIUDAD_A, CIUDAD_D) << endl; 
   cout << "Distancia B-E: " 
        << Distancia(CIUDAD_B, CIUDAD_E) << endl; 
   cout << "Distancia D-A: " 
        << Distancia(CIUDAD_D, CIUDAD_A) << endl; 
   cout << "Distancia B-B: " 
        << Distancia(CIUDAD_B, CIUDAD_B) << endl; 
   cout << "Distancia E-D: " 
        << Distancia(CIUDAD_E, CIUDAD_D) << endl;
 
   // Liberar memoria dinámica: 
   for(i = 0; i < NCIUDADES-1; i++) delete[] tabla[i]; 
   delete[] tabla; 
   
   return 0; 
}
 
int Distancia(int A, int B) {
   int aux;
 
   // Si ambos subíndices son iguales, volver con cero: 
   if(A == B) return 0; 
   // Si el subíndice A es mayor que B, intercambiarlos: 
   if(A > B) {
      aux = A; 
      A = B; 
      B = aux; 
   } 
   return tabla[A][B-A-1]; 
}

Notas sobre el ejemplo:

Observa el modo en que se usa la directiva #define para declarar constantes. Aunque en C++ es preferible usar variables constantes, como este tema aún no lo hemos visto, seguiremos usando macros.

Efectivamente, para este ejemplo se complica el acceso a los elementos de la tabla ya que tenemos que realizar operaciones para acceder a la segunda coordenada. Sin embargo piensa en el ahorro de memoria que supone cuando se usan muchas ciudades, por ejemplo, para 100 ciudades:

Tabla normal 100*100 = 10000 elementos.

Tabla triangular 100*99/2 = 4950 elementos.

Hemos declarado el puntero a tabla como global, de este modo será accesible desde main y desde Distancia. Si la hubiéramos declarado local en main, tendríamos que pasarla como parámetro a la función.

Observa el método usado para el intercambio de valores de dos variables. Si no se usa la variable "aux", no es posible intercambiar valores. Podríamos haber definido una función para esta acción, "Intercambio", pero lo dejaré como ejercicio.

Problemas

  1. Usando como base el ejemplo anterior, realizar los siguientes cambios:
    • Modificar el código para que "tabla" sea una variable local de main.
    • Definir una función con el prototipo void AsignarDistancia(int**, int, int, int);, para asignar valores a distancias entre dos ciudades. El primer parámetro será la tabla de distancias, los dos siguientes parámetros serán identificadores de dos ciudades y el cuarto la distancia entre dichas ciudades.
      Por ejemplo AsignarDistancia(tabla, CIUDAD_E, CIUDAD_B, 123);.
    • Definir una función con el prototipo void Intercambio(int &, int &);, para intercambiar los contenidos de dos variables enteras.

    Realizar los cambios necesarios en el programa para que se usen estas nuevas funciones siempre que sea posible.

  2. Ordenar un array de float aleatorios, para ello, crear un array dinámico con el mismo número de elementos que contenga valores enteros, cada uno de ellos será un índice del array de floats a ordenar.
    Ordenar los índices en este segundo array según el orden ascendente del array de números, pero sin modificar el orden ni el contenido del array de floats, que debe permanecer constante.
    Por ejemplo, si el array dado contiene los valores: 1.32, 4.21, 2.33, 0.23, 8.35, 2.32, se debe crear un segundo array de enteros dinámico, que una vez ordenado debe contener los valores: 3, 0, 5, 2, 1, 4.
    Para ordenar el array de enteros se debe usar la función qsort.
  3. Modificar el programa anterior para añadir un segundo array de enteros que contenga los índices del array de floats ordenados de mayor a menor, además del que ya tenía, con los índices de los floats ordenados de menor a mayor.
  4. Concatenar dos arrays de enteros ordenados en un tercero, de modo que contenga los elementos de los dos, mezclados de modo que se mantenga el orden.
    Por ejemplo:
        int a1[] = {1,3,4,7,8,9,12,15,16,17,21,23,25};
        int a2[] = {2,5,6,10,11,13,14,18,19,20,22,24,26,27,28};
    
    El resultado debe ser un tercer array con todos los elementos presentes en los dos arrays dados, y manteniendo el orden ascendente.

Comentarios de los usuarios (26)

b0ch0n
2010-10-28 13:45:07

una solucion al problema:

http://codepad.org/kcsu6EuX

Nota del administrador: Hemos eliminado el código ya que las soluciones a los problemas no se han incluido de forma intencionada. La idea es que cada uno haga sus propios problemas a su modo.

AlejandroCH
2011-01-15 23:31:38

A todo esto de la memoria dinamica me ha dejado una duda cuando me leo sobre las consecuencias desastrosas de no borrar la misma y de una practica demaciado sospechosa tambien. ¿Como que consecuencias desastrosas podria dejar no borrar la memoria dinamica, y que podria alguien sospechoso hacer no borrandola?

Salvador Pozo
2011-01-16 00:00:13

Las consecuencias desastrosas más evidentes de no liberar la memoria dinámica son las fugas de memoria. Un programa que haga uso de memoria dinámica y no la libere después, se adueña de recursos limitados del sistema, poniendo en peligro su estabilidad. Con los sistemas operativos actuales puede no ser tan desastroso, sólo tu programa dejará de funcionar. En los más antiguos todo el sistema puede caer.

En cuanto a las actitudes "sospechosas", los programadores desconfiamos de programas que dejan que el sistema libere la memoria dinámica. Es una actitud descuidada, y nos hace sospechar de si no habrá otros "descuidos" en el código.

Ger
2011-01-29 18:02:51

Muy buen tutorial, la verdad es que es completísimo y bien explicado.

Me quedo una duda: ¿hay alguna manera de asignar varios elementos por medio de {} a un array dinamico?

Por ejemplo si yo tengo

tabla = new int*[5];

for(int i=0; i<5; i++)

tabla[i] = new int[5];

Y quiero poner tabla[0][0] = {0, 1, 2, 3, 4} me tira error =/ tengo que asignar elemento por elemento

Muchas gracias.

Esteban
2011-05-26 18:32:38

como hacer un programa que intercambie 2 variables agradezco su ayuda

Jose Luis
2011-12-01 11:38:41

Buenos dias. Gracias por su ayuda ya que me estoy iniciando en C++ y estoy bastante perdido.

Estoy intentando hacer una función que lea una matriz llama J1 que está dentro de una estructura llamada estado.

Necesito que me devuelva la matriz leída.

Como podría realizar esto??. No termino de entender el mecanismo de los punteros.

He probado algo así:

int **leer_matriz(struct estado, int jug_bola){

int **tabla;

int tam;

tam=sizeof(estado);

tabla=estado.J1;

return tabla;

}

Sería correcto?.

Gracias por su ayuda.

Steven R. Davidson
2011-12-01 12:43:45

Hola José Luis,

En primer lugar, no has completado la definición del parámetro 'estado' en la función. Tienes que indicar el nombre de la estructura. Además, en C++, no es necesario indicar 'struct'.

En segundo lugar, creo que el problema está en la definición de matriz, 'J1'. Si con "matriz" te refieres a un array bidimensional, entonces sí tenemos problemas. Es decir, si tienes esto,

struct Algo
{
  int J1[100][200];
};

Como no sabemos la definición de 'J1', le he dado una definición mía con algunos tamaños.

Si esto es lo que tienes, o algo parecido, entonces no podrás asignar este array bidimensional a un puntero doble. La razón es que un puntero es una variable que guarda en memoria la dirección de memoria de otra variable. En este caso, esta otra variable se trata de otro puntero, por lo que guarda la dirección de memoria de otra variable, de tipo 'int'.

En el caso del array bidimensional, éste no es una variable, sino una dirección de memoria. Puedes pensar que el array es una dirección de memoria constante y casi literal. El array 'J1' no es una variable en memoria donde guardar una dirección de memoria. Básicamente, donde coloques 'J1' en tu código fuente, el compilador aparece y lo sustituye por la dirección de memoria del primer elemento de tal array; por eso, 'J1' es equivalente a '&J1[0][0]' - la dirección de memoria del primer elemento.

La solución es usar y retornar un puntero (sencillo) a 'int'. Esto es,

int *leer_matriz( struct Algo estado, int jug_bola )
{
  int *tabla = &estado.J1[0][0];
  ...
  return tabla;
}

Esto significa que tendrás que tratar este puntero como si apuntare a un array unidimensional. La forma correcta de hacer esto es realizando los cálculos necesarios para "saltar" los bloques que son las filas de la tabla y luego saltar a los elementos dentro de ese bloque que son las columnas de la misma tabla. Por ejemplo,

int *ptr = leer_matriz( obj, dato );

ptr[ 5*200 + 40 ] = -1;  // accedemos a obj.J1[5][40]

De hecho, así es como realiza el compilador los accesos a los elementos en un array bidimensional; y por eso necesita conocer los tamaños de cada "dimensión".

Otra solución es retornar un puntero a un array. Esto sería,

typedef int columna[200];

columna *leer_matriz( struct Algo estado, int jug_bola )
{
  columna *tabla = estado.J1;
  ...
  return tabla;
}

Como puedes ver, por sencillez he creado un tipo llamado 'columna' que representa un array el cual representa las columnas de una fila en particular de la tabla o matriz. Y ahora podemos retornar un puntero que contiene una dirección de memoria que representa una fila en concreto que apunta a un array - las columnas.

Espero que esto te sirva.

Steven

Jose Luis
2011-12-02 09:33:30

Buenos días de nuevo. Tras probar lo que comentó Steven, sigo teniendo el mismo problema. No consigo leer la matriz.

Decidí por crear una matriz tridimensional en la cual la primera posición indicase el jugador y las otras dos sus posibles jugadas.

En leer_matriz paso el estado que debo leer (en total tendré 9 estructuras, una por cada caso, con sus respectivas matrices).

Este es mi código.

#include <iostream>

using namespace std;

struct Estado{

//int players[2][3][3];

int players[2][3][3]={{{0,1,2},{3,4,5},{7,5,1}},

{{9,8,7},{6,5,4},{1,5,9}}};

}estado11;

int *leer_matriz(struct Estado estado_actual, int jugador){

return estado_actual.players[jugador];

}

int main(){

//Estado a11;

int *punt;

int a[3][3];

//Estado -> estado_actual;

punt=&a;

*punt=leer_matriz(estado11,1);

for(int i=0;i<3;i++){

for(int j=0;j<3;j++)

printf("%d",a[i][j]);

}

}

Disculpen mi torpeza ya que estoy empezando.

Agradecería mucho su ayuda de nuevo.

Muchas gracias.

Steven R. Davidson
2011-12-02 14:30:27

Hola José Luis,

En primer lugar, debo aclarar tu primer comentario: "...en total tendré 9 estructuras, una por cada caso, con sus respectivas matrices". Esto no es correcto. Lo que sí tendrás es un array bidimensional (una tabla o matriz) de 9 enteros de tipo 'int'. Por ejemplo, 'estado11.players[0]' da lugar a un array de 3 arrays de 3 'int' cada uno.

En segundo lugar, no puedes inicializar un campo de una estructura dentro de la definición de ésta. Tienes que inicializar la variable de tal estructura fuera. Por ejemplo,

struct Estado
{
  int players[2][3][3];
} estado11 = {
               {  // players
                 { {0,1,2},{3,4,5},{7,5,1} }, // players[0]
                 { {9,8,7},{6,5,4},{1,5,9} }  // players[1]
               }
             };

Como puedes ver, también te falta más llaves en la inicialización.

El siguiente problema es en la función 'leer_matriz()'. Escribes:

int *leer_matriz( struct Estado estado_actual, int jugador )
{
  return estado_actual.players[jugador];
}

La expresión de 'return' será de tipo 'int [][3]' mientras que el tipo de retorno de la función es 'int *'. Como podemos ver, los tipos no concuerdan. Tampoco existe una conversión implícita entre estos dos tipos. Lo que sí existe es un vínculo por el "tipo" de valor tratado: una dirección de memoria. Por lo tanto, podemos hacer un cásting explícito; por ejemplo,

int *leer_matriz( struct Estado estado_actual, int jugador )
{
  return (int *) estado_actual.players[jugador];
}

Ahora retornamos la dirección de memoria del primer elemento de ese array bidimensional. El problema es que tenemos que tratar tal puntero como si fuese un array unidimensional. Es decir, que los 3 primeros índices corresponden a la primera "fila" y los 3 siguientes índices sirven para acceder a la segunda fila de la "tabla". Por ejemplo,

int *p = leer_matriz( estado11, 1 );
cout << p[0] << ' ' << p[1] << ' ' << p[2] << endl;
cout << p[3] << ' ' << p[4] << ' ' << p[5] << endl;

aparecerá en pantalla:

9 8 7
6 5 4

La otra posibilidad es obteniendo la dirección de memoria del primer elemento de tal jugador; esto es,

return &estado_actual.players[jugador][0][0];

De esta manera, obtenemos un 'int *' para apuntar al primer elemento de la primera "fila". Como ves, no hace falta realizar un cásting explícito.

El último error está en la función 'main()'. Escribes:

int *punt;
int a[3][3];
punt = &a;
*punt = leer_matriz( estado11, 1 );

Primeramente, no podemos asignar la dirección de memoria de un array bidimensional a 'punt', porque ésta es un puntero a 'int'. Es decir, los tipos no concuerdan. En cualquier caso, podríamos aplicar un cásting a 'int *'; por ejemplo,

punt = (int *) a;

o como expliqué antes, podemos acceder directamente al primer elemento del array así,

punt = &a[0][0];

De todas formas, la siguiente asignación no es correcta:

*punt = leer_matriz( estado11, 1 );

Nuevamente, los tipos no concuerdan. Con '*punt' accedes a la variable apuntada que por su tipo es 'int', mientras que la función 'leer_matriz()' retorna un 'int *'.

Viendo el resto del código, creo que lo que quieres hacer es que copie todos los elementos de una "tabla" a esta otra "tabla" representada por 'a', a través del puntero 'punt'. Me temo que esto no va a funcionar directamente. C++ no realizará esta copia de elementos. Por lo que te queda la opción de copiar los elementos "manualmente"; uno a uno. También puedes optar por usar la función estándar 'memcpy()', declarada en <cstring>. Por ejemplo,

memcpy( &a[0][0], leer_matriz( estado11, 1 ), sizeof a );

Por último, quiero comentarte algo sobre el diseño. Creo que es mejor definir otras estructuras o tipos de datos para representar los conceptos que quieres. Así puedes "construir" un array de estos conceptos que a su vez estos conceptos pueden involucrar otros arrays. La cuestión es organizarse mejor para no tener que estar manipulando toda la información de golpe y a "bajo nivel" como estás haciendo ahora. En fin, esto es una sugerencia mía y no sé qué proyecto estás haciendo, por lo que no sé si es viable lo que te estoy recomendando o no, en tu caso.

Espero que vaya aclarando las dudas sobre este tema.

Steven

José Luis
2011-12-05 08:24:57

Muchas gracias Steven. Me has aclarado mucho el tema de los punteros.

Creo que ahora he conseguido entender como funcionan.

De nuevo, gracias.

Adri
2015-01-02 02:08:12

Cuando usas este codigo:

tabla = new int*[n];

¿Podrías explicarme que significa int*[n]?

Esque no entiendo lo que significa cuando

una variable esta entre corchetes sin que le

preceda el nombre del array que corresponde.

¿Que se supone que significa?

Adri
2015-01-02 02:09:19

Si lo has explicado antes en algun momento, ¿Podrias decirme en que capitulo?

Adri
2015-01-02 03:28:53

Otra pregunta mas:

¿No seria mas facil declarar en lugar de los punteros, arrays?

Steven R. Davidson
2015-01-02 13:03:49

Hola Adri,

Tienes que interpretar las sentencias correctamente para comprender lo que se pide. En este caso, se trata de una sentencia de expresión. Sintácticamente, tenemos,

<expresión> = <expresión>

O sea,

tabla = <expresión>

Ahora miramos la expresión a mano derecha, que es una operación 'new[]', la cual su sintaxis es:

new <tipo> [ <expresión> ]

Aplicando la sintaxis vemos que tenemos la asociación:

<tipo> ::= int *
<expresión> ::= n

Semánticamente, <expresión> debe ser de tipo 'int' que representa la cantidad de elementos a crear para el array dinámico. Por lo tanto, 'n' representa tal cantidad de elementos. Concluyendo, pedimos adjudicar memoria para una cantidad, descrita por el valor en 'n', de elementos contiguos de tipo 'int *'. La dirección de tal memoria es asignada al valor de 'tabla'.

Revisa la sintaxis de 'new[]' en el capítulo 13, y de paso consulta la tabla de la precedencia de operadores en el capítulo 14.

Espero que esto te vaya aclarando las dudas sobre este tema.

Steven

Steven R. Davidson
2015-01-02 13:35:36

Hola Adri,

Por facilidad, sí, pero no es lo mismo. Un array dinámico se crea en tiempo de ejecución pidiendo memoria al sistema operativo, mientras que un array (estático) usa la memoria del mismo programa (o proceso). Existen algunas limitaciones impuestas por el sistema operativo y posiblemente por el sistema de hardware: memoria RAM, por ejemplo.

Desde el punto de vista de C++, el compilador gestiona el array imponiendo ciertas restricciones, como por ejemplo, la cantidad de elementos DEBE ser constante, mientras que al crear un array dinámico, su cantidad puede ser variable, como el ejemplo que has mencionado.

Como suele ocurrir en la informática, hay métodos y prestaciones para la facilidad, pero con cierta inflexibilidad, como también hay otros que son algo más complicados o explícitos, pero que ofrecen más flexibilidad y extensibilidad. Todo depende de lo que necesites para solucionar un problema.

Espero que esto te oriente.

Steven

Alfonso Aguilar
2015-02-01 01:22:31

Buenas mi problema es intentar devolver el doble puntero de una clase para poder trabajar con el en otra. Para probar esto cree el siguiente código, y como soy bastante nuevo en esto no función por un error que dice Segmentation Fault 11. Si alguien puede ayudarme se lo agradeceria mucho.

#include <iostream>

using namespace std;

class pdoble
{

public:
    pdoble(int fil, int col){
        int **M = new int*[fil];
        for(int i =0;i<fil;i++){
            M[i] = new int[col];
        }
        for (int i = 0; i < fil; i++){
                 for (int j = 0; j < col; j++){
                 M[i][j]=1;
                }
        }


    }
    int& getM(){
        return **M;
    }


private:
    int fil, col;
    int **M;
};

int main(){

    pdoble aap(2,2);
    int *P;
    *P=aap.getM();
cout<<P<<endl;

}
Jesus Leyva
2015-03-03 04:55:30

Hola, tengo una duda... he querido realizar una función que me devuelva un array de enteros, como se sabe un array tecnicamente es un puntero asi que hice eso... pero el resultado no es el que esperaba. Alguna solucion para que una funcion me retorne un array de enteros.!?

#include <iostream>
using namespace std;

int* funcion(){
    int arreglo[3];
    arreglo[0] = 10;
    arreglo[1] = 2;
    arreglo[2] = 3;
    cout<<&arreglo[0]<<endl;
    cout<<&arreglo[1]<<endl;
    cout<<&arreglo[2]<<endl;

    cout<<"-----------------"<<endl;

    cout<<*(arreglo+0)<<endl;
    cout<<*(arreglo+1)<<endl;
    cout<<*(arreglo+2)<<endl;

    cout<<"-----------------"<<endl;

    return arreglo;
}
int main()
{
   int *arreglo;
   arreglo=funcion();

    cout<<arreglo+0<<endl;
    cout<<arreglo+1<<endl;
    cout<<arreglo+2<<endl;

    cout<<"-----------------"<<endl;

    cout<<*(arreglo+0)<<endl;
    cout<<*(arreglo+1)<<endl;
    cout<<*(arreglo+2)<<endl;
   return 0;
}

Steven R. Davidson
2015-03-03 15:45:31

Hola Alfonso,

Tienes dos errores. El primer es que has definido \'M\' localmente en el constructor. Esto no es lo que interesa, ya que definimos \'M\' como miembro privado. La solución es simplemente,

pdoble( int fil, int col )
{
  M = new int*[fil];
  for( int i =0; i

El segundo error es el uso del puntero en \'main()\'. Escribes:

int *P;
*P = aap.getM();

\'P\' no apunta a nada controlado ni deseado, por lo que estás accediendo a memoria que seguramente no te pertenece. Esto es muy peligroso. Una posible solución es conseguir la dirección de memoria del primer elemento apuntado por el miembro \'M\', de esta manera,

int *P = &aap.getM();  // El operador & sería la última operación en esta expresión

De todas maneras, no aconsejamos hacer esto, porque lo que interesa es conseguir el puntero en sí, por lo que deberíamos retornar tal valor; esto es,

class pdoble
{
  ...
  int **getM()  { return M; }
};

Y ahora podemos hacer algo más legible,

int *P = aap.getM();

Por cierto, no es un buen diseño dar acceso a los datos miembro de un objeto. La solución ideal sería crear funciones miembro que realicen accesos a los datos miembro o incluso crear copias para dar a las partes externas de los objetos de esta clase.

Por último, la salida que das con \'cout\' sólo mostrará la dirección de memoria guardada en \'P\' que será la misma en \'M\'; por ejemplo,

1ae478c0

Espero que esto te oriente.

Steven

Steven R. Davidson
2015-03-03 17:51:22

Hola Jesús,

Primeramente, quiero matizar que un array es una secuencia de elementos contiguos, mientras que el tipo de un array es una dirección de memoria. Como un tipo de puntero trata direcciones de memoria, existe una relación entre arrays y punteros, porque se pueden tratar como direcciones de memoria en C/C++.

El problema que tienes con la función es que intentas retornar la dirección de memoria de un array local. Recuerda que cualquier variable definida localmente (en un ámbito) se destruirá al terminar tal ámbito. El array local, 'arreglo', dejará de existir al terminar de ejecutar esta función. Por lo tanto, la dirección de memoria retornada deja de ser válida.

La solución es pasar el array por parámetro, para que exista fuera de esta función, y deje de ser local. Por ejemplo,

int *funcion( int *ptr )
{
  ...
  return ptr;
}

int main()
{
  int arreglo[3];
  int *pArreglo = funcion( arreglo );
  ...
}

Obviamente, en este caso no hace falta usar 'pArreglo', porque tenemos 'arreglo' localmente definido en 'main()'.

La otra opción es crear un array dinámico y que 'funcion()' lo retorne. Sin embargo, en general, no es nada aconsejable hacer esto, porque no es fácil ver que el valor retornado requiere un tratamiento especial: se debe liberar su memoria, con 'delete[]'.

Espero que esto aclare las dudas.

Steven

Luis
2015-04-07 23:52:50

Hola!

Una consulta, en el capítulo 1 de punteros (12 real) vimos que haciendo que un puntero apuntase a la primera posicion de un vector, luego podíamos movernos por el vector, avanzando y retrasando el puntero:

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

puntero = &vector[0];
puntero[2]; //puntero apunta a 3
puntero[1]; //puntero apunta a 4

En este capítulo, cuando hacemos:

int * tabla;
tabla = new int [5];
for(i=1,i<6,i++)
  tabla[i] = i ; 

Yo entiendo que el puntero tabla, siempre apunta al primer int y lo único que hacemos es reservar memoria para más ints detrás. Esto está claro pero no veo porque el puntero no avanza o retrasa posiciones al hacer cosas como:

cout<<tabla[3]<<endl; (por ejemplo) porque parece que a la siguiente llamada el puntero vuelve a estar en tabla[0].

y creo que esto estará relacionado pero no entiendo porque lo correcto es hacer cout<<tabla[3]<<endl; y no cout<<*tabla[3]<<endl;

Gracias!

Steven R. Davidson
2015-04-08 17:53:00

Hola Luis,

Tanto en el primer código, que presentas, como en el segundo, no avanzamos ni retrocedemos el puntero. Lo que hacemos es asignar el primer 'int' (o mejor dicho el primer byte del primer 'int') al puntero. Para poder avanzar y retroceder en el ACCESO de los elementos del array (o vector) usamos el operador [] que es de acceso.

Este operador [] sirve para acceder al elemento, según el entero, que actúa como índice, dado. Por ejemplo, 'puntero[2]' accedemos a dos enteros posteriores al entero apuntado por 'puntero'. Este comportamiento es equivalente a calcular la dirección de la memoria del primer byte del tercer elemento (de tipo 'int') y acceder al valor guardado en tal dirección de memoria. Esto se puede expresar de esta manera: *(puntero+2). De hecho, puntero[2] es equivalente a *(puntero+2). Como puedes ver, 'puntero' no varía; realizamos un cálculo para conseguir la dirección correcta de memoria y accedemos al valor.

Espero que esto aclare la duda.

Steven

Germán
2015-09-28 21:01:36

Estoy en el ejercicio 2 y he escrito est, lo basico:

#include <iostream>

#include <stdlib.h>

#include <stdio.h>

using namespace std;

float fl[6] = { 1.32, 4.21, 2.33, 0.23, 8.35, 2.32};

int *cont[6];

int main(){

int i;

for (i=0;i<6;i++) cont[i] = new int[i];

}

pero ahora para introducir el qsort no entiendo como funciona

Steven R. Davidson
2015-09-28 21:37:12

Hola Germán,

La función 'qsort()' se basa en un puntero a función. Hablamos de este tema en el capítulo 20, pero puedes esperar a llegar a tal capítulo o adelantarte yendo a: http://c.conclase.net/curso/?cap=020d#FUN3_puntfunc

Resumidamente, tienes que crear una función para comparar dos elementos consecutivos en el array. El prototipo DEBE ser:

int comparar( const void *, const void * );

Obviamente, el nombre corre de tu cuenta. Ahora invocaríamos la función así,

qsort( array, N, sizeof (array[0]), comparar );

para ordenar 'array' de N elementos.

Espero haber aclarado la duda.

Steven

j. Francisco
2015-12-08 21:57:11

Hola :

vaya por delante felicitaros por la página.

Mi duda es la siguiente. No entiendo la lógica del calculo de las distancias.sigo el código y no doy con la explicación. por ejemplo:

la distancia de ciudad_A a ciudad_E es 452. La posición según la matriz es la TABLA[4][0]. Si sigo el algoritmo tenemos que la llamada es distancia(ciudad_A, ciudad_E) por lo que :

distancia(0,4).

A>B ? --> No, y después de hacer operaciones queda que retorna tabla[0][3] , que no se corresponde con la posición del valor en la tabla. Por favor puedes explicarme esto.

Steven R. Davidson
2015-12-08 23:55:22

Hola J. Francisco,

- Lógicamente, 452 se guardaría en 'tabla[4][0]', pero técnicamente se guarda en 'tabla[0][3]'. De hecho, no guardamos datos para la quinta "fila", ya que creamos solamente cuatro "filas" para 'tabla', como puedes ver en esta sentencia:

tabla = new int*[NCIUDADES-1];

- Sí corresponde con el valor de la tabla[0][3] el cual es 452. Hicimos esta asignación al construir la tabla, que es justamente:

tabla[CIUDAD_A][CIUDAD_E-CIUDAD_A-1] = 452;

que viene a ser,

tabla[0][4-0-1] = 452;

Espero haber aclarado la duda.

Steven

J. Francisco
2015-12-12 01:54:10

Gracias por la pronta respuesta:

he vuelto a seguir el código y ahora lo entiendo mejor. Ahora mi problema está en desarrollar el ejercicio propuesto de Modificar el código para que "tabla" sea una variable local de main. No se como empezar. qué debo tener en cuenta?.

Pienso que debo pasar la tabla por referencia y modificar el código de la función para que procese el parámetro , per no veo como puedo hacer. Me puedes dar una pista.? gracias por la ayuda.