elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Introducción a la Factorización De Semiprimos (RSA)


  Mostrar Mensajes
Páginas: 1 ... 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 [56] 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 ... 102
551  Programación / Programación C/C++ / Re: Importante-Curioso en: 27 Agosto 2019, 17:55 pm
Pues tu problema es porque estás introduciendo valores (opción 4) sin crear la pila vacía primero (opción 1).
Pero vamos que aparte de ese problema, ese código es muy mejorable (pero MUY MUCHO)... Tienes:
  • Variables globales
  • Variables sin inicializar
  • Variables repetidas
  • Parametros innecesarios
  • Funciones a las que les falta el <return>
  • Funciones mal utilizadas
Como no sé si te interesa saber más sobre esto o no y no me apetece explicarme para nada, si quieres saber más detalles sobre el porqué del problema o algunos consejos para mejorar ese código, házmelo saber. AVISO: no te lo voy a dar todo ya hecho. Y si no lo necesitas, tema zanjado. :-X :-X
552  Programación / Programación C/C++ / Re: Problema con los punteros C++ en: 27 Agosto 2019, 17:25 pm
Bueno, sigues teniendo un pequeño error. Fíjate que para mostrar la matriz haces:
Código
  1. for(int i = 0; i < filas; ++i){
  2.    for(int j = 0; j < columnas; ++j)
  3.        cout << *(*matriz + i) + j) << " ";
  4.    cout << endl;
  5. }
En cambio en la función <pedirDatos()> para almacenar los valores en la matriz haces:
Código
  1. for(int i = 0; i < columnas; ++i){
  2.    for(int j = 0; j < filas; ++j)
  3.        cout << *(*matriz + i) + j) << " ";
  4.    cout << endl;
  5. }
Por lo que este segundo bloque está mal. En matrices cuadradas no vas a notar la diferencia pero en matrices que no son cuadradas vas a tener errores de acceso a memoria.

Citar
PD: No se suponía que un puntero es un referencia al espacio de memoria? y que usando "cin>>puntero" te permite guardar el valor en ese espacio, por qué ahora al usar new hace falta pasarlo como puntero referencia y no lo englobaron en 1 sola categoría???, estas cosas son las que me matan  ;D ;D ;D.
Cuando pasas un puntero estás pasando su contenido que es la dirección de memoria del dato al que está apuntando.
Código
  1. int numero = 2; // pongamos que se guarda en 0x1 (voy a usar numeros cortos para que sea mas sencillo)
  2. cout << "Numero vale: " << numero << " y su direccion de memoria original es: " << &numero << endl;
  3. // Salida: Numero vale: 2 y su direccion de memoria original es: 0x1
  4.  
  5. // Pasamos la variable por referencia para pasar la direccion de memoria en la que esta guardado <numero> (0x1)
  6. void incrementar(int &numero){ // pasamos 0x1
  7.    ++numero;
  8.    cout << "Numero vale: " << numero << " y su direccion de memoria ORIGINAL es: " << &numero << endl;
  9. }
  10. // Salida: Numero vale: 3 y su direccion de memoria ORIGINAL es: 0x1
  11.  
  12. int *p_numero = &numero; // pongamos que <p_numero> se guarda en 0x5 y dentro de 0x5 guarda la direccion de <numero> 0x1
  13. // Pasamos un puntero por valor, entonces estamos haciendo una copia de su contenido pero su contenido ya es la direccion de memoria de <numero> 0x1
  14. void incrementar(int *p_numero){ // a la funcion llega 0x1 y se guarda en una copia del puntero en 0x13 por ejemplo
  15.    ++(*p_numero);
  16.    cout << "Numero vale: " << *p_numero << " y su direccion de memoria ORIGINAL es " << p_numero << endl;
  17.    cout << "La direccion de memoria del puntero es: " << &p_numero << endl;
  18. }
  19. // Salida: Numero vale: 4 y su direccion de memoria ORIGINAL es: 0x1
  20. //         La direccion de memoria del puntero es: 0x13 // Aqui se ve la direccion de la copia, no 0x5
  21.  
  22. // Pasamos el puntero por referencia, entonces estamos pasando la direccion de memoria EN LA QUE ESTA EL PUNTERO (0x5), no a la que apunta (<numero>)
  23. void incrementar(int *&p_numero){ // a la funcion llega 0x5 que sigue apuntando a 0x1
  24.    ++(*p_numero);
  25.    cout << "Numero vale: " << *p_numero << " y su direccion de memoria ORIGINAL es " << p_numero << endl;
  26.    cout << "La direccion de memoria del puntero ORIGINAL es: " << &p_numero << endl;
  27. }
  28. // Salida: Numero vale: 5 y su direccion de memoria ORIGINAL es: 0x1
  29. //          La direccion de memoria del puntero ORIGINAL es: 0x5
  30. }

Una vez visto esto, el tema es que los operadores <new> y <delete> no actúan en la dirección a la que apunta el puntero, sino en la dirección en la que está el puntero. Por eso si quisieramos hacer <new> o <delete> en el ejemplo anterior tendríamos que hacerlo sobre la dirección de memoria 0x5. Si el puntero lo pasamos por valor, estamos haciendo un <new> sobre 0x13, pero la dirección 0x5 no se ha enterado de lo que le han hecho a 0x13.
Por eso en ese caso hay que pasar el puntero por referencia.

