Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: m@o_614 en 17 Julio 2013, 02:37 am



Título: scanf y gets
Publicado por: m@o_614 en 17 Julio 2013, 02:37 am
saludos

si tengo que ingresar en un vector 10 datos, por ejemplo: nombres, estos nombres tienen diferente longitud entonces tendria que poner char *nombre[10] pero a la hora de querer ingresar los datos con gets me aparecen warnings

Código
  1. for(i=0;i < 10;i++)
  2.    {
  3.        printf("Dame el nombre numero %d: ",i+1);
  4.        gets(nombre+i);
  5.        system("cls");
  6.    }


pero si cambio el gets por scanf de esta manera ya no me aparece y no entiendo porque

Código
  1. for(i=0;i < 10;i++)
  2.    {
  3.        printf("Dame el nombre numero %d: ",i+1);
  4.        scanf("%s",nombre+i);
  5.        system("cls");
  6.    }

si alguien fuera tan amable de explicarme esto se lo agradeceria mucho


Título: Re: scanf y gets
Publicado por: amchacon en 17 Julio 2013, 02:43 am
Pues sin saber el tipo de variable y sin ver el warning no te podemos ayudar mucho.

Eso sí, gets está anticuada y no está recomendada su uso. En su lugar es preferible usar fgets:
http://www.cplusplus.com/reference/cstdio/fgets/

Que vendría a ser:
Código
  1. fgets(nombre,10,stdin); // Leer del teclado 10 caracteres y meterlos en nombre


Título: Re: scanf y gets
Publicado por: m@o_614 en 17 Julio 2013, 03:11 am
saludos amchacon el tipo de dato es char *arreglo[10] y el warning dice:

passing argument 1 of gets from incompatible pointer type

esto en la linea
Código
  1. gets(nombre+i);

y tambien sale una nota que dice expected char* but argument is of type char**

gracias


Título: Re: scanf y gets
Publicado por: eferion en 17 Julio 2013, 10:59 am
*arreglo[10] es un vector de punteros... equivalente ( que no igual ) a **arreglo, es decir, "char**"

si tu en una instrucción pones "nombre + i"... el tipo resultante sigue siendo char**, que es incompatible con el que espera gets ( char* )

Para resolver eso tienes que quitar un nivel de indirección en el puntero, es decir, pasar de un puntero doble a un puntero simple.

¿De qué manera has definido la variable "arreglo"?

Sin más información es complicado darte una explicación más acertada.


Título: Re: scanf y gets
Publicado por: aguml en 17 Julio 2013, 11:19 am
gets(*nombre[i*10]);

Aunque es solo una suposicion porque sin ver la declaracion...


Título: Re: scanf y gets
Publicado por: m@o_614 en 17 Julio 2013, 21:25 pm
eferion como es eso de que *arreglo[10] es equivalente a **arreglo???


Título: Re: scanf y gets
Publicado por: m@o_614 en 17 Julio 2013, 21:32 pm
y otra duda tambien de un vector de punteros

por ejemplo si tengo

Código
  1. char *meses[3] ={"Enero","Febrero","Marzo"};
  2. for(i=0;i < 3;i++)
  3.    printf("%s",*meses+i);


aqui me imprime Enero Nero Ero

pero si a la linea del printf le agrego unos parentesis
Código
  1. printf("%s",*(meses+i));

me imprime correctamente Enero Febrero Marzo

no entiendo por que
     


Título: Re: scanf y gets
Publicado por: eferion en 17 Julio 2013, 23:53 pm
y otra duda tambien de un vector de punteros

por ejemplo si tengo

Código
  1. char *meses[3] ={"Enero","Febrero","Marzo"};
  2. for(i=0;i < 3;i++)
  3.    printf("%s",*meses+i);


aqui me imprime Enero Nero Ero

pero si a la linea del printf le agrego unos parentesis
Código
  1. printf("%s",*(meses+i));

me imprime correctamente Enero Febrero Marzo

no entiendo por que
     

