12 Tipos de objetos IV: Punteros 1

No, no salgas corriendo todavía. Aunque vamos a empezar con un tema que suele asustar a los estudiantes de C++, no es algo tan terrible como se cuenta. Como se suele decir de los leones: no son tan fieros como los pintan.

¡Pánico!, punteros
El ataque de los punteros.

Vamos a intentar explicar cómo funcionan los punteros de forma que no tengan el aspecto de magia negra ni un galimatías incomprensible.

Pero no bastará con entender lo que se explica en este capítulo. Es relativamente sencillo saber qué son y cómo funcionan los punteros. Para poder manejarlos es necesario también comprender los punteros, y eso significa saber qué pueden hacer y cómo lo hacen. Para comprender los punteros se necesita práctica, algunos necesitamos más que otros, (y yo considero que no me vendría mal seguir practicando). Incluso cuando ya creas que los dominas, seguramente quedarán nuevos matices por conocer.

Pero seguramente estoy exagerando. Si soy capaz de explicar correctamente los conceptos de este capítulo, pronto te encontrarás usando punteros en tus programas casi sin darte cuenta.

Los punteros proporcionan la mayor parte de la potencia al C++, y marcan la principal diferencia con otros lenguajes de programación.

Una buena comprensión y un buen dominio de los punteros pondrá en tus manos una herramienta de gran potencia. Un conocimiento mediocre o incompleto te impedirá desarrollar programas eficaces.

Por eso le dedicaremos mucha atención y mucho espacio a los punteros. Es muy importante comprender bien cómo funcionan y cómo se usan.

Creo que todos sabemos lo que es un puntero, fuera del ámbito de la programación. Usamos punteros para señalar cosas sobre las que queremos llamar la atención, como marcar puntos en un mapa o detalles en una presentación en pantalla. A menudo, usamos el dedo índice para señalar direcciones o lugares sobre los que estamos hablando o explicando algo. Cuando un dedo no es suficiente, podemos usar punteros. Antiguamente esos punteros eran una vara de madera, pero actualmente se usan punteros laser, aunque la idea es la misma. Un puntero también es el símbolo que representa la posición del ratón en una pantalla gráfica. Estos punteros también se usan para señalar objetos: enlaces, opciones de menú, botones, etc. Un puntero sirve, pues, para apuntar a los objetos a los que nos estamos refiriendo.

Pues en C++ un puntero es exactamente lo mismo. Probablemente habrás notado que a lo largo del curso nos hemos referido a variables, constantes, etc como objetos. Esto ha sido intencionado por el siguiente motivo:

C++ está diseñado para la programación orientada a objetos (POO), y en ese paradigma, todas las entidades que podemos manejar son objetos.

Los punteros en C++ sirven para señalar objetos, y también para manipularlos.

Para entender qué es un puntero veremos primero cómo se almacenan los datos en un ordenador.

memoria de ferrita

Memoria de un bit de ferrita.

La memoria de un ordenador está compuesta por unidades básicas llamadas bits. Cada bit sólo puede tomar dos valores, normalmente denominados alto y bajo, ó 1 y 0. Pero trabajar con bits no es práctico, y por eso se agrupan.

Cada grupo de 8 bits forma un byte u octeto. En realidad el microprocesador, y por lo tanto nuestro programa, sólo puede manejar directamente bytes o grupos de dos o cuatro bytes. Para acceder a los bits hay que acceder antes a los bytes.

Cada byte de la memoria de un ordenador tiene una dirección, llamada dirección de memoria.

Los microprocesadores trabajan con una unidad básica de información, a la que se denomina palabra (en inglés word). Dependiendo del tipo de microprocesador una palabra puede estar compuesta por uno, dos, cuatro, ocho o dieciséis bytes. Hablaremos en estos casos de plataformas de 8, 16, 32, 64 ó 128 bits. Se habla indistintamente de direcciones de memoria, aunque las palabras sean de distinta longitud. Cada dirección de memoria contiene siempre un byte. Lo que sucederá cuando las palabras sean, por ejemplo, de 32 bits es que accederemos a posiciones de memoria que serán múltiplos de 4.

Por otra parte, la mayor parte de los objetos que usamos en nuestros programas no caben en una dirección de memoria. La solución utilizada para manejar objetos que ocupen más de un byte es usar posiciones de memoria correlativas. De este modo, la dirección de un objeto es la dirección de memoria de la primera posición que contiene ese objeto.

Dicho de otro modo, si para almacenar un objeto se precisan cuatro bytes, y la dirección de memoria de la primera posición es n, el objeto ocupará las posiciones desde n a n+3, y la dirección del objeto será, también, n.

Todo esto sucede en el interior de la máquina, y nos importa relativamente poco. Pero podemos saber qué tipo de plataforma estamos usando averiguando el tamaño del tipo int, y para ello hay que usar el operador sizeof, por ejemplo:

cout << "Plataforma de " << 8*sizeof(int) << " bits";