Citar
PD2: Donde aprendiste a programar por cierto?
Me metieron en este mundo en el instituto, me gustó y me quedé. Si quieres un consejo: no te quedes con que tu programa funciona después de cambiar tal cosa, investiga por qué funciona después de ese cambio. No hacen falta programas enormes para aprender, hasta el programa más tonto como el código que he puesto arriba sirve para ver cómo funcionan las direcciones de memoria. Después de 2 años de Ingeniería Informática te puedo asegurar que es mucho mejor aprender por libre. Existen recursos como internet de sobra para poder hacerlo y cada uno tiene su ritmo de aprendizaje, si te metes a aprender en un sistema estandarizado no vas a profundizar en nada y no te va a dar tiempo a practicar todo lo que veas y la programación se aprende programando, no leyendo libros y códigos ya hechos una y otra vez.
553  Programación / Programación C/C++ / Re: Problema con los punteros C++ en: 27 Agosto 2019, 00:25 am
Has hecho tantos cambios que estás mezclando <p_m> con <p_m2> en las funciones... :xD
Para evitar estas cosas es mejor usar nombres que se vean mejor y que se distingan mas facilmente. Y si te acostumbras a usar buenos nombres siempre al final los usarás hasta cuando estés haciendo programas de prueba.

Bueno el tema de matrices... Ibas bastante bien. Pero hay un detalle que te puse unos comentarios atrás... Voy a buscarlo:
Citar
Como te he comentado antes el paso de un puntero por referencia es muy aislado. Hasta donde yo he visto y además si estás empezando sólo lo necesitarás para pasar un puntero a una función y dentro de la función reservar memoria con <new> o bien pasar un array (es decir, un puntero al que ya le has hecho <new>) y liberar esa memoria con <delete>. Quitando esos casos, no creo que necesites pasar un puntero por referencia. Por lo que puedes olvidarte un poco de él hasta que veas memoria dinámica.

Entonces tienes dos opciones:
  • Opcion 1: Reservar memoria en el <main> y pasar el doble puntero por valor (como lo estás pasando ahora).
  • Opcion 2: Pasar el doble puntero por referencia y listo. Ya que has llegado hasta donde has llegado, te recomiendo esta segunda:
Código
  1. void pedirDatos(int**&, int&, int&);
Con ese cambio ya estaría listo. A la hora de llamar a la función solo tienes que pasarle el nombre como estás haciendo (es decir, que no debes cambiar la llamada a la función del <main>).

Y en el bucle para pedir la matriz de <pedirDatos()> tienes un pequeño error. Si trabajas siempre con: matriz[filas][columnas], entonces para mostrar la matriz el bucle externo maneja las filas y el interno las columnas (lo tienes al revés).
Si ves que se te complica la aritmética de punteros, puedes usar indexación que es más cómodo de ver y más fácil de trabajar y cuando ya tengas más experiencia usar la aritmética de punteros:
Código
  1. cout << *(*(matriz + i) + j) << endl; // aritmetica de punteros
  2. cout << matriz[i][j] << endl; // indexacion
554  Programación / Programación C/C++ / Re: Problema con los punteros C++ en: 26 Agosto 2019, 18:50 pm
Citar
Pero si lo uso con int me muestra la posición, por?
Código
  1. int nombre[] = {1,2,3}, *p_nombre2;
  2. cout<<"Dir.1: "<<nombre[1]<<" | "<<p_nombre2<<endl;
  3. //Salida: Dir.1: 2 | 0x6ffdf0
En este caso creo que no hay dudas. <p_nombre2> sería un puntero que apunta al mismo sitio que <nombre> aunque no se vea ahí (es decir, imagino que has omitido el <p_nombre2 = nombre> o <p_nombre2 = &nombre[0]> que es equivalente). Entonces al mostrar <nombre[1]> estamos mostrando el valor almacenado en la posición 1 del array y al mostrar <p_nombre2> estamos mostrando la dirección de memoria a la que está apuntando.

Citar
Si uso con char me muestra toda la cadena:
Código
  1. char nombre[] = {'a','b','c'}, *p_nombre2;
  2. cout<<"Dir.1: "<<nombre[1]<<" | "<<p_nombre2<<endl;
  3. //Salida: Dir.1: b | abc
Aquí es donde la cosa cambia, los arrays de <char> que se conocen también como cadenas estilo C porque existían en C y fueron heredadas por C++. Cuando tienes un array de <int> por ejemplo, para mostrarlo hacemos lo siguiente:
Código
  1. for(int i = 0; i < size; ++i)
  2.    cout << arrayEnteros[i] << " ";
Pero imagina que tienes una cadena de <char> con un nombre y que quieres mostrar el nombre. Sería muy incómodo tener que hacer:
Código
  1. char nombre[] = "Pepe";
  2. cout << "Tu nombre es: ";
  3. for(int i = 0; i < 4; ++i)
  4.    cout << nombre[i];
Entonces como las cadenas de <char> no se suelen usar sólo para almacenar valores aislados sino también palabras o frases, para hacerlo más cómodo el lenguaje permite hacer:
Código
  1. char nombre[] = "Pepe";
  2. cout << "Tu nombre es: " << nombre << endl;
