Punteros en c++

Iniciado por Julia13, 26 Mayo 2021, 00:44 AM

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

Eternal Idol

Por supuesto aunque entendi que habia sido explicado en este hilo, salvo que asumamos un desconocimiento de lo que es una direccion de memoria. Sin dudas para poder comprender cabalmente el codigo generado y depurarlo en condiciones es necesario profundizar en varias aristas pero juraria que el esquema de memoria o la convencion de llamada no son parte del standard del lenguaje sino que corren por cuenta del compilador bajo la plataforma en cuestion.




Habria que pensar que es mas probable, si que el que pregunto por punteros en C++ mañana compile tu ejemplo (y le de problemas) o que escriba firmware.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

MAFUS

Entiendo tu postura de dejar las cosas lo más sencillas posibles, pero tal vez esa sencillez no sea suficiente. Estarás de acuerdo conmigo que el problema del entendimiento de los punteros es recurrente en todo el que ha estudiado este lenguaje. Yo, por mi parte, hasta que no vi a los punteros de esa forma no llegué a comprenderlos del todo y por eso, creo yo, que deberían enseñarse de esa forma.

Es cierto que el programa donde se asigna una constante como dirección de memoria no le va a funcionar. Pero sirve de ejemplo para mostrar que ocurre por debajo, y de ahí la construcción progresiva hasta llegar al puntero que recoge una dirección de una variable; es decir, es para que vaya viendo el proceso natural de lo que ocurre en la máquina.

O tal vez es que por mi curiosidad me gusta saber esas cosas.

Tienes razón, tal vez no se dedique a hacer firmwares, pero quien sabe si un estudiante de electrónica si ve este post y está interesado en saber cómo funcionan los punteros en C++. Siempre intento dar las respuestas pensando en un amplio espectro de gente que pueda leerla.

Y si, tienes razón, el esquema de memoria variará de arquitectura a arquitectura y por ende el de las llamadas a funciones. Pero también es un punto a tener en cuenta.

H4cker K

Espero que no sea demasiado tarde para contestar pero paso a responder, los punteros es una de las cosas que la verdad mas me costo dominar pero fue porque cuando lei documentacion pues digamos como que no explicaba bien todo al fondo y pus espero hacerme entender porque los voy a explicar como a mi me hubiera gustado que me lo explicaran, bueno, ves las variables? para este ejemplo vamos a tomar una a casa, el valor de la variable sera el numero de personas viviendo ahi, en una casa hay 5 personas viviendo y esa casa es A, sin embargo, una casa tiene una direccion verdad y digamos que dicha direccion sea la Calle Pepe no.245 como ejemplo(obviamente es una direccion falsa) ahora digamos que hay una persona que le quiere enviar una carta a uno de los integrantes de la familia de 5, esa carta tiene que tener la DIRECCION de la casa para poder enviarla asi que tendra que escribir en la carta que es para fulanito de tal para Calle Pepe no.245. Pues bueno lo mismo pasa con las compus, digamos que declaramos la variable "int a = 5"(haciendo el ejemplo de la casa), pero ahora queremos tener algo que no tenga exactamente un valor, sino que tenga una direccion de memoria, es decir, algo que le diga a la compu onta la variable a y como hacemos eso? pues nada mas y nada menos que con los punteros, en programacion c/c++ para saber la direccion de memoria de una variable se utiliza el caracter address-of que es este de aqui& y para que me entienda mejor, zy tu quieres decir la direccion de la casa A del ejemplo anterior como si fuera estilo programacion dirias &casa_A = Calle Pepe no.245 y pues como practica en lo mientras vamos a depurar un sencillo programa con gdb:


#include <stdio.h>

int main(void){
        int a = 5;

        return 0;
}


Una vez compilado este programa(yo lo compilare en linux y para hacerlo desde ahi es con: gcc nombre_programa_c.c -o nombre_programa_compilado), ahora abriremos el gdb con

Código (bash) [Seleccionar]

gdb -q nombre_programa_compilado
                                                                 

ahora lo que haremos sera poner un breakpoint justo despues de que la instruccion de la declaracion de a se haya dado y eso es en la linea 5 como podras ver(int a = 5; esta en la linea 4) asi que pondremos un breakpoint en la linea 5  y ejecutaremos el programa con:

Código (bash) [Seleccionar]

(gdb) break 5
Breakpoint 1 at 0x1129: file ejemplo.c, line 6.
(gdb) run


yo le puse ejemplo.c al archivo por cierto pero continuando, a tiene un valor de 5 y para comprobarlo desde gdb podemos usar:

Código (bash) [Seleccionar]

(gdb) print a
$1 = 5
(gdb) p a
$2 = 5


los $1 y $2 son variables que se guardaran temporalmente, puse print y p porque muchos comandos de gbd estan abreviados y pus eso pero lo que importa es que como dice gdb a tiene un valor de 5 pero ahora queremos saber la direccion de memoria de a y para hacerlo usaremos el caracter &(address-of) antes de a, asi:

Código (bash) [Seleccionar]

(gdb) p &a
$3 = (int *) 0x7fffffffdfcc


y como puedes ver la direccion de memoria de a es: 0x7fffffffdfcc(en este ejemplo esta larga porque en donde estoy corriendo el programa es de 64 bits pero en la de 32 bits es de unos 4 bytes pero ahorita no me voy a poner a explicar eso) esta direccion de memoria vendria ser como la direccion de la casa A que era Calle Pepe no.245, si queremos hacer un programa donde ya no tengamos que depurar podemos usar el formato de cadena %p en un printf asi:


include <stdio.h>

int main(void){
        int a = 5;
        printf("Direccion de memoria de a es: %p", &a);
        return 0;
}


pues ahora zy con todo esto porfin puedo pasar a explicar lo que es un puntero, hasta ahora vimos que podiamos saber la direccion de memoria de a usando el caracter & pero  ahora queremos que haya algo que pueda tener el valor de la direccion de memoria de a y aqui es donde entran los punteros, en c/c++ para declarar los punteros se utiliza el caracter * antes de poner el nombre del puntero, para entenderme mejor veamos un ejemplo:


#include <stdio.h>

int main(void){
        int a = 5;
        int *pa;
        pa = &a;
        return 0;
}


aqui tenemos a nuestro puntero pa, yo le puse de nombre pa aunque perfectamente se podia declarar como int *a; pero le pongo pa porque me gusta diferenciar punteros de variables cuando se tratan de proyectos complejos, siempre que se declaren punteros recuerda que se usa *<nombre_puntero> y una cosa tambien, es importante siempre indicar el tipo de datos del puntero que en este caso es int porque zy juntas por ejemplo uno char con uno int pues obviamente te va a salir error porque se va malinterpretar a la hora del tamaño de los bytes pero ya teniendo esto en cuenta de que zy vas a usar una variable y un punteros los uses con el mismo tipo pues explicare la parte de pa = &a; esto es asi porque recuerda que los punteros no tienen un valor sino que tienen direcciones de memoria en su lugar y con el caracter & es como indicabamos la direccion de memoria de una variable, asi zy le dices a la compu que pa es igual a la direccion de memoria de a pues te va a entender que te estas poniendo algo para saber onta a(espero que no este cantinfleando a este punto xd) pero bueno, vamos a correr este programa con gdb en la linea que esta despues de la asignacion de la direccion que tendra el puntero pa  y es la linea 7 asi:

Código (bash) [Seleccionar]

(gdb) break 7
Breakpoint 1 at 0x1138: file ejemplo.c, line 7.
(gdb) run


y empezaremos a ver los ver que tienen dentro la variable y el puntero:

Código (bash) [Seleccionar]

(gdb) p a
$1 = 5
(gdb) p &a
$2 = (int *) 0x7fffffffdfc4
(gdb) p pa
$3 = (int *) 0x7fffffffdfc4
(gdb) p &pa
$4 = (int **) 0x7fffffffdfc8
(gdb)


y como puedes notar a sigue teniedo el valor de 5 pero lo interesante esta en el valor de la direccion de memoria de a y la direccion a la que apunta pa que son las mismas y esa direccion es: 0x7fffffffdfc4 y es de hecho una cosa importante que se me olvido mencionar y es que la direccion a la que apuntara un puntero no sera la misma que la de su direccion de memoria, tomare nuevamente el ejemplo de la casa A, cuando alguien le mando la carta a la casa A el que la mando no estaba en la casa A, estaba en B y sus direcciones no son las misma y es que asi B puede estar en Calle Jose no.456 y que el mande la carta a Calle Pepe no.245 y pues aca pasa lo mismo, pa puede estar en un lado pero tener el donde esta otro lugar y pues hasta aca todo bien pero ahora explicare la parte donde la mayoria se confunden y es esto:

Código (bash) [Seleccionar]

(gdb) p *pa
$5 = 5
(gdb) p pa
$6 = (int *) 0x7fffffffdfc4
(gdb)


cuando despues de declarar un putero volvemos a poner el signo * nos referimos al valor de la variable a la que esta apuntando el puntero, pa esta apuntando a la variable a y a tiene el valor de 5 y zy nos referimos al valor de lo apuntado tambien deberia ser 5 y es asi  como lo muestra p *pa, sin embargo cuando no ponemos de nuevo el signo * significa que nos referiremos a la direccion a la que apunta, entonces en resumen:

*pa = valor de lo que esta apuntando
pa = direccion de memoria de lo que esta apuntando

Ahora para aclarar un poco mas esto el siguiente programa lo terminara de hacer:


#include <stdio.h>