Ahora veamos cómo funcionan los punteros. Un puntero es un tipo especial de objeto que contiene, ni más ni menos que, la dirección de memoria de un objeto. Por supuesto, almacenada a partir de esa dirección de memoria puede haber cualquier clase de objeto: un char, un int, un float, un array, una estructura, una función u otro puntero. Seremos nosotros los responsables de decidir ese contenido, al declarar el puntero.

De hecho, podríamos decir que existen tantos tipos diferentes de punteros como tipos de objetos puedan ser referenciados mediante punteros. Si tenemos esto en cuenta, los punteros que apunten a tipos de objetos distintos, serán tipos diferentes. Por ejemplo, no podemos asignar a un puntero a char el valor de un puntero a int.

Intentemos ver con mayor claridad el funcionamiento de los punteros. Podemos considerar la memoria del ordenador como un gran array, de modo que podemos acceder a cada celda de memoria a través de un índice. Podemos considerar que la primera posición del array es la 0 celda[0].

indice
Indice <-> puntero

Si usamos una variable para almacenar el índice, por ejemplo, indice=0, entonces celda[0] == celda[indice]. Finalmente, si prescindimos de la notación de los arrays, podemos ver que el índice se comporta exactamente igual que un puntero.

El puntero indice podría tener por ejemplo, el valor 3, en ese caso, el valor apuntado por indice tendría el valor 'val3'.

Las celdas de memoria tienen una existencia física, es decir son algo real y existirán siempre, independientemente del valor del puntero. Existirán incluso si no existe el puntero.

De forma recíproca, la existencia o no existencia de un puntero no implica la existencia o la inexistencia del objeto. De la misma forma que el hecho de no señalar a un árbol, no implica la inexistencia del árbol. Algo más oscuro es si tenemos un puntero para árboles, que no esté señalando a un árbol. Un puntero de ese tipo no tendría uso si estamos en medio del mar: tener ese puntero no crea árboles de forma automática cuando señalemos con él. Es un puntero, no una varita mágica. :-D

Del mismo modo, el valor de la dirección que contiene un puntero no implica que esa dirección sea válida, en el sentido de que no tiene por qué contener la dirección de un objeto del tipo especificado por el puntero.

Supongamos que tenemos un mapa en la pared, y supongamos también que existen diferentes tipos de punteros láser para señalar diferentes tipos de puntos en el mapa (ya sé que esto suena raro, pero usemos la imaginación). Creamos un puntero para señalar ciudades. Nada más crearlo (o encenderlo), el puntero señalará a cualquier sitio, podría señalar incluso a un punto fuera del mapa. En general, daremos por sentado que una vez creado, el puntero no tiene por qué apuntar a una ciudad, y aunque apunte al mapa, podría estar señalando a un mar o a un río.

Con los punteros en C++ ocurre lo mismo. El valor inicial del puntero, cuando se declara, podría estar fuera del mapa, es decir, contener direcciones de memoria que no existen. Pero, incluso señalando a un punto de la memoria, es muy probable que no señale a un objeto del tipo adecuado. Debemos considerar esto como el caso más probable, y no usar jamás un puntero que no haya sido inicializado correctamente.

Dentro del array de celdas de memoria existirán zonas que contendrán programas y datos, tanto del usuario como del propio sistema operativo o de otros programas, el sistema operativo se encarga de gestionar esa memoria, prohibiendo o protegiendo determinadas zonas.

Pero el propio puntero, como objeto que es, también se almacenará en memoria, y por lo tanto, también tendrá una dirección de memoria. Cuando declaramos un puntero estaremos reservando la memoria necesaria para almacenarlo, aunque, como pasa con el resto del los objetos, el contenido de esa memoria contendrá basura.

En principio, debemos asignar a un puntero, o bien la dirección de un objeto existente, o bien la de uno creado explícitamente durante la ejecución del programa o un valor conocido que indique que no señala a ningún objeto, es decir el valor 0. El sistema operativo, cuanto más avanzado es, mejor suele controlar la memoria. Ese control se traduce en impedir el acceso a determinadas direcciones reservadas por el sistema.

Declaración de punteros

Los punteros se declaran igual que el resto de los objetos, pero precediendo el identificador con un asterisco (*).

Sintaxis:

<tipo> *<identificador>; 

Ejemplos:

int *pEntero;
char *pCaracter;
struct stPunto *pPunto;

Los punteros sólo pueden apuntar a objetos de un tipo determinado, en el ejemplo, pEntero sólo puede apuntar a un objeto de tipo int.

La forma:

<tipo>* <identificador>;

con el (*) junto al tipo, en lugar de junto al identificador del objeto, también está permitida. De hecho, también es legal la forma:

<tipo> * <identificador>;

Veamos algunos matices. Tomemos el primer ejemplo:

int *pEntero;

equivale a:

int* pEntero;

Otro detalle importante es que, aunque las tres formas de situar el asterisco en la declaración son equivalentes, algunas de ellas pueden inducirnos a error, sobre todo si se declaran varios objetos en la misma línea:

int* x, y;