Y la máquina ya entiende que tiene que mostrar desde donde apunta <nombre> (&nombre[0]) hasta el caracter de fin de cadena '\0'. Cosa que con un array de enteros no podemos hacer. Entonces digamos que puedes entenderlo como que los arrays de <char> tienen sus excepciones respecto al resto.

Además en C++, al introducir la POO y con ello las clases y objetos, aparece la clase <string> que funciona como una cadena <char> pero sin necesidad de tener que reservar nosotros la memoria:
Código
  1. string nombre = "Pepe";
  2. cout << "La primera letra de " << nombre << " es: " << nombre[0] << endl;
555  Programación / Programación C/C++ / Re: Problema con los punteros C++ en: 26 Agosto 2019, 00:41 am
Citar
Lo que no se es si luego al usar visual studio la forma de programar va a ser distinta a como funciona Dev C++
Visual Studio al igual que Dev C++ solo es un IDE como cualquier otro: Eclipse, NetBeans, CodeBlocks, etc. Pero al final C++ es C++, es decir, el lenguaje es el lenguaje y eso no cambia. Yo por ejemplo no uso ningún IDE, todos los proyectos los hago en un editor de texto (puedes usar desde un simple Bloc de Notas hasta un editor más especializado en programación como SublimeText o Atom que permiten tener muchas extensiones para facilitar el trabajo como si de un IDE se tratara) y los compilo desde la Terminal (en Linux) lo que equivaldría a hacerlo desde la Consola de Windows. Quiero decir que da igual dónde escribas tu código.

Citar
PD: Hice la carrera de electrónica y en el primer año di informática (muuuy básico) y llegamos hasta funciones, usábamos la librería <stdlib> para todo y usábamos printf o fprintf si era para una función y scanf, he visto que en algunos post hay gente que no usa <iostream> en C++ si se supone que es la estándar, por?
Normalmente cuando se trabaja a bajo nivel se usa mucho el lenguaje C que trabaja a más bajo nivel que C++. C se caracteriza por la librería <stdio.h> que contiene las funciones <printf()>, <scanf()>, <fprintf()>, etc mientras que C++ se caracteriza por <iostream> que engloba <cin>, <cout> entre otros.
C++ es como una expansión de C, por lo que todo lo que se podía hacer en C, se puede hacer en C++, pero en muchas ocasiones en C++ se crearon formas más sencillas de hacer las cosas al no ser de tan bajo nivel. Esto genera una gran confusión entre si estás aprendiendo C o C++. Un programa en C (fichero.c) lo puedes compilar como código fuente C (.c) o como código fuente C++ (.cpp), sin embargo, al revés, no. De ahí que muchas personas digan que programan en C++ y usen <stdio.h>, <stdlib.h>, etc. C++ también tiene sus propias versiones para estas librerías que tienen el mismo nombre quitando el <.h> del final y añadiendo una <c> al principio: <cstdio>, <cstdlib>, etc.

Citar
PD2: He visto que en C++ también se usa la programación orientada a objetos que se usa en unreal y que se parece a java. el C++ no es un lenguaje procedural? como puede ser orientado a objetos también?
El lenguaje C siempre ha sido uno de los más claros ejemplos de lenguaje procedural ya que permite dividir un problema en subproblemas que se resolverán mediante procedimientos (funciones). El lenguaje C++ se desarrolló a partir de C para hacer como una expansión de C que trabajase con el paradigma de programación orientada a objetos pero por ello no deja de ser un lenguaje procedural. Un lenguaje puede tener más de un paradigma de programación como es el caso.

PD: Te recomiendo una página que es cplusplus. Te dejo el enlace AQUÍ Ahí tienes un apartado a la izquierda de la página llamado <Reference> que contiene muchas librerías con su nombre en C y en C++. Además de otros archivos de cabecera como son los contenedores de la STL (muy útiles y cómodos de usar) y otros; y dentro de cada librería se encuentran sus funciones y constantes con explicaciones de uso y ejemplos. Está en inglés pero es muy fácil de entender y seguro que te saca de muchos apuros. Suerte. :-X
556  Programación / Programación C/C++ / Re: Problema con los punteros C++ en: 25 Agosto 2019, 23:07 pm
Citar
No sabia que que cuando pasabas un vector a una función lo podías pasar como puntero con "*", hasta lo que he visto cuando se pasaba algo por referencia a una función se hacia con &
Cuando se pasa algo por referencia se pasa con &, eso es cierto. Pero un puntero no siempre se pasa por referencia.
Código
  1. void funcion1(int a); // variable por valor
  2. void funcion2(int &a); // variable por referencia
  3. void funcion3(int *pa); // puntero por valor
  4. void funcion4(int *&pa); // puntero por referencia
Diría que esos son los tipos de parámetros que podemos usar más típicos. Luego claro está que podemos hacer conversiones de uno a otro. Por ejemplo es lo mismo pasar una variable por referencia que pasar un puntero a esa variable por valor. Todo depende de lo que nos guste más o lo que más nos convenga en cada momento: si estás trabajando todo el tiempo con variables y necesitas mandar una a una función y modificarla pero no puedes retornarla porque ya tienes un <return> (o cualquier otro motivo) pues la pasas por referencia (así ahorras crear un puntero y hacer que apunte a esa variable). En cambio si por un casual estás trabajando con punteros y quieres pasar el valor del puntero (es decir la variable a la que apunta) y modificarla pues para qué vas a crear otra variable y asignarle el valor del puntero para pasarla por referencia? En este caso te ahorras trabajo pasando el puntero directamente.