int main(void){
        int a = 5;
        int *pa;
        pa = &a;
        printf("El valor de la variable a la que apunta pa es: %i", *pa);
        printf("\nEl valor de la variable a es: %i", a);
        printf("\nLa direccion de memoria a la que apunta pa es: %p", pa);
        printf("\nLa direccion de memoria de a es: %p", &a);
        return 0;
}


Entonces cuando te sientas confundido con los punteros siempre recuerda que *pa hace referencia al valor de lo que esta apuntando y pa a la direccion de lo que apunta, y todavia esta lo de las funciones pero creo que por el momento es suficiente y por zy acaso, la salida del programa anterior es:

El valor de la variable a la que apunta pa es: 5
El valor de la variable a es: 5
La direccion de memoria a la que apunta pa es: 0x7ffdd29322f4
La direccion de memoria de a es: 0x7ffdd29322f4

y ya como ultimo te dejare aqui unos programas que hacen practicamente lo mismo(a uno nada mas le agregue una comparacion para saber cuando un usuario intenta engañar al programa pero es lo de menos), para que veas la diferencia entre usar y no los punteros:

SIN punteros:


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

int main(void){
int a, c, d, b = 1000;
printf("Bienvenido a cajero automatico 1!!!, tienes 1000 varos");
printf("\n\nOpcion 1: Retirar");
printf("\nOpcion 2: Depositar");
printf("\nOpcion 3: Salir");
printf("\n\n\nEliga una opcion: ");
scanf("%i", &a);
switch(a){
case 1:
c;
printf("\n\nIngrese la cantidad a retirar: ");
scanf("%i", &c);
d = b - c;
printf("\nLa cantidad que quedo es de: %i", d);
system("pause>nul");
break;

case 2:
c;
printf("\n\nIngrese la cantidad a depositar: ");
scanf("%i", &c);
d = b + c;
printf("\nLa cantidad que quedo es de: %i", d);
system("pause>nul");
break;

case 3:
printf("\n\nSaliendo...");
system("pause>nul");
break;

default:
printf("Ingrese algo correcto");
system("pause>nul");
break;
}
return 0;
}


CON punteros:


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

struct cajero{
uint16_t cantidad;
uint16_t varia;
uint16_t deposito;
uint16_t numero;
uint16_t *pcantidad;
};

void retirar(int *cantidad_retirar, int *valor_o){
struct cajero retir;
if(*cantidad_retirar <= 0){
printf("Ingrese una cantidad valida");
}
else{
if(*cantidad_retirar > *valor_o){
printf("\nNo tienes esa cantidad de dinero");
}
else{
retir.varia = *valor_o - *cantidad_retirar;
printf("\nSe han retirado %u y el saldo final es de: %u", *cantidad_retirar, retir.varia);
}
}
}

void depositar(int *depositar_cantidad, int *valor_o){
struct cajero deposite;
if(*depositar_cantidad <= 0){
printf("Ingrese una cantidad valida");
}
else{
deposite.deposito = *depositar_cantidad + *valor_o;
printf("\nSe ha depositado satisfactoriamente");
printf("\nAhora tienes en tu cuenta %u", deposite.deposito);
}
}

void salir_x(void){
printf("\nAviso!: Esta saliendo del programa");
}

int main(void){
struct cajero opcion;
opcion.cantidad = 1000;
printf("Bienvenido a cajero automatico 2!!!, tienes 1000 varos");
printf("\n\nOpcion 1: Retirar");
printf("\nOpcion 2: Depositar");
printf("\nOpcion 3: Salir");
printf("\n\n\nEliga una opcion: ");
scanf("%u", &opcion.numero);
switch(opcion.numero){
case 1:
opcion.pcantidad = &opcion.cantidad;
printf("\n\nIngrese la cantidad a retirar: ");
scanf("%u", &opcion.varia);
retirar(&opcion.varia, opcion.pcantidad);
getch();
break;

case 2:
opcion.pcantidad = &opcion.cantidad;
printf("\n\nIngrese la cantidad a depositar: ");
scanf("%u", &opcion.varia);
depositar(&opcion.varia, opcion.pcantidad);
getch();
break;

case 3:
salir_x();
getch();
return 0;

default:
printf("Ejecute algo correcto");
getch();
return -1;
}
return 0;
}


Nota: si se usan los punteros es para evitar usar mucha memoria porque creeme que muchas veces en programacion vas a tener que usar el mismo valor de una variable dentro de otras funciones pero cuando son proyectos de muchas lineas de codigo puede llenar mucha memoria y con lo punteros se evita todo ese gasto de energia utilizando solamente referencias a en que parte del programa esta la variable y por el momento es todo de mi parte, zy te quedaron dudas te dejo este link de este profe:

https://www.youtube.com/watch?v=5TS1tdBcIoM&list=PLcYO0Mqfc4g87Y-XcZ0TWwrSiKlNXo0kh&index=9