En este caso, x es un puntero a int, pero y no es más que un objeto de tipo int. Colocar el asterisco junto al tipo puede que indique más claramente que estamos declarando un puntero, pero hay que tener en cuenta que sólo afecta al primer objeto declarado, si quisiéramos declarar ambos objetos como punteros a int tendremos que hacerlo de otro modo:

int* x, *y;
// O:
int *x, *y;
// O:
int* x;
int* y;

Obtener punteros a objetos

Los punteros apuntan a objetos, por lo tanto, lo primero que tenemos que saber hacer con nuestros punteros es asignarles direcciones de memoria válidas de objetos.

Para averiguar la dirección de memoria de cualquier objeto usaremos el operador de dirección (&), que leeremos como "dirección de".

Por supuesto, los tipos tienen que ser "compatibles", no podemos almacenar la dirección de un objeto de tipo char en un puntero de tipo int.

Por ejemplo:

int A;
int *pA;

pA = &A;

Según este ejemplo, pA es un puntero a int que apunta a la dirección donde se almacena el valor del entero A.

Objeto apuntado por un puntero

La operación contraria es obtener el objeto referenciado por un puntero, con el fin de manipularlo, ya sea modificando su valor u obteniendo el valor actual.

Para manipular el objeto apuntado por un puntero usaremos el operador de indirección, que es un asterisco (*).

En C++ es muy habitual que el mismo símbolo se use para varias cosas diferentes, este es el caso del asterisco, que se usa como operador de multiplicación, para la declaración de punteros y, como vemos ahora, como operador de indirección.

Como operador de indirección sólo está permitido usarlo con punteros, y podemos leerlo como "objeto apuntado por".

Por ejemplo:

int *pEntero;
int x = 10;
int y;

pEntero = &y;
*pEntero = x; // (1)

En (1) asignamos al objeto apuntado por pEntero en valor del objeto x. Como pEntero apunta al objeto y, esta sentencia equivale (según la secuencia del programa), a asignar a y el valor de x.

Diferencia entre punteros y otros objetos

Debemos tener muy claro, en el ejemplo anterior, que pEntero es un objeto del tipo "puntero a int", pero que *pEntero NO es un objeto de tipo int, sino una expresión.

¿Por qué decimos esto?

Pues porque, como pasa con todos los objetos en C++, cuando se declaran sólo se reserva espacio para almacenarlos, pero no se asigna ningún valor inicial, (recuerda que nuestro puntero para árboles no crea árbol cada vez que señalemos con él). El contenido del objeto permanecerá sin cambios, de modo que el valor inicial del puntero será aleatorio e indeterminado. Debemos suponer que contiene una dirección no válida.

Si pEntero apunta a un objeto de tipo int, *pEntero será el contenido de ese objeto, pero no olvides que *pEntero es un operador aplicado a un objeto de tipo "puntero a int". Es decir, *pEntero es una expresión, no un objeto.

Declarar un puntero no creará un objeto del tipo al que apunta. Por ejemplo: int *pEntero; no crea un objeto de tipo int en memoria. Lo que crea es un objeto que puede contener la dirección de memoria de un entero.

Podemos decir que existe físicamente un objeto pEntero, y también que ese objeto puede (aunque esto no es siempre cierto) contener la dirección de un objeto de tipo int.

Como todos los objetos, los punteros también contienen "basura" cuando son declarados. Es costumbre dar valores iniciales nulos a los punteros que no apuntan a ningún sitio concreto:

int *pEntero = 0; // También podemos asignar el valor NULL
char *pCaracter = 0;

NULL es una constante, que está definida como cero en varios ficheros de cabecera, como "cstdio" o "iostream", y normalmente vale 0L. Sin embargo, hay muchos textos que recomiendan usar el valor 0 para asignar a punteros nulos, al menos en C++.

Comentarios de los usuarios (30)

yemramirezca
2011-01-28 21:42:31

en cuanto a las declaraciones entiendo que

int *punInt es un puntero a un int

de la misma forma que:

char *PunChar seria declarar un puntero a un char

el que no me queda claro es :

struct stPunto *pPunto;

este es un puntero a un struct o un puntero a stPunto.

como es la relación en este caso?????

Gracias!!!

Salvador Pozo
2011-01-28 23:16:19

Yemramirezca:

Todas las declaraciones de variables u objetos tienen la misma forma:

<tipo> <identificador>;

En el caso que indicas, está claro que el identificador es pPunto, por lo tanto, el resto es el tipo: struct stPunto.

En C es obligatorio usar la palabra clave "struct" en las declaraciones de variables de un tipo que sea una estructura, pero en C++ no lo es, por lo tanto, en C++ estas dos declaraciones son equivalentes:

struct stPunto *pPunto;
stPunto *pPunto;

Lo que lleva a errores con la declaración de punteros es el asterisco. En estos ejemplos lo ponemos junto al identificador, pero podemos ponerlo entre ambos o junto al tipo. En cualquier caso, el compilador lo interpreta como parte del identificador, esté el asterisco donde esté.

El equívoco se produce al declarar varias variables en la misma línea:

