Un puntero es una variable que almacena la dirección de memoria de otra variable. Esta capacidad permite a los punteros manipular datos en memoria.
En lugar de contener directamente un valor de datos, un puntero contiene la dirección donde se encuentra ese valor. Por tanto, son una de las formas más simples de realizar una referencia.
Los punteros son una característica heredada de C. Son muy odiados y amados (ambas cosas de forma injustificada)
Uno de los objetivos de C++ es reducir o eliminar el uso de punteros. Sin embargo, siguen siendo necesarios.
Definición de un puntero
La sintaxis básica para definir un puntero es:
tipo *nombrePuntero;
Donde:
tipo
es el tipo de dato al que el puntero apunta.nombrePuntero
es el nombre del puntero.
Por ejemplo,
int *ptr;
En este ejemplo, ptr
es un puntero a un entero.
Operador de desreferencia
El operador de desreferencia *
se utiliza para acceder al valor de la variable a la que apunta el puntero.
int var = 10;
int *ptr = &var;
std::cout << "Valor de var: " << var << std::endl;
std::cout << "Valor de var usando puntero: " << *ptr << std::endl;
Asignación de direcciones
Para asignar una dirección de memoria a un puntero, se utiliza el operador de dirección &
.
int var = 10;
int *ptr = &var;
Aquí, ptr
almacena la dirección de var
.
Punteros y arrays
Los arrays y los punteros están estrechamente relacionados en C++. El nombre de un array es en sí mismo un puntero al primer elemento del array.
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;
std::cout << "Primer elemento del array: " << *ptr << std::endl;
std::cout << "Segundo elemento del array: " << *(ptr + 1) << std::endl;
En este ejemplo, ptr
apunta al primer elemento del array arr
.
Punteros y funciones
Los punteros pueden ser utilizados para pasar grandes estructuras de datos a funciones sin necesidad de copiarlas, lo cual es eficiente en términos de memoria y tiempo.
#include <iostream>
void incrementar(int *ptr) {
(*ptr)++;
}
int main() {
int num = 10;
incrementar(&num);
std::cout << "Valor de num después de incrementar: " << num << std::endl;
return 0;
}
En este ejemplo, la función incrementar
modifica directamente el valor de num
a través de un puntero.
Punteros y memoria dinámica
En C++, se puede gestionar la memoria de forma dinámica utilizando los operadores new
y delete
.
Asignación dinámica de memoria
int *ptr = new int;
*ptr = 20;
std::cout << "Valor almacenado en memoria dinámica: " << *ptr << std::endl;
delete ptr;
En este ejemplo, se asigna memoria para un entero dinámicamente y luego se libera.
Arrays dinámicos
int *arr = new int[5];
for (int i = 0; i < 5; ++i) {
arr[i] = i * 2;
}
for (int i = 0; i < 5; ++i) {
std::cout << "arr[" << i << "] = " << arr[i] << std::endl;
}
delete[] arr;
Aquí, se asigna y libera memoria para un array dinámico.
Punteros a punteros
Un puntero a puntero es una variable que contiene la dirección de un puntero, permitiendo niveles adicionales de indireccionamiento.
int var = 5;
int *ptr = &var;
int **pptr = &ptr;
std::cout << "Valor de var: " << var << std::endl;
std::cout << "Valor de var usando ptr: " << *ptr << std::endl;
std::cout << "Valor de var usando pptr: " << **pptr << std::endl;
Buenas prácticas
Inicializar punteros
Siempre inicializar punteros.
int *ptr = nullptr;
Utilizar punteros que no han sido inicializados puede causar comportamientos indefinidos.
Liberar memoria
Asegurarse de liberar cualquier memoria dinámica asignada.
int *ptr = new int;
delete ptr;
No liberar memoria asignada dinámicamente puede resultar en fugas de memoria.
Uso de nullptr
Usar nullptr
en lugar de NULL
o 0
para punteros nulos.
int *ptr = nullptr;
Ejemplo completo
Vamos a ver un ejemplo algo más amplio, donde veríamos una gestión dinámica de una lista de enteros.
#include <iostream>
class ListaEnteros {
private:
int *datos;
int capacidad;
int tamaño;
public:
ListaEnteros(int cap) : capacidad(cap), tamaño(0) {
datos = new int[capacidad];
}
~ListaEnteros() {
delete[] datos;
}
void agregar(int valor) {
if (tamaño < capacidad) {
datos[tamaño++] = valor;
} else {
std::cerr << "Capacidad de la lista excedida" << std::endl;
}
}
void mostrar() const {
for (int i = 0; i < tamaño; ++i) {
std::cout << datos[i] << " ";
}
std::cout << std::endl;
}
};
int main() {
ListaEnteros lista(10);
lista.agregar(1);
lista.agregar(2);
lista.agregar(3);
std::cout << "Elementos en la lista: ";
lista.mostrar();
return 0;
}
En este ejemplo, se crea una clase ListaEnteros
que gestiona una lista de enteros de forma dinámica, demostrando la creación, uso y liberación de memoria dinámica.
No significa que sea la mejor forma de resolver el problema. Es un ejemplo para ilustrar el uso de punteros.