[DUDA] Punteros

Iniciado por kayron8, 24 Marzo 2014, 12:25 PM

0 Miembros y 2 Visitantes están viendo este tema.

kayron8

Hola buenos días,

Llevo todo el fin de semana dándole vueltas a un ejercicio de punteros que no hay forma humana de que me funcione correctamente, el caso es que en el tema de punteros ando un poco pez, por lo que me debería de poner las pilas.

El ejercicio en cuestión, dice lo siguiente: Dada una frase, se contará el numero de as, es, is, os y us que tiene. La cantidad de cada vocal se almacenará en un vector de 5 enteros. Se
mostrará una estadística de vocales.

La manipulación de la frase y del vector se hará con punteros.
La declaración de variables será así:
(···)
char frase[256], *pf=frase;
int vocales[5];
int * pv;
pv = &vocales[0];
(···)

Ahora bien, os comento que haciendo el ejercicio sin punteros, me funciona perfectamente, dejo el código para que sirva de ejemplo:


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

//Prototipo
void cuentavocales(char v[]);

int main()
{
 char frase[256];
 cuentavocales(frase);
 
 system("PAUSE");
 return 0;
}

//Función que cuenta les vocales de la frase
void cuentavocales(char frase[])
{
    int i=0,vocales[5]={0};
   
    printf("Introduce una frase: \n");
    gets(frase);
    for(i=0;i<strlen(frase);i++)
{
          if(frase[i]=='a'|| frase[i]=='A') vocales[0]++;
          else if(frase[i]=='e' || frase[i]=='E') vocales[1]++;
          else if(frase[i]=='i' || frase[i]=='I') vocales[2]++;
          else if(frase[i]=='o' || frase[i]=='O') vocales[3]++;
          else if(frase[i]=='u' || frase[i]=='U') vocales[4]++;
   }
//Mostramos los resultados por pantalla
printf("Estadística \n");
printf("as: %d \n",vocales[0]);    
printf("es: %d \n",vocales[1]);
printf("is: %d \n",vocales[2]);
printf("os: %d \n",vocales[3]);    
printf("us: %d \n",vocales[4]);
printf("\n TOTAL: %d\n",vocales[0]+vocales[1]+vocales[2]+vocales[3]+vocales[4]);
}


Ahora bien, pasamos al apartado de los punteros... No se si lo que he hecho está bien o es una barbaridad...


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

//Prototipo
void cuentavocales(char v[5]);

int main()
{
 char frase[256],*pf=frase;
 cuentavocales(frase);
 
 system("PAUSE");
 return 0;
}

//Función que cuenta las vocales de la frase
void cuentavocales(char frase[5])
{
    int i=0,vocales[5]={0};
    int * pv;
    pv = &vocales[0];
   
    printf("Introduce una frase: \n");
    gets(frase);
    for(i=0;i<strlen(frase);i++)
{
  if(frase[i]=='a'|| frase[i]=='A') {
  pv++;
  }
         
  else if(frase[i]=='e' || frase[i]=='E') {
  pv=pv+1;
  pv++;
  }
         
  else if(frase[i]=='i' || frase[i]=='I') {
  pv=pv+2;
  pv++;
  }
 
  else if(frase[i]=='o' || frase[i]=='O') {
  pv=pv+3;
  pv++;
  }
         
  else if(frase[i]=='u' || frase[i]=='U'){
  pv=pv+4;
  pv++;
  }
   }

//Mostramos los resultados por pantalla
printf("Estadística \n");
printf("as: %d \n",*pv);    
printf("es: %d \n",*(pv+1));
printf("is: %d \n",*(pv+2));
printf("os: %d \n",*(pv+3));  
printf("us: %d \n",*(pv+4));
printf("\n TOTAL: %d\n",*pv+*(pv+1)+*(pv+2)+*(pv+3)+*(pv+4));
}


Compilar, compila pero da unos resultados que no son nada correctos xD

Espero que me podáis echar un cable.

Un saludo :D
¡Salu2 a to2!

eferion

Mira que dudo yo que el primer ejemplo te funcione...

int i=0,vocales[5];

En qué momento se supone que inicializas los valores para "vocales"??

Y bueno, pasando al segundo código, te cuento.

Imagínate que tienes un puntero, no se, algo tal que


int * pv;
pv = &vocals[0];