int* x, y;

Recuerda, el compilador interpreta que el asterisco es parte del identificador (para entendernos), de modo que x es un puntero a un int, pero y es un int, no un puntero.

Por eso preferimos la otra notación:

int *x, y;

Hasta pronto.

Anónimo
2011-02-18 23:12:14

Buenas, tengo una pregunta:

int LongitudCadena(char *cadena)
{    
    int i=0;
    
    while(*cadena++) i++;

    return i;
}

Esta es mi funcion LongitudCadena, que funciona bien siempre que no haya espacios. Porque motivo si pongo la cadena "Hola que tal" me cuenta como si fuera "Hola"? Si pongo cadenas sin espacios me calcula la longitud perfectamente.

Un saludo.

PD: Grandisima esta web!

Steven
2011-02-18 23:47:13

Hola Anónimo,

No veo que haya ningún problema con esta función acerca de si la cadena contiene espacios o no. Lo que sí creo que está ocurriendo es que si lees una cadena desde el teclado con 'cin >>' en la que el usuario introduce espacios, la cadena sólo contiene los primeros caracteres hasta el primer espacio. Por ejemplo,

char szCadena[40];

cin >> szCadena;

Si el usuario escribe: "hola a todos", la cadena 'szCadena' contendrá: "hola". Esto es porque 'cin >>' se comporta de esta manera. En cuanto lea un espacio blanco: ' ' (espacio), '\t' (tabulador), '\r' (retorno), '\n' (nueva línea), '\v' (tabulador vertical), y '\f' (salto de página).

Si quieres leer y guardar espacios blancos, usa la función miembro 'getline()'. Esto es,

cin.getline( szCadena, 40 );

Leerá hasta 39 caracteres, el carácter de "fin-de-fichero" (EOF), o hasta el carácter '\n' (por defecto), el cual no será guardado en la cadena.

Espero que esto te sirva.

Steven

Mario
2011-02-19 01:16:16

Eso es exactamente lo que pasava Steve!!

Muchisimas gracias.

Por cierto, existe alguna funcion (hablando con propiedad seria un metodo?) de cin que lea la entrada de teclado y la almacene en un array sin indicarle el numero de caracteres?

Es decir, algo como

cin.getline(cadena)

... pero que funcione!

scanf? yo aprendi en la universidad a leer de teclado todo usando scanf (antes nunca habia usado cin o cout).

Steven
2011-02-19 02:38:11

Hola Mario,

En C++, solemos hablar de "funciones miembro", pero en otros lenguajes y ya casi en general se habla de "métodos". Elijas cual elijas, te entendemos :)

Para una cadena de caracteres como un array, no existe ninguna función miembro que no acepte la cantidad máxima de caracteres. De hecho, ni aconsejaría usar una, si existiese. En el caso de ANSI C, sí existe 'gets()', pero su uso no es recomendado, incluso por los propios diseñadores de ANSI C.

Sí existe uan forma de guardar todos los posibles caracteres, hasta el carácter de fin-de-línea, en una cadena. Usando la función global 'getline()', podemos pasar una cadena de la clase 'string'; ambas entidades están definidas en <string>. Por ejemplo,

string sCadena;

getline( cin, sCadena );

La clase 'string' representa una cadena dinámica de caracteres. Esto significa que la cadena aumenta de tamaño automáticamente cuando es necesario. Teóricamente, no tiene límite de la cantidad de caracteres que puede almacenar.

Espero que esto te sirva.

Steven

yemramirezca
2011-03-04 23:23:19

HOla!!

Debo hacer un programa que lea una linea de caracteres y diga cuantas palabras repetidas hay....

me dijeron que usara punteros a cadena pero no me funciona...

me podrian guiar por favor????

edwin
2011-03-30 20:46:27

1. Comente sobre cada instrucción del programa siguiente:

#include <stdio.h>

void main()

{

int i, j;

i=90;

j=180;

pt=&j;

*pt=55;

pt=&j;

*pt=66;

}

2. Comente sobre cada instrucción del programa siguiente:

#include <stdio.h>

void main()

{

int a, *pt;

a=7;

pt=&a;

cout << ”La direccion de a es: ” << &a;

cout << ”El contenido de pt es: ” << pt;

cout << ”El valor de a es: ” << a;

cout << ”El valor de pt es: ” << *pt;

cout << ”El valor de &*pt es: ” << &*pt;

cout << ”El valor de *&pt es: ” << *&pt;

}

3. Comente sobre cada instrucción del programa siguiente:

#include <stdio.h>

void main()

{

int a, *pt;

a=17;

pt=&a;

cout << *&pt;

cout << &*pt;

cout << &**&pt;

cout << &*&*pt;

cout << *&*&pt;

cout << *&*&*&pt;

cout << &*&*&*pt;

}

4. Considere el programa siguiente:

#include <sodio.h>

void main()

{

int i;

flota f;

void *pt;

/* modificacion del valor de i */

pt=&i;

*((int *)pt)=12;

/* modificacion del valor de f */

pt=&f;

*((float *)pt)=165.33;

cout<<”El valor de i es ”<<i;

cout<<”El valor de f es ”<<f;

}