Como te he comentado antes el paso de un puntero por referencia es muy aislado. Hasta donde yo he visto y además si estás empezando sólo lo necesitarás para pasar un puntero a una función y dentro de la función reservar memoria con <new> o bien pasar un array (es decir, un puntero al que ya le has hecho <new>) y liberar esa memoria con <delete>. Quitando esos casos, no creo que necesites pasar un puntero por referencia. Por lo que puedes olvidarte un poco de él hasta que veas memoria dinámica.

Citar
y si querías pasar un puntero tenias que crearlo del mismo tipo que el vector, luego vincularlo al primer espacio de la memoria y luego ya se pasaba con "*". 
Esto no es necesario pero en algunas ocasiones se hace. Cuando tú creas un vector, el propio vector funciona como un puntero que apunta a la primera posición del vector.
Código
  1. int numeros[5] = {1,2,3,4,5};
  2. // aqui podemos usar <numeros> como si fuera un puntero que apunta a &numeros[0]
  3. // por lo tanto podemos hacer cosas como:
  4. cout << *numeros << endl; // imprime: 1
  5. for(size_t i = 0; i < 5; ++i)
  6.    cout << *(numeros+i) << " " << endl; // imprime: 1 2 3 4 5

¿Y para qué sirve crear otro puntero? Bueno, imagina que tienes una función para recorrer todo un vector. Esta la podemos hacer de varias formas, por ejemplo:
Código
  1. void recorrerVector1(int *numeros, int longitud){
  2.    for(int i = 0; i < longitud; ++i)
  3.        cout << *(numeros+i) << endl;
  4. }
  5.  
  6. void recorrerVector2(int *numeros, int longitud){
  7.    for(int i = 0; i < longitud; ++i){
  8.        cout << *numeros << endl;
  9.        ++numeros;
  10.    }
  11. }
Con ambas vas a obtener el mismo resultado pero tienen una diferencia. La primera le va diciendo a <numeros> "imprime el valor que está i posiciones por delante de ti" mientras que la segunda le dice a <numeros> "imprime el valor que hay en tu posición 0 y muévete". La primera no tiene ningún inconveniente, sin embargo, la segunda sí. Si a la segunda función le pasamos el nombre del vector para que haga de puntero y avanzamos el puntero, los valores que vaya dejando atrás los perderemos para siempre. Un vector se puede encontrar en memoria siempre que tengamos un puntero apuntando a él (al comienzo de él), si perdemos el puntero, perdemos el vector. Para ello se crea otro puntero nuevo que sea el que se vaya moviendo mientras el original siempre apunta al comienzo.
Código
  1. int numeros[5] = {1,2,3,4,5};
  2. int *puntero = numeros;
  3. recorrerVector2(puntero, 5); // aqui no hay problemas porque aunque <puntero> se mueva, <numeros> siempre apunta al comienzo del vector
  4. //en cambio si hacemos esto...
  5. recorrerVector2(numeros, 5); // ... adios al vector. Ya no hay forma de volver a usarlo ni de liberarlo si fuera memoria dinamica

Citar
PD: Entonces lo que paso con: (char* nombre)
es la posición de memoria de la posición v[0] del vector?
o
es un puntero del vector que se llama nombre y esta en la posición v[0] del vector con su mismo nombre?.
Lo que pasas es lo primero que dices, la posición de memoria de la posición 0 del vector.
Citar
En el caso de (char& letra) que es un paso por referencia pasas la dirección de memoria también no? ya que puedes modificar la variable en otra funciones.
Exacto. Cuando pasas una variable por referencia siempre pasas la dirección de memoria en la que está almacenada esa variable.

Citar
Y  por qué no se usa (char& letra) como por ejemplo sí pasar un solo char como 'a'.
Respecto a esto voy a extenderme un poco más para intentar explicarlo del todo...
Este ha sido tu planteamiento de antes:
Código
  1. int pedirNombre(char& us){
  2. int longitud;
  3.  
  4. cout<<"Digite su nombre: ";
  5. cin.getline(us,30,'\n');
  6. longitud = strlen(us);
  7.  
  8. return longitud;
  9. }