Si tu dibujas el mapa de memoria tendrás que lo que el vector se encuentra estructurado así ( cada letra una posición de memoria ):

'E' 'n' 'e' 'r' 'o' '\0' 'F' 'e' 'b' 'r' 'e' 'r' 'o' '\0' 'M' 'a' 'r' 'z' 'o' '\0'

y dado que "meses" es una matriz de cadena de caracteres, se cumple que:

*meses es igual a *meses + 0 y apunta al primer carácter ( 'E' ).
*meses + 1 es un desplazamiento de una posición respecto a *meses, es decir,  apunta al segundo carácter ( 'n' )
*meses + 2 es un desplazamiento de dos posiciones respecto a *meses, es decir,  apunta al tercer carácter ( 'e' )
...

Es decir, primero resuelves el puntero *meses y luego incrementas la posición apuntada.

Con esto, si intentas imprimir las cadenas resultantes te sale:

Enero, nero, ero, ro, o, (nada), Febrero, ebrero, ...

Sin embargo, *( meses + i ) es equivalente a moverte primero una fila y apuntar al caracter que esté en esa posición, de tal forma que:

*(meses + 0) o *meses apunta a 'E'
*(meses + 1) apunta a 'F'
*(meses + 2) apunta a 'M'

Al final el cambio viene promovido porque los paréntesis cambian el orden en el que se ejecutan las operaciones... no es lo mismo resolver un puntero doble y luego incrementar su posición que incrementar un puntero doble y resolver su posición.


Título: Re: scanf y gets
Publicado por: m@o_614 en 18 Julio 2013, 20:23 pm
Muchas gracias eferion por tus respuestas ahora ya me quedo claro, solo es cuestion de repasar las prioridades de los operadores (),[],*,+, etc...


Una ultima pregunta cuando yo declaro un arreglo de punteros char *a[5]; para despues con un for ingresarle cada uno de los elementos (a+1),(a+2),etc..., primero tengo que asignarle memoria dinamica con malloc, calloc ???


Título: Re: scanf y gets
Publicado por: Caster en 18 Julio 2013, 23:25 pm
Una ultima pregunta cuando yo declaro un arreglo de punteros char *a[5]; para despues con un for ingresarle cada uno de los elementos (a+1),(a+2),etc..., primero tengo que asignarle memoria dinamica con malloc, calloc ???

Si, esto seria asi:

Código
  1. a[i] = (char *) malloc(n * sizeof(char));

Y para ingresar cada elemento con un bucle for:

Código
  1. int i;
  2. for (i=0; i<5; i++)
  3.    {
  4.        scanf("%s", a[i]);
  5.    }


Título: Re: scanf y gets
Publicado por: m@o_614 en 19 Julio 2013, 01:13 am
Caster si es un arreglo de punteros tengo que asignarle memoria a cada uno de los 5 punteros de char *a[5]????por eso pusiste:

Código
  1. a[i] = (char *) malloc(n * sizeof(char));


y la n para que es???

gracias


Título: Re: scanf y gets
Publicado por: Caster en 19 Julio 2013, 01:23 am
No llevo mucho tiempo manejando los arrays de punteros, pero si, creo que es obligatorio asignar memoria a todos los punteros. La "n" es el numero de elementos de cada array. Si el array a[0] tiene 10 elementos, n = 10, si el array a[1] tiene 12 elementos, n=12. Al hacer la reserva de memoria con un ciclo for, el numero de elementos reservados es igual para todos los arrays, si por el contrario quieres reservar distinto numero de elementos para cada array, tendras que ir uno por uno.


Título: Re: scanf y gets
Publicado por: m@o_614 en 19 Julio 2013, 06:48 am
entonces si por ejemplo quiero ingresarle no se... 5 nombres cualquieras:

char *a[5]
 
*(a+0)="Maria";
*(a+1)="Jose";
etc...

pero no quiero estar contando el numero de letras cada vez que tenga que asignar memoria para cada nombre, como se le asignaria la memoria??