a.¿Cómo se debe modificar el programa si se declara pt como un puntero sobre objetos de tipo int?

b. ¿Cómo se debe modificar el programa si se declara pt como un puntero sobre objetos de tipo float?

c. ¿Como se debe modificar el programa si se declara pt como un puntero sobre objetos de tipo char?

5. ¿De acuerdo a la declaración de las variables, de qué tipo son cada una?

int* a,b;

a. a puntero, b puntero

b. a puntero, b entero

c. a entero, b puntero

d. a entero, b entero

6. Considerando las siguientes declaraciones y sentencias:

int array[]={1,2,3,4,5,6};

int *puntero;

puntero = array;

puntero++;

*puntero=*puntero+6;

puntero=puntero+3;

puntero=puntero-puntero[-2];

int x=puntero-array;

a) ¿Cuál es el valor de x?

b) ¿Cuál es el valor de array[1]?

7. Considerando la siguiente declaración:

struct A {

struct {

int x;

int y;

} campoB;

} *estructuraA;

¿Cómo se referenciaría el campo x de la estructura A?

Carlos
2011-07-06 01:42:32

Buenas!! Una pregunta con respecto a la declaración de punteros y su inicialización. Yo había leído que no se debía iniciar un puntero a NULL no recuerdo muy bien porque y que la mejor forma es declarar una variable auxiliar del tipo del puntero (iniciada a 0) e igualar el puntero a la dirección de memoria de dicha variable auxiliar.

Si la inicializo a 0 en vez de a NULL? se solventa el problema? o siempre sería mejor usar la variable auxiliar?

Muchas gracias

Oscar
2012-06-02 20:44:31

hola a todos:

¿Es posible tener estructuras dentro de una función y compartir los objetos con otra función por referencia?

creo yo que seria teniendo un puntero tipo void y haciendo un casting a la hora de enviar el objeto, pero el problema seria el tamaño de la estructura que creo yo es el mismo tamaño del objeto.

void func1();
void func2(void *punt);

int main()
{
func1();
cin.get();
return 0;
}

func1()
{
struct osc{
int a;
}obj;
cout<<"a="<<obj.a<<endl;
func2(&obj);
}

func2(*(int *) putn)
{
cout<<"a="<<punt->b<<endl;
}

Espero haberme explicado bien.

Gracias por cualquier ayuda o comentario.

Liz
2012-06-14 00:17:05

Hola!

alguien me podría ayudar diciéndome si el siguiente programa es correcto? se tienen que utilizar punteros

#include<stdio.h>

#include<conio.h>

void Funcion (int *suma, int *mult);

main()

{

int suma,mult;

Funcion(&suma, &mult);

printf("%d\n",suma);

printf("%d\n",mult);

getch();

}

void Funcion (int *suma, int *mult)

{

int suma1, mult1, a, b;

scanf("%d",&a);

scanf("%d",&b);

suma1=(a)+(b);

mult1=(a)*(b);

*suma=suma1;

*mult=mult1;

}

Salvador Pozo
2012-06-15 15:02:31

Hola, Liz:

En general se podría decir que es "correcto", pero ese es un término un poco ambiguo, al menos en lo que se refiere a la programación.

Algunas consideraciones:

- Olvida la librería "conio", no es estándar y está muy anticuada. En tu programa sólo la usas para la función "getch", que puede ser sustituida sin problemas por la función estándar "getchar".

- Generalmente hay que intentar separar el interfaz de la parte de los algoritmos en el programa, es decir, no es buena idea mezclar en la misma función la captura de datos y los cálculos. Deberías leer los datos a y b en main, y pasarlos como parámetros, por valor, a la función. La función sólo debería hacer los cálculos.

- Evita las variables auxiliares no necesarias. En este caso, las variables locales suma1 y mult1 no las necesitas, ya que podrías haber asignado el resultado de las operaciones directamente a los parámetros:

     *suma=(a)+(b);
     *mult=(a)*(b);

Hasta pronto.

Antonio
2012-12-19 20:53:16

Hola a todos y felicidades a C++ con Clase por el magnifico curso...

soy infógrafo autodidacta y a la par que aprendo C y C++ estoy haciendo un juego RPG con ayuda de las bibliotecas SDL.

Mi duda es la siguiente:

quiero separar el código en partes para hacerlo mas fácil, y comprenderlo mejor, y la pregunta es si me pueden ayudar los punteros a crear nuevas estructuras de datos o generarlas desde algún tipo de "prototipo"...

mi idea es crear un struct con los datos de cada tipo de entidad:

struct player {
  char nombre[31]; // nombre del jugador
  SDL_Surface *frames; //las imagenes del jugador
  ...
 }jugador[?] // inicialmente 1 pero en futuro
 //pretendo que sea multiplayer asique infinitos