¿Se puede hacer? ¿No se puede hacer? La respuesta es que sí, se puede hacer pero como todo, hay que hacerlo bien. Pasamos primero a un ejemplo más simple:
Código
  1. void funcion1(int a){
  2.    cout << "La variable vale: " << a << " y su direccion de memoria es: " << &a << endl;
  3.    // Se muestra la direccion de memoria de la copia
  4.    // IMPORTANTE: el valor se obtiene con el nombre tal cual y la direccion con &
  5. }
  6.  
  7. void funcion2(int &a){
  8.    cout << "La variable vale: " << a << " y su direccion de memoria es: " << &a << endl;
  9.    // Se muestra la direccion de memoria de la original
  10.    // IMPORTANTE: el valor se obtiene con el nombre tal cual y la direccion con &
  11. }
  12.  
  13. void funcion3(int *pa){
  14.    cout << "La variable vale: " << *pa << " y su direccion de memoria es: " << pa << endl;
  15.    // Se muestra la direccion de memoria de la original pero no de <pa> sino a la que apunta <pa>
  16.    // IMPORTANTE: el valor se obtiene con * y la direccion con el nombre tal cual
  17. }
  18.  
  19. // USO DE LAS FUNCIONES
  20. int numero = 2;
  21. funcion1(numero); // se pasa un <int>
  22. funcion2(numero); // se pasa un <int> aunque el programa ya sabe que es por referencia
  23. funcion3(numero); // ERROR: El programa espera una direccion de memoria (puntero) y le estamos pasando un <int>
  24. funcion3(&numero); // Correcto: Le pasamos una direccion de memoria sin crear un puntero auxiliar (que tambien se podria hacer)
  25.  
Lo que quiero que veas con esto es que tanto en el paso por valor como por referencia lo que pasamos es el nombre de la variable y eso que en el primer caso estamos copiando el valor y en el segundo caso estamos usando su dirección de memoria pero nosotros siempre pasamos siempre el nombre de la variable tal cual y dentro de las funciones usamos también el nombre de la variable tal cual para mostrarla. En cambio, en la tercera función no pasamos el nombre de la variable tal cual sino que pasamos su dirección de memoria y dentro de la función no usamos el nombre tal cual para mostrarla <pa> sino que usamos el operador de desreferencia (el asterisco).

Entonces volviendo a tu función. La forma más simple es esta:
Código
  1. int pedirNombre(char *nombre){
  2.    cout << "Introduce nombre: ";
  3.    cin.getline(nombre, 30, '\n');
  4.    return strlen(nombre);
  5. }
  6.  
  7. // USO DE LA FUNCION
  8. int longitud = pedirNombre(nombre); // Como hemos visto antes tiene que recibir directamente la direccion de memoria, bien asi o bien asi:
  9. int longitud = pedirNombre(&nombre[0]); // que es exactamente lo mismo

¿Se puede hacer usando el paso por referencia? Claro que se puede pero necesitamos hacer algunos cambios:
Código
  1. int pedirNombre(char &nombre){
  2.    cout << "Introduce tu nombre: ";
  3.    cin.getline(&nombre, 30, '\n');
  4.    return strlen(&nombre);
  5. }
  6. // o bien:
  7. int pedirNombre(char &nombre){
  8.    char *puntero = &nombre;
  9.    cout << "Introduce nombre: ";
  10.    cin.getline(puntero, 30, '\n');
  11.    return strlen(puntero);
  12. }
  13.  
  14. // USO DE LA FUNCION
  15. int longitud = pedirNombre(nombre[0]); // como hemos visto antes se pasa la variable y ya el programa sabe que tiene que usar su direccion de memoria

Lo importante es saber cuando se usa la dirección de memoria y cuando no pero también hay que saber cuando es necesario que lo indiquemos nosotros y cuando no. ¿Cuál usar? Pues cada uno la que más le guste pero lo ideal sería usar la más legible en cada momento y para vectores siempre va a ser más legible el paso de punteros que el de referencias.
557  Programación / Programación C/C++ / Re: Problema con los punteros C++ en: 25 Agosto 2019, 21:00 pm
Respecto a tu primer mensaje (para no citarlo entero):
Bueno ya empieza a coger forma aunque hay que terminar de pulir ese código...
El primer error lo tienes en la función <pedirNombre()>. Aunque antes has visto que era equivalente pasar una variable por referencia que pasar un puntero ya que ambas son direcciones de memoria, no es exactamente lo mismo. Por lo que tienes que cambiar el parámetro de un <char> por referencia a un puntero de <char>. Es decir la función debe quedar así:
Código
  1. int pedirNombre(char*);
No olvides cambiarlo tanto en el prototipo de la función como abajo en la implementación.

Otro error lo tienes en la línea 22: Tenemos una función que debe recibir un puntero a <char> (porque cuando pasamos un array como parámetro lo que pasamos en realidad es un puntero de la primera posición del array) y tú le estás pasando <usuario[30]> que es la posición 30 del array <usuario>, es decir, le estás pasando un <char> (que además está fuera de los límites ya que si el tamaño es 30, las posiciones del array irían del 0 al 29 incluidos). En este caso solo se escribe el nombre del array tal que:
Código
  1. longitud = pedirNombre(usuario);

Hasta aquí los errores como tal. Una vez corregido eso, el programa ya funcionaría.

Y aquí un par de consejos o trucos:
  • Como antes te he comentado, hay que evitar las variables globales. Por tanto no deberíamos tener las líneas 12 y 13. El nombre de un array corresponde a un puntero que apunta a la primera posición del array. En tu caso:
Código
  1. // creamos un array de <int>
  2. int vocales[5] = {0}; // Con esto se inicializa la primera posicion al valor que indiques y el resto a 0 por lo tanto todo a 0
  3. int *puntero = &vocales[0]; // hacemos que <puntero> apunte al comienzo de <vocales>
  4. // Ahora tenemos dos punteros que apuntan a <vocales[0]>. Uno es <puntero> y el otro es <vocales> (<vocales> equivale a <&vocales[0]> por lo que podemos hacer:)
  5. int *puntero = vocales; // esto equivale a lo anterior
  6. // O directamente podemos usar <vocales> y nos ahorramos un puntero global que no nos traera nada bueno

  • La aritmética de puntero no es lo más cómodo digamos. Por lo tanto a no ser que te lo exijan o te guste, puedes usar la indexación:
Código
  1. vocales[i]
  2. *(vocales+i)
Estás dos sentencias son exactamente lo mismo. Siempre puedes pasar de una nomenglatura a la otra. Y sí se pueden incrementar punteros pero debes usar paréntesis:
Código
  1. int vocales[5] = {1,2,3,4,5};
  2. int *puntero = vocales;
  3. ++puntero; // ahora <puntero> apunta a la siguiente posicion
  4. ++(*puntero); // ahora incrementamos el valor al que apunta <puntero> que es <vocales[1]> entonces <vocales> = {1,3,3,4,5}
  5. ++(*(puntero+1)); // incrementamos el valor de la siguiente posicion a la que apunta <puntero>. Ahora <vocales> = {1,3,4,4,5}
Esto es igual para el -- y tanto si lo ponemos en prefijo como en sufijo.

Por lo tanto creo que lo mejor es:
  • Crear el array <char nombre[30]> en el <main>
  • Crear el array <int vocales[5]> en el <main>
  • Usar una función <int pedirNombre(char *nombre)> para pedir el nombre al usuario y obtener su longitud.
  • Usar una función <void contarVocales(char *nombre, int longitud, int *vocales)> para contar las vocales.
Así consigues resolver el programa sin variables globales, sin memoria dinámica y usando únicamente 2 variables (<nombre> y <vocales>) :-X


Respecto a tu segundo mensaje:
Te sugiero que modifiques lo que te comento más arriba en tu primer código para no meterte con memoria dinámica (<new>). Es mejor que primero aprendas bien a pasar los parámetros antes de nada.
558  Programación / Programación C/C++ / Re: Problema con los punteros C++ en: 25 Agosto 2019, 19:00 pm
Lo primero, para tu próxima consulta (o si quieres puedes modificar tu mensaje para añadirlo), coloca tu código entre etiquetas de código GeSHi. Esto hace el código más fácil de ver y evita algunos problemas de etiquetas.

Ahora vamos parte a parte... AVISO: Va a ser un mensaje extenso y con bastante contenido.
Empezando por las librerías: no uses la librería <conio.h>. Esta librería no es estándar por la que no siempre se puede compilar un programa que la incluye. Además sólo la usas para <getch()> (esto lo puedes sustituir por <cin.get()> que pertenece a la librería <iostream> y así ya no la necesitas y el programa sigue igual). Y aunque sea un tema menor, la convención del <.h> en las librerías es típico de C (para C++ empieza por <c> y no lleva <.h>) Ej: <stdlib.h> (C) -> <cstdlib> (C++)

Citar
Tengo un cacao con eso, debo o no debo poner las variables globales dentro del parentesis?¿ por lo que se si lo hago sin ser un puntero y no utiliza & pasaría una copia del valor (para asi no modificar el valor de la variable y usarlo en otra funciones si hace falta) pero en los punteros no veo la diferencia la verdad.
Lo siguiente que vamos a ver es el scope o alcance de una variable y su tiempo de vida. Una variable existe dentro de la función donde se declara y se "destruye" cuando acaba esa función (esto es así también para el <main> que no deja de ser una función) y para poder usarla en otra función debemos mandársela de alguna manera (uso de parámetros). Una variable global existe durante toda la ejecución del programa y es accesible en cualquier punto del programa, por lo que una variable global no es necesario mandarla como parámetro. Por esto último, una variable global se puede modificar desde cualquier lugar y hay que evitar su uso. Se pueden usar variables globales o más bien constantes para determinar valores predefinidos.

Citar
PD: he declarado los punteros de forma global por que no se otra forma de pasarlos a otra función y no se si puede pasar mas de un valor por el return (todos los ejemplos que he visto solo pasan 1 dato por el return).
Un <return> sólo devuelve un valor. Este puede ser un valor de tipo <int>/<char>/... o también puede ser un puntero. Por ejemplo para arrays/arreglos/vectores (las 3 son lo mismo). Existe la excepción de usar el tipo <pair> de la STL que es un par de datos. Por lo que podríamos devolver 2 o más si concatenamos un <pair> dentro de otro. Esto no es lo más habitual y no será necesario tirar de ello pero para que lo conozcas... Como regla general: el <return> devuelve un valor.

En C++ existe lo que es el paso por referencia (lo opuesto al paso por valor). Un parámetro pasado por valor, crea una copia dentro de la función por lo que su valor original no se ve alterado una vez estamos fuera de esa función. En cambio, un parámetro pasado por referencia manda la dirección de memoria original por lo que si su valor se altera dentro de la función, este también se verá alterado fuera. Los punteros guardan direcciones de memoria por lo que pasar un puntero como parámetro es como pasar la dirección de memoria del dato al que apunta (que es equivalente a pasar el dato por referencia).
Código
  1. int incrementar(int a){
  2.    return ++a;
  3. }
  4. int a = 2;
  5. int b = incrementar(a);