perdona que pregunte tanto pero es que los punteros no se me dan


Título: Re: scanf y gets
Publicado por: eferion en 19 Julio 2013, 08:40 am
entonces si por ejemplo quiero ingresarle no se... 5 nombres cualquieras:

char *a[5]
 
*(a+0)="Maria";
*(a+1)="Jose";
etc...

pero no quiero estar contando el numero de letras cada vez que tenga que asignar memoria para cada nombre, como se le asignaria la memoria??

perdona que pregunte tanto pero es que los punteros no se me dan

Los arrays de caracteres no se pueden copiar "a pelo" con el operador igual... tienes que utilizar funciones de copia ( strcpy, memcpy, etc ).

Para el caso que planteas necesitarías tener por un lado el array de cadenas de caracteres y por otro una variable que te indique cuántas posiciones están ocupadas. Esto mismo lo puedes agrupar en una estructura para que su uso sea más cómodo:

Código
  1. struct cadenas
  2. {
  3.  // De las dos opciones siguientes elige una:
  4.  
  5.  // * Si quieres gestionar la memoria dinámicamente con malloc y free
  6.  char **a;
  7.  
  8.  // * Si quieres que de la gestión de la memoria se encargue el sistema
  9.  char a[5][20];
  10.  
  11.  int index;
  12. };

De esta forma, si inicializas index a 0 y lo vas incrementando después de añadir un nombre al vector, gestionar las cadenas se vuelve una tarea bastante sencilla:

Código
  1. struct cadenas cadena;
  2.  
  3. // ...
  4.  
  5. // Nuevo elemento
  6. strcpy( cadena.a[ cadena.index ], "nombre" );
  7. cadena.index++;
  8.  
  9. // Preguntar por el numero de elementos
  10. int numero_elementos = cadena.index;
  11.  
  12. // Recorrer todos los elementos
  13. int i;
  14. for ( i=0; i<cadena.index; ++i )
  15. {
  16.  // ...
  17. }


Título: Re: scanf y gets
Publicado por: rir3760 en 19 Julio 2013, 17:24 pm
eferion como es eso de que *arreglo[10] es equivalente a **arreglo???
Porque en C en la mayoría de los casos el uso de un array resulta en la dirección en memoria de su primer elemento. El caso usual es la llamada a funcion. Por ejemplo:
Código
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. void imprimir(char const *texto);
  5.  
  6. int main(void)
  7. {
  8.   char linea[] = "Texto de prueba\n";
  9.  
  10.   imprimir(linea);
  11.  
  12.   return EXIT_SUCCESS;
  13. }
  14.  
  15. void imprimir(char const *texto)
  16. {
  17.   int i;
  18.  
  19.   for (i = 0; texto[i] != '\0'; i++)
  20.      putchar(texto[i]);
  21. }

Tu caso es similar: si el array se declara como "char *arreglo[10]" en expresiones su nombre resulta en un puntero al primer elemento del array ("char *arreglo[10]" ==> "char **")

----

Una ultima pregunta cuando yo declaro un arreglo de punteros char *a[5]; para despues con un for ingresarle cada uno de los elementos (a+1),(a+2),etc..., primero tengo que asignarle memoria dinamica con malloc, calloc?
No exactamente.

Antes de utilizar una variable de tipo puntero debes almacenar en esta una dirección de memoria valida, esta puede ser la retornada por la función malloc, la dirección de algún objeto o una cadena literal. Por ejemplo:
Código
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. void imprimir(char const *texto);
  5.  
  6. int main(void)
  7. {
  8.   char *nombre[3];
  9.   int i;
  10.  
  11.   nombre[0] = "Jesus\n";
  12.   nombre[1] = "Maria\n";
  13.   nombre[2] = "Jose\n";
  14.  
  15.   for (i = 0; i < 3; i++)
  16.      imprimir(nombre[i]);
  17.  
  18.   return EXIT_SUCCESS;
  19. }
  20.  
  21. void imprimir(char const *texto)
  22. {
  23.   int i;
  24.  
  25.   for (i = 0; texto[i] != '\0'; i++)
  26.      putchar(texto[i]);
  27. }