struct objetos {
  char nombre[21]; // nombre del objeto
  char descripción[101];
  SDL_Surface *frames; //la/s image/nes del objeto
  unsigned int tipo[x][x]; // tipo de objeto y sus cualidades
  ...}objeto[x];
...
int main(){
CreaVentanaSDL(); //esto es facil (nada del tema)
CreaEntidades(texto_externo); // carga los datos de objetos,
// armas, jugadores,etc., y sus respectivas imagenes.
// a continuación el bucle de juego etc...
} // fin de main
// 
... //este codigo solo es un ejemplo para hacerme entender

mi intención es crear tanos arrais de las estructuras como objetos defina en un doc. de texto externo, esto seria posible? quizas deba apoyarme en alguna librería no estándar dedicada al scripting como lua o algo asi, pero creo que lo que quiero se puede resolver con punteros, ¿algien me puede encaminar o darme alguna pista?

se pueda o no gracias de antemano.

Antonio
2012-12-19 21:11:38

hola de nuevo... se ma ha olvidado decir que quiero hacerlo desde dentro de las funciones... y que los datos afectarían a otras funciones por lo que los struct por lo que se de momento, han de estar o en la función desde que las llame (main, en un principio una pantalla) o ser glovales, no se si me logro explicar... bueno, lo dicho, gracias a todos.

Antonio
2012-12-19 22:15:01

creo que he encontrado la respuesta, en el apartado Objetos dinámicos http://c.conclase.net/curso/?cap=012d#inicio se habla de asignación dinámica de memoria, ya lo he leído y aunque no este muy seguro creo que es lo que buscaba. por lo que mis mensajes pueden ser borrados si el sitio lo estima adecuado... gracias.

Steven R. Davidson
2012-12-20 03:36:44

Hola Antonio,

Efectivamente, deberías usar memoria adjudicada dinámicamente (i.e., en tiempo de ejecución), cuando quieras crear tantos datos como necesite el programa. Por otro lado, si sabes cuántos datos necesitas antes de compilar, entonces puedes usar un array (estático). Eso sí, no siempre necesitas un array como la única estructura de datos para organizar la información.

Sugiero que consultes el curso de Estructuras Dinámicas de Datos (EDD) en nuestra página yendo a: http://c.conclase.net/edd/index.php Podrás ver que existen otras formas de organizar los datos durante la ejecución del programa, las cuales ofrecen ventajas y desventajas para las operaciones que manipulan sus elementos.

Espero que esto te oriente un poco.

Steven

Antonio
2012-12-20 05:16:15

Desde luego se ve interesante, y ya me ha resuelto alguna duda con solo ojear un poco... también echare un vistazo al tema de los objetos y clases, creo que con todo esto no voy solo a lograr el objetivo si no que voy a mejorarlo...

Muchas gracias, sin duda hacéis que esta web sea grande!!

Salvador Pozo
2012-12-20 10:58:57

Hola, Antonio:

Como complemento a lo dicho, quisiera añadir algún comentario.

Las estructuras dinámicas son una forma eficaz de manejar datos estructurados dentro del programa, pero no se debe olvidar que los datos de los que hablas serán modificados con el tiempo, los jugadores cambiarán de lugar, tendrán nuevos objetos, etc. Los objetos también pueden modificarse. Se crearán conjuntos nuevos... En fin, necesitas un modo de almacenar datos entre distintas ejecuciones del juego.

Crear ficheros externos es siempre problemático, de modo que también te aconsejo, al menos como un objetivo a medio plazo, que te familiarices con las bases de datos. En concreto, para el problema que tienes entre manos, SQLite puede ser una muy buena opción.

Lo menciono porque también hay información sobre esos temas en la página:

http://c.conclase.net/sqlite/index.php

http://mysql.conclase.net/ (Sobre SQL)

Suerte.

Antonio
2012-12-20 16:14:11

Caray no lo había pensado... si bien ya he usado antes bases de datos SQL pero en diseño web, y a un nivel muy basico...

Muchas gracias por el aporte, le voy a echar un buen vistazo...

maria
2013-01-10 13:57:16

Hola soy nueva en esto de programar en c++ y queria consultar estoy haciendo un juego de cartas orientado a objetos. mi objeto es el siguiente: #ifndef CcartaH

#define CcartaH

#define NULL 0

class carta {

private:

AnsiString numero[5], palo[10], nombre[19];

carta *siguiente;

public:

carta(AnsiString num[], AnsiString pal[], AnsiString nom[], carta *sig=NULL);

friend class mazo;

friend class mazo_jugador;

};

typedef carta *pcarta;

//---------------------------------------------------------------------------

#endif

#pragma hdrstop

#include "Ccarta.h"

#include<stdio.h>

#define NULL 0

//---------------------------------------------------------------------------

carta::carta( AnsiString num[], AnsiString pal[], AnsiString nom[], carta *sig=NULL){

numero[5]=num[5];

palo[10]=pal[10];

nombre[19]=nom[19];

siguiente=sig; }

#pragma package(smart_init)

y me tira el siguiente error

[BCC32 Error] Ccarta.cpp(11): E2148 Default argument value redeclared for parameter 'sig'

