Problema con los punteros C++

Iniciado por 98Fran, 25 Agosto 2019, 16:47 PM

0 Miembros y 1 Visitante están viendo este tema.

98Fran

Estoy empezando con C++ y hay algo que no entiendo por qué esta pasando, aquí esta mi codigo:

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

using namespace std;

int pedirNombre();
void contarVocales(char*,int);
void mostrarVocales();

int *p_a, *p_e, *p_i, *p_o, *p_u; // punteros de los contadores;
char *nombre;

int main(){

contarVocales(nombre,pedirNombre());
mostrarVocales();



getch();
return 0;
}

int pedirNombre(){
int longitud;
char usuario[30];

cout<<"Digite su nombre: ";
cin.getline(usuario,30,'\n');
longitud = strlen(usuario);

nombre = &usuario[0];

return longitud;
}

void contarVocales(char* nombre,int n){
int c_a, c_e, c_i, c_o, c_u; //contador vocales;

c_a = 0;
c_e = 0;
c_i = 0;
c_o = 0;
c_u = 0;

p_a = &c_a;
p_e = &c_e;
p_i = &c_i;
p_o = &c_o;
p_u = &c_u;

for(int i=0;i<n;i++){
switch(*(nombre+i)){
case 'a': c_a++; break;
case 'e': c_e++; break;
case 'i': c_i++; break;
case 'o': c_o++; break;
case 'u': c_u++; break;
}
}
}

void mostrarVocales(){
cout<<"num. de a: "<<*p_a<<endl;
cout<<"num. de e: "<<*p_e<<endl;
cout<<"num. de i: "<<*p_i<<endl;
cout<<"num. de o: "<<*p_o<<endl;
cout<<"num. de u: "<<*p_u<<endl;
}


SALIDA:
---------------------------------------------------------------------------------------
Digite su nombre: aaaaaeeeeiiioou
num. de a: 1
num. de e: 4
num. de i: 3
num. de o: 2
num. de u: 1
---------------------------------------------------------------------------------------------

Si pongo un cout dentro de la funcion contarVocales() *p_a me da el valor correcto pero una vez sale de esa funcion y se meustra en la funcion mostrarVocales() me da el valor 1 todo el rato. El resto de vocales funcionan correctamente menos en la vocal a que es como si se borrase el valor al acabar la funcion y se sustituye por 1.

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).

PD2: por que cuando se usa una variable global como *nombre se declara dentro de el prototipo de función si en teoría si lo pasas sin referenciar también sive puesto que es un puntero global, es decir, en ves de usar:

Código (cpp) [Seleccionar]
void contarVocales(char*,int);

usar:

Código (cpp) [Seleccionar]
void contarVocales();

y escribir directamente la variable como en:

Código (cpp) [Seleccionar]
int pedirNombre();


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.

Se que es mucho pero la verdad agradecería a algún iluminado que me resuelva estas dudas que me están volviendo loco  ;D ;D. No quiero seguir más adelante con el curso ya que prefiero tener todo bien claro antes de profundizar más. Gracias!!!

K-YreX

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 (cpp) [Seleccionar]

int incrementar(int a){
    return ++a;
}
int a = 2;
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 (cpp) [Seleccionar]

int incrementar(int &a){
    return ++a;
}
int a = 2;
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 (cpp) [Seleccionar]

int incrementar(int *p){
    return ++(*p);
}
int a = 2:
int *pa = &a;
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
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

98Fran

#2
Muchas gracias!!!, ahora surge otro problema xD:

Código (cpp) [Seleccionar]


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

using namespace std;

int pedirNombre(char&);
void contarVocales(int);
void mostrarVocales();

int *p_vocales; // punteros de los contadores;
char *nombre;

int main(){
int c_vocales[5] ={0,0,0,0,0}, longitud;  //0 == a, 1 == b ... 4 == u;
char usuario[30];

nombre = &usuario[0];
p_vocales = &c_vocales[0];

longitud = pedirNombre(usuario[30]);
contarVocales(longitud);
mostrarVocales();



cin.get();
return 0;
}

int pedirNombre(char& us){
int longitud;

cout<<"Digite su nombre: ";
cin.getline(us,30,'\n');
longitud = strlen(us);

return longitud;
}

void contarVocales(int n){

for(int i=0;i<n;i++){
switch(*(nombre+i)){
case 'a': *(p_vocales+i) = *(p_vocales+i) + 1; break;
case 'e': *(p_vocales+i) = *(p_vocales+i) + 1;; break;
case 'i': *(p_vocales+i) = *(p_vocales+i) + 1;; break;
case 'o': *(p_vocales+i) = *(p_vocales+i) + 1; break;
case 'u': *(p_vocales+i) = *(p_vocales+i) + 1; break;
}
}

}