En este caso hemos pasado el parámetro por valor por lo que el original no cambia. (No voy a entrar en el tema de poner ++ antes o después del nombre ya que el resultado varía). La función hace una copia de <a>, la incrementa, se la asigna a <b> y al acabar la función la copia de <a> se "destruye" (porque su alcance sólo existe dentro de la función). Así que a == 2 y b == 3.

Código
  1. int incrementar(int &a){
  2.    return ++a;
  3. }
  4. int a = 2;
  5. int b = incrementar(a);
En este caso lo estamos pasando por referencia por lo que sí cambia. La función recibe la dirección de memoria de <a> por lo tanto no es ninguna copia. Incrementa <a>, se lo asigna a <b> y se acabó. No hay copia por lo que no se "destruye". Entonces a == 3 y b == 3.

Código
  1. int incrementar(int *p){
  2.    return ++(*p);
  3. }
  4. int a = 2:
  5. int *pa = &a;
  6. int b = incrementar(pa);
Aquí <pa> tiene como valor la dirección de memoria de <a>. La función recibe un puntero POR VALOR por lo que se hace una copia de su contenido (entonces se copia la dirección de <a> que es el contenido de <pa>). Si te das cuenta al final la función está trabajando con la dirección de memoria de <a> al igual que en el caso anterior por lo que esto es equivalente a hacer un paso por referencia de <a>. El valor entonces es a == 3 y b == 3.

También se puede pasar un puntero por referencia. Esto únicamente será necesario para trabajar sobre la dirección de memoria en la que está el puntero, NO A LA QUE APUNTA (nótese la diferencia, una variable está en una dirección de memoria y guarda un valor pero un puntero está en una dirección de memoria y guarda otra dirección de memoria). Y sólo se trabaja sobre la dirección de memoria del puntero para reservar memoria y liberarla (<new> y <delete>).

Y finalmente vamos a tu problema en concreto. Aunque en tu caso parece que sólo <*p_a> tiene un valor incorrecto, en realidad el fallo es bastante más grande. Entre el vector <usuario> que es local y se "destruye" al terminar la función y la mezcla de punteros globales con direcciones de memoria temporales como lo son las variables para contar las vocales, internamente hay un montón de errores de saltos que dependen de valores no inicializados, posiciones de escritura no válidas...

Creo que la mejor opción que tienes para no modificar mucho tu programa es:
1. Crear el array <nombre> en el <main>. Pasárselo a una función como parámetro y pedir un nombre al usuario. Devolver el tamaño de <nombre>.
2. Usar una función que reciba como parámetros el array <nombre>, la longitud, y una de estas dos opciones:
  • 5 variables por referencia declaradas en el <main> usadas como contadores.
  • Un array de <int> de tamaño 5 que equivale a juntar las 5 variables de la otra opción en un array.

Prueba los códigos que te he comentado antes y las cosas que no te hayan quedado muy claras y si tienes algún problema de nuevo, no dudes en preguntar pero por favor con el código entre etiquetas de código GeSHi... :-X :-X
559  Programación / Programación C/C++ / Re: problema de arreglo bidimencional en: 12 Agosto 2019, 17:55 pm
Lo primero de todo: El código entre etiquetas de código GeSHi   :-X :-X

Algunos consejos y cosillas que te pueden servir de ayuda;
  • No uses <stdio.h>. Esta librería es típica de C, no de C++ (en C++ es <cstdio>) y además no la usas para nada. Usas las entradas/salidas de <iostream> por lo que no la necesitas. Hay que saber lo que se pone y por qué se pone.
  • Sobre el <cout> y el <endl>: Se pueden concatenar estos últimos y no es necesario poner unas comillas vacías para usarlo por separado. También puedes usarlo antes de una cadena y después. Además puedes usar el caracter especial "\n" para saltos de línea (hay una sútil diferencia entre "\n" y <endl> pero bueno...)
Código
  1. cout << "Texto" << endl;
  2. cout << "Texto" << endl << endl << endl;
  3. cout << endl;
  4. cout << endl << "Texto" << endl;
  5. cout << "\nTexto" << endl;

Ahora los errores de tu programa:
  • No estás usando un array/arreglo bidimensional. Esto es una matriz y se declara con dos parejas de corchetes: <tipo> <nombre_matriz>[<filas>][<columnas>].
  • Estás acumulando valores (notas) en <suma> pero no has inicializado <suma> por lo que puede tener cualquier valor.
  • Debes guardar todas las notas y al no usar una matriz, una vez que has utilizado las notas de un alumno, las pierdes porque guardas las notas del siguiente.