Pero en este caso con el detalle de que las cadenas literales son de solo lectura, no se puedes hacer mas con ellas ademas de imprimirlas.

----

entonces si por ejemplo quiero ingresarle no se... 5 nombres cualquieras:

char *a[5]
 
*(a+0)="Maria";
*(a+1)="Jose";
etc...

pero no quiero estar contando el numero de letras cada vez que tenga que asignar memoria para cada nombre, como se le asignaria la memoria??
A priori no hay forma. Lo mas sencillo es utilizar un array de caracteres para leer ahí cada una de las cadenas tomando nota de su longitud. Lees una cadena, reservas el espacio justo para ella y la copias ahí. Un programa de ejemplo lo puedes revisar en el tema Duda Punteros Dobles/Array de punteros (http://foro.elhacker.net/programacion_cc/duda_punteros_doblesarray_de_punteros-t378927.0.html).

Un saludo


Título: Re: scanf y gets
Publicado por: m@o_614 en 21 Julio 2013, 02:51 am
Citar
A priori no hay forma. Lo mas sencillo es utilizar un array de caracteres para leer ahí cada una de las cadenas tomando nota de su longitud. Lees una cadena, reservas el espacio justo para ella y la copias ahí.

rir3760 con esto ultimo que me acabas de decir ya me quedo claro como lo tengo que hacer, aqui dejo un pedazo del codigo que estoy haciendo, creo que era a lo que te referias con utilizar un array de caracteres y demas

Código
  1. #define MAX 5
  2.  
  3. int main()
  4. {
  5.    char *nombre[MAX];
  6.  
  7.    entrada(nombre);
  8.    return 0;
  9. }
  10.  
  11. void entrada(char *nombre[])
  12. {
  13.    char B[50];
  14.    int i;
  15.    for(i=0;i < MAX;i++)
  16.    {
  17.        printf("Dame la escuela numero %d: ",i);
  18.        gets(B);
  19.        (nombre+i) = (char*)malloc(strlen(B)sizeof(char));
  20.        strcpy(nombre+i,B);
  21.        system("cls");
  22.    }
  23. }
 

de la funcion de malloc no se si tiene algun error porque no se bien como es su estructura


Título: Re: scanf y gets
Publicado por: rir3760 en 21 Julio 2013, 19:29 pm
Si vas a publicar código fuente por favor publicalo completo ya que faltan las directivas del preprocesador. En la función "entrada" faltan los operadores de indireccion y multiplicación en la llamada a malloc, no es necesario con esta la conversión explicita como tampoco multiplicar por "sizeof(char)" ya que este ultimo siempre es igual a uno. Y algunos mas.

Con los cambios compila sin problemas:
Código
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5. #define MAX 5
  6.  
  7. void entrada(char *nombre[]);
  8.  
  9. int main(void)
  10. {
  11.   char *nombre[MAX];
  12.  
  13.   entrada(nombre);
  14.  
  15.   return EXIT_SUCCESS;
  16. }
  17.  
  18. void entrada(char *nombre[])
  19. {
  20.   char B[50];
  21.   int i;
  22.  
  23.   for (i = 0; i < MAX; i++){
  24.      printf("Dame la escuela numero %d: ",i);
  25.      fflush(stdout);
  26.      fgets(B, 50, stdin);
  27.      *(nombre + i) = malloc(strlen(B) + 1);
  28.      strcpy(nombre[i], B);
  29.   }
  30. }

Por ultimo no deberías utilizar la función gets, el porque y mas recomendaciones en el tema |Lo que no hay que hacer en C/C++. Nivel basico| (http://foro.elhacker.net/programacion_cc/lo_que_no_hay_que_hacer_en_cc_nivel_basico-t277729.0.html)

Un saludo