Necesito ayuda porque no lo entiendo

Salvador Pozo
2013-01-10 16:05:39

Hola María:

El problema está en la declaración de un valor por defecto para el parámetro sig del constructor de "carta".

carta(AnsiString num[], AnsiString pal[], AnsiString nom[], carta *sig=NULL);

Cuando se declaran parámetros con un valor por defecto, sólo debe indicarse o bien en el prototipo, o en la definición de la función, pero no en los dos sitios, aunque el valor por defecto sea el mismo.

Personalmente, prefiero hacerlo en el prototipo, ya que es el lugar donde es más probable que se consulte como documentación. De todos modos, no está de más indicarlo también en la definición, en forma de comentario.

Hasta pronto.

yang paul
2013-03-14 04:00:35

hola que tal me dejaron hacer un programa

de DIRECCION DE MEMORIA PARA CADA DATO PRIMITIVO EMPLEADO

que es lo que debo hacer.....

apenas he visto las bibliotecas basicas el stadio.h y el conio.h

como hago el programa??

Luis J. Mora
2013-11-25 23:50:22

Hola, este es un programa que verifica si una palabra es palindrome o no, me aparece un error cuando el programa verifica la condición de "aux->letra != aux2->letra", entonces siempre me la acepta así sea cierta o no y me redefine el valor de pal=0,y así supondremos que no es Palindrome y si lo es, bueno pero al final con toda palabra que inserte me dice que no es palindrome. Espero que me ayude, le agradecería la respuesta..

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>

typedef char string[5];

struct lista{
       string letra;
       struct lista*sig;
       };
       
typedef struct lista L;

int main(){
    int sel, cont=1,pal=1;
    L*aux,*aux2,*aux3,*aux4,*aux5;
    L*cab=NULL;
    string letra;
    printf("Tarea\n\n");    
    MENU:
    printf("\n1) Insertar Letra.\n2) Palindrome.\n");
    scanf("%d",&sel);
    switch(sel){
                case 1:
                     if(cab==NULL){
                                   cab=(L*)malloc(sizeof(L));
                                   printf("Inserte: ");
                                   scanf("%s",&letra);
                                   strcpy(cab->letra,letra);
                                   cab->sig=NULL;
                                  }
                     else         {
                                   aux5=cab;
                                   while(aux5->sig!=NULL){
                                                          aux5=aux5->sig;
                                                         }
                                   aux4=(L*)malloc(sizeof(L));
                                   printf("Inserte: ");
                                   scanf("%s",&letra);
                                   strcpy(aux4->letra,letra);
                                   aux5->sig=aux4;
                                   aux4->sig=NULL;
                                  }
                                  goto MENU;
                case 2:
					aux=cab;
					while(aux->sig!=NULL)
					{
						aux=aux->sig;
						cont++;
					}
					aux2=cab;
					aux=cab;
					while(aux2->sig!=NULL)
					{
						aux2=aux2->sig;
					}
					if(cont%2==0)
					{
						while(pal==1&&aux2->sig!=aux)
						{
							aux3=cab;
							while(aux3->sig!=aux2)
							{
								aux3=aux3->sig;
							}
							if((aux->letra)!=(aux2->letra))
							{
								pal=0;
							}
							aux=aux->sig;
							aux2=aux3;
						}
						if(pal==1)
						{
							printf("\n\nEs Palindrome.");
						}
						else
						{
							printf("\n\nNo es Palindrome.");
						}
					}
					else
					{
						while(pal==1&&aux2!=aux)
						{
							if(aux->letra!=aux2->letra)
							{
								pal=0;
							}
							aux3=cab;
							while(aux3->sig!=aux2)
							{
								aux3=aux3->sig;
							}
							aux=aux->sig;
							aux2=aux3;
						}
						if(pal==1)
						{
							printf("\n\nEs Palindrome.");
						}
						else
						{
							printf("\n\nNo es Palindrome.");
						}
					}
	}
    getch();    
}                                                           
Steven R. Davidson
2013-11-26 18:37:23

Hola Luis,

El error es que 'letra' es un array, por lo que está comparando arrays directamente. Esto significa que comparas sus direcciones de memoria, y no sus contenidos. Necesitas usar la función 'strcmp()' para comparar la información guardada en ambas cadenas.

Además, debo matizar algunos detalles del código fuente:

- No estoy muy seguro de por qué quieres representar una sola letra como una cadena de 5 caracteres. Realmente debería ser 'char' y no 'char[5]'.

- No uses 'goto'. Es un vocablo que no se debería usar. En tu caso, no es necesario usar 'goto' ya que te interesa usar un bucle: for, while, o do/while.

- Escribes:

string letra;
...
scanf( "%s", &letra );

Como 'letra' es un array, no tiene sentido usar el operador & para conseguir su dirección de memoria, porque un array ES una dirección de memoria. Simplemente escribe:

scanf( "%s", letra );

- No es necesario definir tantas variables auxiliares. Podemos definir las auxiliares en el momento que se necesitan, creando un bloque escribiendo la pareja de llaves {}. Por ejemplo,

else
{
  aux5 = cab;
  while( aux5->sig != NULL )
  {
    aux5 = aux5->sig;
  }
  aux4 = (L*)malloc( sizeof(L) );
  ...
}

Podemos escribir:

else
{
  L *aux = cab;
  while( aux->sig != NULL )
    aux = aux->sig;

  aux->sig = (L*)malloc( sizeof(L) );
  aux = aux->sig;
  // Usamos 'aux' a partir de ahora, en lugar de 'aux->sig'
  ...
}

Además, como puedes ver, no necesitamos 'aux4'; podemos usar 'aux' directamente.

- Para el caso #2, debes comprobar que la lista existe antes de usarla; de lo contrario, puede haber errores durante la ejecución del programa, ya que los punteros serían nulos.

- Compruebas si la cantidad de nodos es par o impar. Podrías optar por comprobar ambos punteros. Por ejemplo,

while( pPrincipio != pUltimo && pPrincipio->sig != pUltimo && ... )
{ ... }

Es parecido a lo que hiciste en el caso par, pero puedes hacer esta comprobación para ambos casos.

Si decides usar 'cont' para la paridad, entonces sugiero no realizar dos bucles para encontrar el final. Escribes:

aux = cab;
while( aux->sig != NULL )
{
  aux = aux->sig;
  cont++;
}
aux2 = cab;
aux = cab;
while( aux2->sig != NULL )
{
  aux2 = aux2->sig;
}

Cuando es más eficiente escribir un solo bucle:

cont = 1;
aux = aux2 = cab;
while( aux2->sig != NULL )
{
  aux2 = aux2->sig;
  cont++;
}

Como puedes ver, también aconsejo realizar la asignación inicial a 'cont'. Esto nos ayuda a pensar de forma modular y si quieres meter todo este código en un bucle, para repetir esta misma operación, no tendremos sorpresas luego.

- El otro problema que tienes es que no liberas la memoria que creaste. Necesitas recorrer la lista eliminando - con cuidado - cada nodo. Usa 'free()'.

- Sugiero definir funciones para realizar estas operaciones básicas para manipular listas enlazadas. Esto es lo que se llama crear un TDA: Tipo de Dato Abstracto (ADT, en inglés). Así creamos una interfaz legible, más clara, y modular.

Espero que todo esto te oriente.

Steven

Luis J. Mora
2013-11-26 19:24:37

Muchas gracias!

Luis J. Mora
2013-12-03 20:07:53

Hola, si me pudieran ayudar con este problema, lo que no logro entender es como se puede introducir un archivo de texto, leerlo y almacenarlo en una structura que a la vez corresponde a una direccion de memoria...

1. Se tiene un archivo de entrada donde se encuentran los datos de una agenda, se pide que realice un procedimiento que lea el archivo y cargue todos los contactos en una lista enlazada y un método de búsqueda para encontrar el contacto que este repetido dentro de esta lista. Asuma que las primitivas ya están definidas y hay un solo contacto repetido.

Formato del Archivo:

Nombre1

Telefono1

Nombre 2

Telefono2

Steven R. Davidson
2013-12-03 20:24:27

Hola Luis,

Creo que el problema es que crees que la lista dinámicamente enlazada se guarda tal cual en el fichero, junto con el miembro del puntero al "siguiente nodo". Esto no es el caso. El formato del fichero no guarda una lista enlazada y de hecho, como puedes ver, el formato que te dan no indica ninguna dirección de memoria. La lista enlazada es creada y usada por el programa, no por el fichero.

En general, uno debería crear esta estructura para representar un nodo de una lista enlazada:

struct dato { ... };

struct nodo
{
  dato info;
  nodo *pSig;
};

Así separamos la información a guardar, que en este caso es la estructura 'dato', de los otros miembros de la estructura 'nodo' que representa un nodo a usarse en una lista enlazada.

Espero que esto te oriente.

Steven

Felipe
2014-07-06 04:08:58

Estimados, necesito saber qué significa cuando el operador de referencia está a la derecha de una variable, es decir, en un código tengo lo siguiente

if(variable&1){

.........

}

Donde variable & y el 1 están todos juntos, desde ya muchas gracias

Steven R. Davidson
2014-07-06 05:43:14

Hola Felipe,

No se trata del operador de referencia sino del operador binario de la operación AND a nivel de bits. Explicamos este operador en el capítulo 18 ( http://c.conclase.net/curso/index.php?cap=018#inicio ).

En el ejemplo que escribes, la expresión sirve para conseguir el último bit del valor guardado en 'variable'. Luego comprobamos si el resultado es un 1 (verdadero) o un 0 (falso).

Espero que esto te aclare la duda.

Steven

lz
2014-11-19 15:44:09

buenas necesito ayuda con un programa que sea de cualquier cosa pero hecho en funciones y que tenga 5,6,7 .... funciones y que use pase por parametros, pase de valor y referrencial.. no se como haria para incluir estos 3 ..