void mostrarVocales(){
cout<<"num. de a: "<<p_vocales[0]<<endl;
cout<<"num. de e: "<<p_vocales[1]<<endl;
cout<<"num. de i: "<<p_vocales[2]<<endl;
cout<<"num. de o: "<<p_vocales[3]<<endl;
cout<<"num. de u: "<<p_vocales[4]<<endl;
}


Me da error a la hora de usar cin.getline en la cadena de caractares que he pasado.

98Fran

#3
Al final he podido resolverlo (aun que dudo que sea de la manera mas adecuada xD):
Código (cpp) [Seleccionar]
#include <iostream>
#include <stdlib.h>
#include <string.h>

using namespace std;

int pedirNombre();
void contarVocales(int);
void mostrarVocales();

int *p_vocales; // punteros de los contadores;
char *nombre;

int main(){
int c_vocales[5] ={0,0,0,0,0}, longitud;  //0 == a, 1 == b ... 4 == u;
p_vocales = &c_vocales[0];

longitud = pedirNombre();
contarVocales(longitud);
mostrarVocales();


cout<<"\n"<<nombre<<endl;

cin.get();
return 0;
}

int pedirNombre(){
int longitud;

cout<<"Digite su nombre: ";
nombre = new char[30];
cin.getline(nombre,30,'\n');
longitud = strlen(nombre);

return longitud;
}

void contarVocales(int n){

for(int i=0;i<n;i++){
switch(nombre[i]){
case 'a': *(p_vocales) = *(p_vocales) + 1; break;
case 'e': *(p_vocales+1) = *(p_vocales+1) + 1; break;
case 'i': *(p_vocales+2) = *(p_vocales+2) + 1; break;
case 'o': *(p_vocales+3) = *(p_vocales+3) + 1; break;
case 'u': *(p_vocales+4) = *(p_vocales+4) + 1; break;
}
}
}

void mostrarVocales(){
cout<<"num. de a: "<<p_vocales[0]<<endl;
cout<<"num. de e: "<<p_vocales[1]<<endl;
cout<<"num. de i: "<<p_vocales[2]<<endl;
cout<<"num. de o: "<<p_vocales[3]<<endl;
cout<<"num. de u: "<<p_vocales[4]<<endl;
}


/*                             SALIDA:
---------------------------------------------------------------
Digite su nombre: aaaaeaeeieioiou
num. de a: 5
num. de e: 4
num. de i: 3
num. de o: 2
num. de u: 1

aaaaeaeeieioiou

---------------------------------------------------------------
PD: tenía mal lo del conteo que había puesto:
Código (cpp) [Seleccionar]
case 'a': *(p_vocales+i) = *(p_vocales+i) + 1; break;

Y no tenia que usar la i y otro error que me salto era que no podía usar ++ con los punteros:
Código (cpp) [Seleccionar]
case 'a': *(p_vocales) ++; break;

Aun que no se si usando esto serviría también:
Código (cpp) [Seleccionar]
case 'a': *(p_vocales) += 1; break;

Muchisimas gracias, ando un poco liado con la nomenglatura a la hora de pasar vectores por las funciones, pero al menos ya tengo esto resuelto  ;-) ;-). Si me resolvieses la duda de como pasar vectores ya sea int o cadenar char por referencia en funciones te lo agradecería muchimo tambien jajaja. Mil gracias ^^.

K-YreX

#4
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 (cpp) [Seleccionar]

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 (cpp) [Seleccionar]

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 (cpp) [Seleccionar]

    // creamos un array de <int>
    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
    int *puntero = &vocales[0]; // hacemos que <puntero> apunte al comienzo de <vocales>
    // 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:)
    int *puntero = vocales; // esto equivale a lo anterior
    // 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 (cpp) [Seleccionar]

    vocales[i]
    *(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 (cpp) [Seleccionar]

    int vocales[5] = {1,2,3,4,5};
    int *puntero = vocales;
    ++puntero; // ahora <puntero> apunta a la siguiente posicion
    ++(*puntero); // ahora incrementamos el valor al que apunta <puntero> que es <vocales[1]> entonces <vocales> = {1,3,3,4,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.
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

98Fran

#5
Funciona perfecto tambien:
Código (cpp) [Seleccionar]
#include <iostream>
#include <stdlib.h>
#include <string.h>

using namespace std;

int pedirNombre(char*);
void contarVocales(char*,int,int*);
void mostrarVocales(int*);


int main(){
int c_vocales[5] ={0,0,0,0,0}, longitud;  //0 == a, 1 == b ... 4 == u;
char nombre[30];

longitud = pedirNombre(nombre);
contarVocales(nombre,longitud,c_vocales);
mostrarVocales(c_vocales);


cout<<"\n"<<nombre<<endl;

cin.get();
return 0;
}

int pedirNombre(char* nombre){
int longitud;

cout<<"Digite su nombre: ";

cin.getline(nombre,30,'\n');
longitud = strlen(nombre);

return longitud;
}

void contarVocales(char*nombre,int n,int*p_vocales){

for(int i=0;i<n;i++){
switch(nombre[i]){
case 'a': *(p_vocales) = *(p_vocales) + 1; break;
case 'e': *(p_vocales+1) = *(p_vocales+1) + 1; break;
case 'i': *(p_vocales+2) = *(p_vocales+2) + 1; break;
case 'o': *(p_vocales+3) = *(p_vocales+3) + 1; break;
case 'u': *(p_vocales+4) = *(p_vocales+4) + 1; break;
}
}
}

void mostrarVocales(int* vocales){
cout<<"num. de a: "<<vocales[0]<<endl;
cout<<"num. de e: "<<vocales[1]<<endl;
cout<<"num. de i: "<<vocales[2]<<endl;
cout<<"num. de o: "<<vocales[3]<<endl;
cout<<"num. de u: "<<vocales[4]<<endl;
}


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 &, 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 "*".

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?.

    Y  por qué no se usa (char& letra) como por ejemplo sí pasar un solo char como 'a'.
    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.

    PD2: Lo explicas todo muy bien jajaja gracias por la paciencia que estas teniendo conmigo pero  incluso después de haber victo los pocos videos en youtube que hay de puntero y comprado 2 cursos en udemy de c++, nadie explica en profundidad como funciona la sintaxis y sus posibles variaciones si no que te obligan a aceptarlo todo como dogma y una vez hago mis propios problemas ya no se por donde tirar.

K-YreX

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 (cpp) [Seleccionar]

void funcion1(int a); // variable por valor
void funcion2(int &a); // variable por referencia
void funcion3(int *pa); // puntero por valor
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 (cpp) [Seleccionar]

int numeros[5] = {1,2,3,4,5};
// aqui podemos usar <numeros> como si fuera un puntero que apunta a &numeros[0]
// por lo tanto podemos hacer cosas como:
cout << *numeros << endl; // imprime: 1
for(size_t i = 0; i < 5; ++i)
    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 (cpp) [Seleccionar]

void recorrerVector1(int *numeros, int longitud){
    for(int i = 0; i < longitud; ++i)
        cout << *(numeros+i) << endl;
}

void recorrerVector2(int *numeros, int longitud){
    for(int i = 0; i < longitud; ++i){
        cout << *numeros << endl;
        ++numeros;
    }
}

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 (cpp) [Seleccionar]

int numeros[5] = {1,2,3,4,5};
int *puntero = numeros;
recorrerVector2(puntero, 5); // aqui no hay problemas porque aunque <puntero> se mueva, <numeros> siempre apunta al comienzo del vector
//en cambio si hacemos esto...
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 (cpp) [Seleccionar]

int pedirNombre(char& us){
int longitud;

cout<<"Digite su nombre: ";
cin.getline(us,30,'\n');
longitud = strlen(us);

return longitud;
}

¿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 (cpp) [Seleccionar]

void funcion1(int a){
    cout << "La variable vale: " << a << " y su direccion de memoria es: " << &a << endl;
    // Se muestra la direccion de memoria de la copia
    // IMPORTANTE: el valor se obtiene con el nombre tal cual y la direccion con &
}

void funcion2(int &a){
    cout << "La variable vale: " << a << " y su direccion de memoria es: " << &a << endl;
    // Se muestra la direccion de memoria de la original
    // IMPORTANTE: el valor se obtiene con el nombre tal cual y la direccion con &
}

void funcion3(int *pa){
    cout << "La variable vale: " << *pa << " y su direccion de memoria es: " << pa << endl;
    // Se muestra la direccion de memoria de la original pero no de <pa> sino a la que apunta <pa>
    // IMPORTANTE: el valor se obtiene con * y la direccion con el nombre tal cual
}

// USO DE LAS FUNCIONES
int numero = 2;
funcion1(numero); // se pasa un <int>
funcion2(numero); // se pasa un <int> aunque el programa ya sabe que es por referencia
funcion3(numero); // ERROR: El programa espera una direccion de memoria (puntero) y le estamos pasando un <int>
funcion3(&numero); // Correcto: Le pasamos una direccion de memoria sin crear un puntero auxiliar (que tambien se podria hacer)

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 (cpp) [Seleccionar]

int pedirNombre(char *nombre){
    cout << "Introduce nombre: ";
    cin.getline(nombre, 30, '\n');
    return strlen(nombre);
}

// USO DE LA FUNCION
int longitud = pedirNombre(nombre); // Como hemos visto antes tiene que recibir directamente la direccion de memoria, bien asi o bien asi:
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 (cpp) [Seleccionar]

int pedirNombre(char &nombre){
    cout << "Introduce tu nombre: ";
    cin.getline(&nombre, 30, '\n');
    return strlen(&nombre);
}
// o bien:
int pedirNombre(char &nombre){
    char *puntero = &nombre;
    cout << "Introduce nombre: ";
    cin.getline(puntero, 30, '\n');
    return strlen(puntero);
}

// USO DE LA FUNCION
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.
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

98Fran

#7
Ahora sí que sí, más claro el agua. No sabía muy bien por donde tirar, o por punteros o por referencias, la nomenclatura cuando es por referencia en una cadena o vector, pero ahora ya lo tengo todo mucho mas claro.

Supongo que cuando avance más en el temario llegare a la sección de variables dinámicas, no había puesto lo del delete[] porque con todos los otros problemas que tenía se me había pasado. (De momento solo se crear variables dinámicas ya que hizo un pequeño inciso en el curso hablando de como ahorrar espacio en la memoria utilizando ese tipo de variables ya que supongo que si es de forma didáctica un par de vectores[100] no van a saturar la RAM pero si fuera algo más elaborado supongo que tiene que haber un limite).


Muchas gracias de verdad ^^. Ya fuera offtopic xD estoy haciendo un juego con unreal y al pasarlo a multijugador los blueprints(es codigo pero en viñetas unidas por nodos, la lógica básica pero no hace falta saber punteros o funciones) no sirven ya que ocurren Bugs cuando modificas ciertos valores y hay que usar C++. Lo que no se es si luego al usar visual studio la forma de programar va a ser distinta a como funciona Dev C++.

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?

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?.

K-YreX

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
Código (cpp) [Seleccionar]

cout << "Todos tenemos un defecto, un error en nuestro código" << endl;

98Fran

#9
He hecho un pequeño programa con las tres formas de pasar vectores por funciones que me has comentando para interiorizarlo un poco y que no se me olviden xD, pero tengo un problema a la hora de imprimir la dirección de memoría en donde queda almacenada la cadena.

Código (cpp) [Seleccionar]
#include <iostream>
#include <cstring>

using namespace std;

void pedirNombre1(char*);
void pedirNombre2(char*);
void pedirNombre3(char&);
void contadorVocales1(char*);
void contadorVocales2(char*);
void contadorVocales3(char&);


int main(){
char nombre1[30], *p_nombre;
char nombre2[30];
char nombre3[30];

p_nombre = &nombre1[0];

pedirNombre1(p_nombre);
pedirNombre2(nombre2);
pedirNombre3(nombre3[0]);
contadorVocales1(p_nombre);
contadorVocales2(nombre2);
contadorVocales3(nombre3[0]);


cin.get();
return 0;
}

void pedirNombre1(char *p_nombre){
cout<<"Ej.1 -- Digite una frase: ";
cin.getline(p_nombre,30,'\n');
}

void pedirNombre2(char *daigual2){
cout<<"\nEj.2 -- Digite una frase: ";
cin.getline(daigual2,30,'\n');
}

void pedirNombre3(char &daigual3){
cout<<"\nEj.3 -- Digite una frase: ";
cin.getline(&daigual3,30,'\n');
}

void contadorVocales1(char* p_daigual){

cout<<"\n------------------------------------------------------";
cout<<"\nEj.1 -- La frase digitada es: "<<p_daigual<<". En la direccion: "<<*p_daigual;

}

void contadorVocales2(char* daigual){

cout<<"\n------------------------------------------------------";
cout<<"\nEj.1 -- La frase digitada es: "<<daigual<<". En la direccion: "<<*daigual;

}

void contadorVocales3(char& daigual){

cout<<"\n------------------------------------------------------";
cout<<"\nEj.1 -- La frase digitada es: "<<&daigual<<". En la direccion: "<<daigual;

}


Si uso con char me muestra toda la cadena:

Código (cpp) [Seleccionar]
char nombre[] = {'a','b','c'}, *p_nombre2;
cout<<"Dir.1: "<<nombre[1]<<" | "<<p_nombre2<<endl;

//Salida: Dir.1: b | abc


Pero si lo uso con int me muestra la posición, por?

Código (cpp) [Seleccionar]
int nombre[] = {1,2,3}, *p_nombre2;
cout<<"Dir.1: "<<nombre[1]<<" | "<<p_nombre2<<endl;
//Salida: Dir.1: 2 | 0x6ffdf0


PD: pongo daigual para así acordarme que no hace falta poner el mismo nombre de la variable de main en la función, normalmente suelo acortar el nombre o alargarlo pero como era un ejemplo lo he dejado asi.