Si tu depuras, pv tendrá algo tal que...


pv = 0x45672240 // Posicion de memoria apuntada por pv
*pv = 234523 // Valor de vocals[ 0 ] porque no la has inicalizado


Si tu coges pv y, por ejemplo, haces:

pv++;

entonces pv queda tal que:


pv = 0x45672244 // Nueva posicion, ahora es vocals[1]
*pv = -345023 // vocals[ 1 ] tampoco esta inicializada, tiene un valor aleatorio


Es decir, con pv++, pv = pv+6 y cosas así únicamente modificas la posición de memoria a la que apunta, pero no modificas el valor de esa posición de memoria apuntada.

Sin embargo, ahora imagínate ( para simplificar el ejemplo ) que has inicializado vocals. Entonces pv queda algo así:


pv = 0x45672240 // Posicion de memoria apuntada por pv
*pv = 0// Valor de vocals[ 0 ]


y ahora haces lo siguiente:

*pv = *pv + 1

Ahora la cosa queda tal que


pv = 0x45672240 // Posicion de memoria apuntada por pv ( no ha cambiado )
*pv = 1// Valor de vocals[ 0 ]


Que es el resultado esperado si lo que quieres es contar apariciones.

Ahora bien, esto te sirve para el valor apuntado por pv... pero si ahora nos encontramos con algo tal que:


else if(frase[i]=='e' || frase[i]=='E')
{
  pv=pv+1; // Esta linea no la he cambiado
  *pv = *pv + 1;
}


Entonces, efectivamente, haces que pv apunte a vocals[1] e incrementas el contador de la 'e'... pero después, pv se queda apuntando al contador de 'e'... imagínate que te encuentras con una frase tal que 'ee'... al ejecutar el código verás que te va a contar una e y una i... ¿por que? pues porque NO DEBES hacer pv = pv + 1. O al menos, si lo haces, restaura pv a su posición original...

Una recreación del fallo:

* Situacion original:


pv = 0x45672240
*pv = 0


Primera 'e':


pv = 0x45672244 // pv = pv + 1
*pv = 1 // *pv++


Segunda 'e'


pv = 0x45672248 // pv = pv + 1 MAL, deberia ser 0x45672244, este es el contador de 'ies'
*pv = 1


No se si ha quedado claro lo que pretendo transmitir.

kayron8

#2
Hola eferion,

Gracias por tu explicación. El ejercicio anterior di que me funciona correctamente, el problema es que había echo un copy paste de una versión antigua. Faltaba inicializar el vector vocales:


int vocales[5]={0};