Todos los ejercicios de este tipo tienen una forma estándar de hacerlos que es la siguiente:
Código
  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. // Usamos constantes y asi podemos agrandar o reducir los valores del programa sin tener que modificarlo entero
  6. const int MAX_ALUMNOS = 100; // numero maximo de alumnos que podemos tratar
  7. const int NUM_NOTAS = 3; // numero de notas por alumno
  8.  
  9. int main(){
  10.    // Creamos una matriz. Cada fila son las notas de un alumno y cada columna es una nota
  11.    double notas[MAX_ALUMNOS][NUM_NOTAS];
  12.    int num_alumnos; // numero de alumnos que vamos a tratar. Debe ser menor o igual que <MAX_ALUMNOS>
  13.  
  14.    // El promedio de cada alumno podemos guardarlo (necesitamos un array/arreglo mas entonces) o calcularlo y sobrescribirlo para cada alumno
  15.    double promedio[MAX_ALUMNOS] = {0}; // arreglo para guardarlos con cada celda inicializada en 0
  16.    double promedio = 0; // si no queremos guardarlos, basta con esto. Inicializamos a 0 para poder acumular luego ahi las notas
  17.  
  18.    // Podemos hacer un filtro para obtener el numero de alumnos a tratar
  19.    // El filtro te pide un valor y te lo sigue pidiendo mientras se cumpla la condicion del <while>
  20.    do{
  21.        cout << "Introduce el numero de alumnos: ";
  22.        cin >> num_alumnos;
  23.    }   while(num_alumnos < 1 || num_alumnos > MAX_ALUMNOS);
  24.    // El programa no pasara el filtro hasta que <num_alumnos> este entre 1 y <MAX_ALUMNOS>
  25.    // A partir de ahora usaremos <num_alumnos> en lugar de <MAX_ALUMNOS>
  26.  
  27.    // Pedimos las notas de cada alumno
  28.    for(size_t i = 0; i < num_alumnos; ++i){
  29.        cout << "Notas del alumno " << i+1 << endl;
  30.        for(size_t j = 0; j < NUM_NOTAS; ++j){
  31.        // Pedir cada nota
  32.        }
  33.        // Si vamos a guardar todos los promedios en un arreglo, podemos calcularlos aqui
  34.    }
  35.  
  36.    // ...El resto del programa...
  37. }

Yo creo que ya tienes una ayuda bastante grande con el programa. Ahora te queda terminarlo a ti (tampoco te lo iba a hacer entero...  :silbar:)
Si tienes algún problema en alguna parte, coloca tu código (entre etiquetas de código GeSHi) para que podamos ayudarte.
560  Programación / Programación C/C++ / Re: Ayuda con tarea de programa en lenguaje C en: 9 Agosto 2019, 13:49 pm
Así voy ahora, pero tengo ciertos errores con el modulo 1
Código:
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>



#define L_CLAVE 7
#define L_DESCRIPCION 20
#define L_BUFFER 100


struct producto {
char clave[L_CLAVE];
char descripcion[L_DESCRIPCION];
float precio;
};


void captura_producto(){

producto prod;
char buffer[L_BUFFER];

clrscr();
printf("Captura de producto\n\n");


/* captura de clave de producto */
printf("Clave: ");
gets(buffer);
if (strlen(buffer)<=L_CLAVE){
strcpy(prod.clave, buffer);
}
else{
printf("Error en captura de clave");
}

/* captura de descripcion */
printf("Descripcion: ");
gets(buffer);
if (strlen(buffer)<=L_DESCRIPCION){
strcpy(prod.descripcion, buffer);
}
else{
printf("Error en captura de descripcion");
}

/* captura de precio */
printf("Precio: ");
gets(buffer);
prod.precio = atof(buffer);

printf("\n\n\n");

printf("datos capturados\n");
printf("Clave: %s -- Decripcion: %s  Precio: %f ", prod.clave, prod.descripcion, prod.precio);

}


int main(){

captura_producto();
return 0;

}

Primero, te recomiendo no usar funciones no estándar ni librerías no estándar, en tu caso: <clrcsr()> y <conio.h> ya que según el compilador que se use no va a funcionar. Y segundo el único error que tiene ese código es que en C:
Código
  1. struct Producto{
  2.    //...
  3. };
  4.  
  5. Producto mi_producto; // ERROR!!
  6. struct Producto mi_producto; // Correcto!!
Para no escribir siempre <struct> cuando te refieras al tipo <Producto> puedes usar <typedef>:
Código
  1. typedef struct Producto{
  2.    // ...
  3. } Producto;
  4.  
  5. Producto mi_producto; // Correcto!!
Dejar claro que así lo que haces es permitirte usar el nombre <Producto> como sustituto de <struct Producto> pero NO estás creando ninguna variable llamada <Producto> de tipo <Producto>. En cambio si quitas <typedef> y haces:
Código
  1. struct Producto{
  2.    // ...
  3. } mi_producto;
Sí estás creando ya una variable de tipo <Producto>. No te recomiendo hacer esto aunque lo verás en muchos sitios ya que entonces la variable es global y no es una buena práctica.

Otros consejos:
  • Usar <fgets()> en lugar de <gets()>
  • Usar <strncpy()> en lugar de <strcpy()>
Las alternativas que te he puesto permiten un parámetro donde especificar la longitud. Esto hace que las funciones sean más seguras para evitar desbordamientos.
  • Si vas a comprobar si un nombre/clave/etc es demasiado larga, lo suyo sería después de avisar del error, permitir al usuario introducirlo de nuevo.

Pero vamos que estás cosas son recomendaciones que te digo. Si lo que quieres es acabarlo cuanto antes para entregarlo y ya pues como dicen muchos "Mientras compile..."
Páginas: 1 ... 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 [56] 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 ... 102
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines