Llevo 1 año estudiando C++ aprox., y aún no hallo sentido a los punteros..

Iniciado por digimikeh, 10 Febrero 2019, 00:43 AM

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

digimikeh

Hola amigos..

pues tal como se lee en el título, seguramente me creerán idiota o algo por el estilo (asumo que es muy probable que sea así después de hacer esta pregunta)..

Creí que los punteros tenían un poder enorme en la programación, referencia memoria en vez de valor, supuse que era la opción perfecta para interactuar con muchos datos, pero después de ver que los arreglos y matrices internamente se procesan como punteros (pues son las reglas que conozco al menos de manejar mucha información) no le hallé mas sentido a los punteros, es decir... si tengo :

Código (cpp) [Seleccionar]


int numero = 5;

void ConsultarNumero(){
   cout << "Numero es : " << numero << endl;
}

void ModificarNumero(){
    numero = 6;
}

void SumarUnoAlNumero(){
     numero++;
}



En vez de :

Código (cpp) [Seleccionar]


int main (){
   int * numero = new int;
   *numero = 5;
   consultarNumero(numero);
   modificarNumero(numero, 6);
   sumarUnoAlNumero(numero);

}

void consultarNumero(int * _numero){
     cout << "El numero es : " << *_numero << endl;
}

void modificarNumero(int * _numero, int _nuevoNumero){
    *_numero = _nuevoNumero;
}

void sumarUnoAlNumero(int * _numero){
    *_numero++;
}





Como verán, simplemente reemplacé el puntero por una variable global, de todas formas sin puntero estoy accediendo a una variable compartida sin la necesidad de consultar su memoria...

Alguien me podría guiar sobre el uso de punteros?
Gracias de antemano.
Dungeons & dragons;
dragons.Attack();

EdePC

Saludos,

- La utilidad de los punteros está más enfoca al uso eficiente de la Memoria, lo que se dice Memoria Dinámica, ya has tocado los temas de Listas Enlazadas, Variables de tamaño variable en tiempo de ejecución, etc.

- Te recomiendo este libro, en su página 155 habla sobre el uso de los Punteros y muchos ejemplos donde demuestra el porqué de su uso:
https://openlibra.com/es/book/fundamentos-de-programacion-con-el-lenguaje-de-programacion-c-ed-2017

Capítulo 12

Memoria Dinámica. Punteros

Hasta ahora, todos los programas que se han visto en capítulos anteriores almacenan su estado
interno por medio de variables que son automáticamente gestionadas por el compilador. Las varia-
bles son creadas cuando el flujo de ejecución entra en el ámbito de su definición (se reserva espacio
en memoria y se crea el valor de su estado inicial), posteriormente se manipula el estado de la varia-
ble (accediendo o modificando su valor almacenado), y finalmente se destruye la variable cuando el
flujo de ejecución sale del ámbito donde fue declarada la variable (liberando los recursos asociados
a ella y la zona de memoria utilizada). A este tipo de variables gestionadas automáticamente por el
compilador se las suele denominar variables automáticas (también variables locales), y residen en
una zona de memoria gestionada automáticamente por el compilador, la pila de ejecución, donde
se alojan y desalojan las variables locales (automáticas) pertenecientes al ámbito de ejecución de
cada subprograma.

Así, el tiempo de vida de una determinada variable está condicionado por el ámbito de su decla-
ración. Además, el número de variables automáticas utilizadas en un determinado programa está
especificado explícitamente en el propio programa, y por lo tanto su capacidad de almacenamiento
está también especificada y predeterminada por lo especificado explícitamente en el programa.
Es decir, con la utilización única de variables automáticas, la capacidad de almacenamiento de
un determinado programa está predeterminada desde el momento de su programación (tiempo de
compilación), y no puede adaptarse a las necesidades reales de almacenamiento surgidas durante
la ejecución del programa (tiempo de ejecución). (1)

La gestión de memoria dinámica surge como un mecanismo para que el propio programa, du-
rante su ejecución (tiempo de ejecución), pueda solicitar (alojar) y liberar (desalojar) memoria
según las necesidades surgidas durante una determinada ejecución, dependiendo de las circuns-
tancias reales de cada momento de la ejecución del programa en un determinado entorno. Esta
ventaja adicional viene acompañada por un determinado coste asociado a la mayor complejidad
que requiere su gestión, ya que en el caso de las variables automáticas, es el propio compilador el
encargado de su gestión, sin embargo en el caso de las variables dinámicas es el propio programa-
dor el que debe, mediante código software, gestionar el tiempo de vida de cada variable dinámica,
cuando debe ser alojada y creada, como será utilizada, y finalmente cuando debe ser destruida
y desalojada. Adicionalmente, como parte de esta gestión de la memoria dinámica por el propio
programador, la memoria dinámica pasa a ser un recurso que debe gestionar el programador, y se
debe preocupar de su alojo y de su liberación, poniendo especial cuidado y énfasis en no perder
recursos (perder zonas de memoria sin liberar y sin capacidad de acceso).

1 En realidad esto no es completamente cierto, ya que en el caso de subprogramas recursivos, cada invocación
recursiva en tiempo de ejecución tiene la capacidad de alojar nuevas variables que serán posteriormente desalojadas
automáticamente cuando la llamada recursiva finaliza.

digimikeh

Cita de: EdePC en 10 Febrero 2019, 02:26 AM
Saludos,

- La utilidad de los punteros está más enfoca al uso eficiente de la Memoria, lo que se dice Memoria Dinámica, ya has tocado los temas de Listas Enlazadas, Variables de tamaño variable en tiempo de ejecución, etc.

- Te recomiendo este libro, en su página 155 habla sobre el uso de los Punteros y muchos ejemplos donde demuestra el porqué de su uso:
https://openlibra.com/es/book/fundamentos-de-programacion-con-el-lenguaje-de-programacion-c-ed-2017

Capítulo 12

Memoria Dinámica. Punteros

Hasta ahora, todos los programas que se han visto en capítulos anteriores almacenan su estado
interno por medio de variables que son automáticamente gestionadas por el compilador. Las varia-
bles son creadas cuando el flujo de ejecución entra en el ámbito de su definición (se reserva espacio
en memoria y se crea el valor de su estado inicial), posteriormente se manipula el estado de la varia-
ble (accediendo o modificando su valor almacenado), y finalmente se destruye la variable cuando el
flujo de ejecución sale del ámbito donde fue declarada la variable (liberando los recursos asociados
a ella y la zona de memoria utilizada). A este tipo de variables gestionadas automáticamente por el
compilador se las suele denominar variables automáticas (también variables locales), y residen en
una zona de memoria gestionada automáticamente por el compilador, la pila de ejecución, donde
se alojan y desalojan las variables locales (automáticas) pertenecientes al ámbito de ejecución de
cada subprograma.

Así, el tiempo de vida de una determinada variable está condicionado por el ámbito de su decla-
ración. Además, el número de variables automáticas utilizadas en un determinado programa está
especificado explícitamente en el propio programa, y por lo tanto su capacidad de almacenamiento
está también especificada y predeterminada por lo especificado explícitamente en el programa.
Es decir, con la utilización única de variables automáticas, la capacidad de almacenamiento de
un determinado programa está predeterminada desde el momento de su programación (tiempo de
compilación), y no puede adaptarse a las necesidades reales de almacenamiento surgidas durante
la ejecución del programa (tiempo de ejecución). (1)

La gestión de memoria dinámica surge como un mecanismo para que el propio programa, du-
rante su ejecución (tiempo de ejecución), pueda solicitar (alojar) y liberar (desalojar) memoria
según las necesidades surgidas durante una determinada ejecución, dependiendo de las circuns-
tancias reales de cada momento de la ejecución del programa en un determinado entorno. Esta
ventaja adicional viene acompañada por un determinado coste asociado a la mayor complejidad
que requiere su gestión, ya que en el caso de las variables automáticas, es el propio compilador el
encargado de su gestión, sin embargo en el caso de las variables dinámicas es el propio programa-
dor el que debe, mediante código software, gestionar el tiempo de vida de cada variable dinámica,
cuando debe ser alojada y creada, como será utilizada, y finalmente cuando debe ser destruida
y desalojada. Adicionalmente, como parte de esta gestión de la memoria dinámica por el propio
programador, la memoria dinámica pasa a ser un recurso que debe gestionar el programador, y se
debe preocupar de su alojo y de su liberación, poniendo especial cuidado y énfasis en no perder
recursos (perder zonas de memoria sin liberar y sin capacidad de acceso).

1 En realidad esto no es completamente cierto, ya que en el caso de subprogramas recursivos, cada invocación
recursiva en tiempo de ejecución tiene la capacidad de alojar nuevas variables que serán posteriormente desalojadas
automáticamente cuando la llamada recursiva finaliza.

Gracias por tu respuesta!

Entonces los punteros están enfocados a ser utilizados en casos donde se requiera solicitar memoria dinámica, por ejemplo la dimensión personalizada de una matriz o arreglo.. verdad?..  y en que otros ámbitos se puede usar?
saludos!
Dungeons & dragons;
dragons.Attack();

CalgaryCorpus

Hay otras situaciones en que los punteros tambien son utiles y muestran su poderio.

Los punteros pueden guardar direcciones de memoria de otras zonas que no has pedido explicitamente, y leer o escribir alli informacion o datos que tu quieras.

Las funciones de tu programa tambien tienen direcciones de memoria y un puntero puede guardar esa direccion y luego ir e invocar esa funcion. Puedes usar esto para, en una clase por ejemplo, hacer que un codigo sirva para multiples propositos, pues la funcionalidad se "inyecta" hacia otra, parecido al patron de dicen~o Strategy.
No es necesario realmente una clase para este uso, una funcion tambien puede recibir como parametro a un puntero a una funcion, para hacer algo parecido a lo mencionado previamente, por ejemplo, la funcion qsort recibe como tercer parametro la direccion de memoria de una funcion
e.g. http://www.cplusplus.com/reference/cstdlib/qsort/
Aqui mi perfil en LinkedIn, invitame un cafe aqui

Loretz

El uso de punteros en C++ es algo muy excepcional, en los últimos 10 años ya casi nadie en el mundo real usa punteros.

No se usa new, (no se usa delete) se usan smart pointers (std::unique_ptr y std::shared_ptr).

Es más, siempre que se puede elegir entre una variable en la stack (pila, variables de duración automática) o en memoria libre, probablemente la mejor opción sea la stack.

En las funciones de tu ejemplo, en la primera deberías haber pasado una copia del numero; en la segunda y la tercera deberías haber pasado una referencia.

void consultarNumero(int _numero) {
   cout << "El numero es : " << _numero << endl;
}

void modificarNumero(int& _numero, int _nuevoNumero) {
   _numero = _nuevoNumero;
}

void sumarUnoAlNumero(int& _numero) {
   _numero++;
}

int main() {
   int numero = 5;

   consultarNumero(numero);
   modificarNumero(numero, 6);
   sumarUnoAlNumero(numero);
}




Un caso común en el que suele preferirse un puntero es cuando se necesita interactuar con alguna librería en C, o pasar como parámetro el puntero crudo de un smart pointer, sin transferir el ownership y sabiendo que el tiempo de vida del objeto está manejado por quién invoca la función. También podría pasarse una referencia, pero si el parámetro puede ser nulo no irá una referencia, irá un puntero. Ejemplo:

struct ObjetoMuyPesado {
    static inline const string BibliotecaDeAlejandria { "Empezando por el catálogo ... " };
    size_t size() const { return BibliotecaDeAlejandria.max_size(); }
};

void cuantosBytes(ObjetoMuyPesado* biblio) {
    if (!biblio)
        throw std::runtime_error("quemada");

    std::cout << "La biblioteca de Alejadnria tiene "
        << biblio->size() << " bytes\n";
}

int main() {
    auto biblio = std::make_unique<ObjetoMuyPesado>();
    cuantosBytes(biblio.get());
}


Cuando eres un principiante te esfuerzas por entender y aprender a usar punteros, es natural, supongo. Cuando ya crees que has aprendido, te sientes orgulloso y quieres mostrar el mundo que sabes usar punteros. Cuando has aprendido algo no usas punteros.


MAFUS

Por lo que veo C++ ha cambiado mucho; pero en C, un lenguaje mucho más fiel a sus orígenes sí son necesarios los punteros pues el tratamiento de la memoria sigue estando en manos del programador.

Por otra parte si te alejas del modelo de memoria protegido de los uP modernos y sus sistemas operativos y programas para modos reales, uC y estas historias, puedes acceder directamente a diferentes zonas de memoria; así puedes cambiar datos directamente en la memoria de vídeo, acceder a los datos que te proporcionan los sensores y periféricos mapeados en memoria y hacer diferentes cosas que los lenguajes sin punteros no pueden hacer.

Loretz

Cita de: MAFUS en 10 Febrero 2019, 19:40 PM
Por lo que veo C++ ha cambiado mucho; pero en C, un lenguaje mucho más fiel a sus orígenes sí son necesarios los punteros pues el tratamiento de la memoria sigue estando en manos del programador.

Por otra parte si te alejas del modelo de memoria protegido de los uP modernos y sus sistemas operativos y programas para modos reales, uC y estas historias, puedes acceder directamente a diferentes zonas de memoria; así puedes cambiar datos directamente en la memoria de vídeo, acceder a los datos que te proporcionan los sensores y periféricos mapeados en memoria y hacer diferentes cosas que los lenguajes sin punteros no pueden hacer.

MAFUS; el C++ usa punteros, es el programador de C++ el que ya casi no usa más "punteros crudos", punteros del estilo C.

string, vector, list, unique_ptr, referencias, ... en el fondo son (tienen) punteros, sólo que se usan de manera más segura y eficiente.

No importa cuántos archivos haya en el proyecto, cuántos programadores han estado ahí durante años, en C++ un memory leaks es una rara avis. Y es el programador quien decide el uso de la memoria libre, cuándo tomarla, cuando liberarla, eso siempre fue así. Y con las referencias a rvalues viene también la semántica de move.

Por ejemplo, aprovechando que se mencionó más arriba; nadie usaría qsort en C++, la std::sort de C++ pude ser 10 veces más rápida para hacer lo mismo (no es posible exagerar el énfasis, 10 veces más rápida). https://www.geeksforgeeks.org/c-qsort-vs-c-sort/


digimikeh

Gracias por las respuestas, tendré que enfocarme en qué y cuando usar punteros, lo que pasa es que en mi afán de querer usarlos, cometo errores, típicamente cuando aprendo algo nuevo, me dan ganas de usar ese algo a la fuerza, y no debería pensar asi..

saludos.
Dungeons & dragons;
dragons.Attack();