Respecto el tema de los punteros más o menos entiendo que es lo que hago mal, pero no consigo arreglar el problema que tengo :( Soy un negado para la programación y me cuesta bastante entender las cosas.

Se que no eres un profesor (al menos en el foro) y no recibes un salario por ello, pero me gustaría que me ayudases otro poco, sí es posible, para acabar de comprender el ejercicio.

Un saludo y muchas gracias :D
¡Salu2 a to2!

eferion

Efectivamente, ni soy profesor ( ni en el foro ni en "la vida real" jejeje ) ni tampoco cobro por ayudar en la medida de mis posibilidades.

Sin embargo tu puedes preguntar tus dudas que aquí te ayudaremos lo mejor que podamos.

PD.: si alguien se apiada de mí, acepto donaciones encantado XDDDD

kayron8

Cita de: eferion en 24 Marzo 2014, 13:46 PM
PD.: si alguien se apiada de mí, acepto donaciones encantado XDDDD

No iría nada mal y menos en los tiempos que corren xDD

Bueno, regresando a mi cruda realidad, me gustaría que me echaras un cable para ayudarme a detectar el/los problema/s que pueda tener mi programa.

Buscando por éste fantástico foro, encontré éste ejemplo que es la mar de simple (lo entiendo perfectamente): http://foro.elhacker.net/programacion_cc/duda_contador_de_vocales_mediosolucionado_xd-t312146.0.html;msg1551059#msg1551059

Pero veo mi ejercicio y la declaración de variables que hay que hacer por narices y no se cómo continuar :(

Un saludo y muchas gracias :D
¡Salu2 a to2!

leosansan

Cita de: kayron8 en 24 Marzo 2014, 13:55 PM
.............................................................
Bueno, regresando a mi cruda realidad, me gustaría que me echaras un cable para ayudarme a detectar el/los problema/s que pueda tener mi programa.
......................................................
Pero veo mi ejercicio y la declaración de variables que hay que hacer por narices y no se cómo continuar :(
.............................................

Si tienes en cuenta la relacion intima entre punteros y arrays no te pasaria esto:

Código (cpp) [Seleccionar]
*(pv+2))=vocales[2]=0

Y lo que quieres hacer es incrementar vocales[2] que lo harias como
vocales[2]++;

Pues con el puntero lo mismo:

(*(pv+2))++;


Con esta idea sale el siguiente código:

Código (cpp) [Seleccionar]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//Prototipo
void cuentavocales(char v[5]);

int main()
{
  char frase[256],*pf=frase;
  cuentavocales(frase);
  system("PAUSE");
  return 0;
}

//Función que cuenta las vocales de la frase
void cuentavocales(char frase[5])
{
     int i=0,vocales[5]={0};
     int * pv,pv0;
     pv = &vocales[0];

    pv0=pv;
     printf("Introduce una frase: \n");
     gets(frase);
     for(i=0;i<strlen(frase);i++){
       if(frase[i]=='a'|| frase[i]=='A')
        (*(pv+0))++;
      else if(frase[i]=='e' || frase[i]=='E')
        (*(pv+1))++;
      else if(frase[i]=='i' || frase[i]=='I')
       (*(pv+2))++;
       else if(frase[i]=='o' || frase[i]=='O')
        (*(pv+3))++;
      else if(frase[i]=='u' || frase[i]=='U')
        (*(pv+4))++;
    }

//Mostramos los resultados por pantalla
printf("Estadistica \n");
printf("a: %d \n",*pv);
printf("e: %d \n",*(pv+1));
printf("i: %d \n",*(pv+2));
printf("o: %d \n",*(pv+3));
printf("u: %d \n",*(pv+4));
printf("\n TOTAL: %d\n",*pv+*(pv+1)+*(pv+2)+*(pv+3)+*(pv+4));
}







Yoel Alejandro

#6
Kayron,

Para explicarte un poco las cosas, un arreglo es un puntero. O sea, cuando declaras:

int x[3];

entonces "x" no es más que un apuntador al primer elemento del arreglo. Entonces será lo mismo:

*x que x[0]
*(x+1) que x[1]
*(x+2) que x[2]

En *(x+1) entre los paréntesis se realiza aritmética de punteros para incrementar en 1 en valor del puntero (la dirección física del dato). O sea, que x+1 contiene la dirección del dato x[1]. Ojo, la dirección no el dato en sí mismo. Para acceder el valor contenido en dicha dirección debemos usar el operador de indirección "*". Por eso, *(x+1)

Espero lo hayas podido entender clarito, jeje. Ahora, te cuento que hay gente que escribe &x[0] que es redundante, es lo mismo que x; del mismo modo &x[1] es lo mismo que x+1.

En tu programa si quieres usar exclusivamente puntero y no indexación de arreglos, debes disponer de un apuntador a char para recorrer los caracteres de la frase. Ese creo que lo llamaste fp. Su valor inicial es frase, de este modo queda apuntado al primer elemento del arreglo. Luego, las expresiones

frase

se han de cambiar por *fp y en cada ciclo el valor del puntero es incrementado con fp++ para que direccione a la siguiente letra de la frase.

Para el caso de las vocales, el mismo arreglo vocales sirve como puntero (!!!).
La expresión

vocales[ i ]++

se cambia por (*(vocales + i))++ con cada valor de i desde 0 hasta 4. Los dobles paréntesis son necesarios porque el operador de incremento "++" tiene igual precedencia que el de indirección "*", pero se evalúa de derecha a izquierda. De colocar:

*(vocales + i)++

se hubiera interpretado *((vocales + i)++) que no es lo que queremos.

El código:
Código (cpp) [Seleccionar]

void cuentavocales(char frase[])
{
   int vocales[5] = {0};
   char * fp;

   printf("Introduce una frase: \n");
   gets(frase);

   /* La idea es que fp recorra el arreglo frase. Se inicializa al primer
    * ELEMENTO (no el primer INDICE) del arreglo. */
   fp = frase;

   while ( *fp != '\0' )
   {
      if (*fp=='a'|| *fp=='A') (*vocales)++;
      else if (*fp=='e' || *fp=='E') (*(vocales + 1))++;
      else if (*fp=='i' || *fp=='I') (*(vocales + 2))++;
      else if (*fp=='o' || *fp=='O') (*(vocales + 3))++;
      else if (*fp=='u' || *fp=='U') (*(vocales + 4))++;

      fp++; /* aumentamos el puntero que recorre la frase */
   }
   //Mostramos los resultados por pantalla
   printf("Estadística \n");
   printf("as: %d \n", *vocales);
   printf("es: %d \n", *(vocales + 1));
   printf("is: %d \n", *(vocales + 2));
   printf("os: %d \n", *(vocales + 3));
   printf("us: %d \n", *(vocales + 4));
   printf("\n TOTAL: %d\n", *vocales + *(vocales + 1) + *(vocales + 2)
          + *(vocales + 3) + *(vocales + 4) );
}


Ahora no usas índices en ninguna parte, sólo punteros. Por cierto, lo probé y funciona correctamente.

================================
EDITO:

Estaba revisando tu código y la verdad es que le faltaba poco para funcionar. Por ejemplo, para incrementar el conteo de la vocal 'e' reemplaza

pv=pv+1;
pv++;

por

*(pv + 1) = *(pv + 1) + 1;

lo cual es completamente equivalente a

pv[1] = pv[1] + 1;

También puedes sintetizarlo a:

(*(pv+1))++;


Quedaría entonces:
Código (cpp) [Seleccionar]

void cuentavocales2(char frase[5])
{
   int i=0,vocales[5]= {0};
   int * pv;
   pv = vocales;

   printf("Introduce una frase: \n");
   gets(frase);
   for(i=0; i<strlen(frase); i++)
   {
      if(frase[i]=='a'|| frase[i]=='A')
         (*pv)++;
      else if(frase[i]=='e' || frase[i]=='E')
         (*(pv+1))++;
      else if(frase[i]=='i' || frase[i]=='I')
         (*(pv+2))++;
      else if(frase[i]=='o' || frase[i]=='O')
         (*(pv+3))++;
      else if(frase[i]=='u' || frase[i]=='U')
         (*(pv+4))++;
   }

//Mostramos los resultados por pantalla
   printf("Estadística \n");
   printf("as: %d \n",*pv);
   printf("es: %d \n",*(pv+1));
   printf("is: %d \n",*(pv+2));
   printf("os: %d \n",*(pv+3));
   printf("us: %d \n",*(pv+4));
   printf("\n TOTAL: %d\n",*pv+*(pv+1)+*(pv+2)+*(pv+3)+*(pv+4));
}


Tu código anterior estaba malo porque con

pv = pv + 1;
pv++;

estás incrementando los valores del puntero, o sea las DIRECCIONES de los datos, y no los datos en sí mismos. Tal vez con

pv = pv + 1;
(*pv)++;

donde modificas pv para que apunte a la posición de la vocal 'e', y luego incrementas el valor en el arreglo. El problema es que ya el valor del puntero te queda alterado, no apunta a vocales[0] sino a vocales[1]. Tendría que ser en todo caso

pv = pv + 1;
(*pv)++;
pv = pv - 1;

donde "acomodas" de nuevo el valor del puntero. Pero eso es completamente equivalente a

*(pv + 1) = *(pv + 1) + 1;

:)
Saludos, Yoel.
P.D..-   Para mayores dudas, puedes enviarme un mensaje personal (M.P.)

kayron8

Muchísimas gracias a todos por el esfuerzo que habéis hecho con mi problema.

¡Jo! cómo me gustaría a mi tener esa habilidad que tenéis vosotros de resolver problemas cómo si se tratase de abrir un frasco de garbanzos xDD

Le pongo todas las ganas que puedo pero es que no hay manera de que me entre, debo de ser tonto o no se... U.u''

Nuevamente, daros las gracias a todos y cada  uno de vosotros por ser mejor que mi profe (espero que no llegue a leer esto) xDD

Un saludo :D
¡Salu2 a to2!

Yoel Alejandro

Modestia aparte kayron  :-[. Luego que pases años programando como nosotros, lo harás igual jajaja  :D
Saludos, Yoel.
P.D..-   Para mayores dudas, puedes enviarme un mensaje personal (M.P.)