¿Cómo se usan los punteros en C++?

Los punteros son una característica fundamental en C++, pero pueden resultar confusos para los principiantes. En este artículo, explicaremos las particularidades de los punteros en C++ de manera clara y concisa.

¿Qué es un puntero?

Un puntero no es más que una variable que almacena la dirección de memoria de otra variable. Esto significa que, en lugar de almacenar el valor de una variable directamente, un puntero almacena la dirección de memoria donde se encuentra esa variable.

Cómo se usan los punteros en C++

Para declarar un puntero en C++, se utiliza el símbolo de asterisco (*). Por ejemplo, la declaración «int *p» declara un puntero a un entero llamado «p». Una vez que se ha declarado un puntero, se puede asignar a él la dirección de memoria de una variable utilizando el operador de dirección «&». Por ejemplo, «p = &x» asigna a «p» la dirección de memoria de la variable «x».

Diagrama punteros

En el diagrama de ejemplo anterior hemos creado dos punteros que apuntan a variables de tipo entero. Estos punteros p1 y p2 solamente señalan a la dirección de memoria que contiene a las variables 1 y 2. En este caso los punteros contienen los valores de memoria 0x01 y 0x05.

Particularidades

Una de las particularidades de los punteros en C++ es que se pueden utilizar para acceder al valor de la variable a la que apuntan mediante el operador de indirección «*». Por ejemplo, «cout << *p» imprimirá el valor de la variable a la que apunta el puntero «p».

Un uso habitual de los punteros en C++ es para crear y manipular estructuras de datos dinámicas, como matrices y listas enlazadas. Esto se logra utilizando los operadores «new» y «delete» para asignar y liberar memoria dinámicamente.

Peligros

Es importante tener en cuenta que los punteros en C++ pueden ser peligrosos si se usan incorrectamente. Es fácil cometer errores como acceder a una dirección de memoria no válida o liberar la memoria dos veces. Para evitar estos errores, se recomienda utilizar técnicas de programación defensiva, como inicializar los punteros a null y comprobar que no son null antes de utilizarlos.

Conclusión

En resumen, los punteros son una característica importante en C++, que permiten acceder a la memoria de manera eficiente y crear estructuras de datos dinámicas. Sin embargo, es fundamental tener cuidado al utilizarlos para evitar errores peligrosos.

Archivos de cabecera

También conocidos como header files. Como ya vimos en lecciones anteriores, podemos incluir todo tipo de librerías adicionales en C++, y podemos crear las nuestras e incluirlas también.

Generalmente los archivos de cabecera tienen la extensión .hpp, pero puedes utilizar la extensión que prefieras. Lo más importante acerca de los archivos de cabecera es que tienes que saber que son usados para definir información sobre como ejecutar una tarea, a diferencia del programa principal, que define lo que se va a ejecutar.

Veamos como un archivo de cabecera funciona con un sencillo programa ‘Hola, mundo!’

#include <iostream>

using namespace std;

int main(){
  cout << "Hola, estamos usando archivos de cabecera!";
  return 0
}

Si quisiéramos usar un archivo de cabecera para este propósito podríamos mover todo el código que no estuviera estrictamente relacionado con la tarea, de forma que podríamos generar un archivo main.hpp con el siguiente contenido:

#include <iostream>

using namespace std;

Y modificaríamos nuestro archivo principal (el que contiene la funcion main) para que quedara de la siguiente manera:

#include "main.hpp"

int main(){
  cout << "Hola, estamos usando archivos de cabecera!";
  return 0
}

Fíjate en las comillas dobles para incluir el archivo. De esta forma indicamos que se encuentra en el directorio actual.

Constantes

Hay un tipo de variable en C++ que podemos usar si queremos forzar que su valor no cambie a lo largo de la ejecución del programa, las constantes.

Hay dos formas de declarar constantes en C++:

  • Mediante la palabra clave const.
  • Utilizando el preprocesador mediante #define

Definición de constantes mediante const

El formato de esta declaración es el siguiente:

const type variable = value;

Por ejemplo:

const int pokemonCount = 898;	

Tratar de cambiar este tipo de variables nos mostrará un error. Compruébalo por tu cuenta:

#include <iostream>
using namespace std;

int main()
{
    const int pokemonCount = 898;
    cout<<"Número de pokemon = "<<pokemonCount<<"\n";
    pokemonCount = 1000;
    cout<<"Número de pokemon = "<<pokemonCount<<"\n";
    return 0;
}

Definición de constantes en la cabecera

El formato de la declaración es el siguiente:

#define identifier value

Por ejemplo:

#include <iostream>
using namespace std;

#define POKEMON_COUNT 898

int main()
{
    cout<<"Número de pokemon = "<<POKEMON_COUNT<<"\n";
    POKEMON_COUNT = 1000;
    cout<<"Número de pokemon = "<<POKEMON_COUNT<<"\n";
    return 0;
}

De nuevo nos encontraremos con un error al tratar de ejecutar este programa.

Constantes enumeradas

Hay un tipo de constante especialmente útil, las constantes enumeradas o enumerated constants. Se usan para declarar un tipo de variable y asignarle así un número finito de valores. Fíjate en este ejemplo:

enum pokemonTipo {
	PLANTA,
  	FUEGO,
    AGUA,
    LUCHA,
    BICHO,
    ELECTRICO
};

Los valores son automáticamente traducidos a enteros de tal forma que:

PLANTA = 0
FUEGO = 1
...

Fíjate en el programa de ejemplo:

#include <iostream>

using namespace std;

int main()
{
    enum POKEMON_TIPO {
    PLANTA,
  	FUEGO,
    AGUA,
    LUCHA,
    BICHO,
    ELECTRICO
      };
  
  	// definimos mejorTipo como una variable de tipo POKEMON_TIPOS
    POKEMON_TIPO mejorTipo;
    
    // hacemos una asignación
    mejorTipo = ELECTRICO;
    
    // y comprobamos como haríamos con cualquier otra variable
    if(mejorTipo == ELECTRICO)
    {
        cout<<"No me parece que ese sea el mejor tipo de pokemon\n";
    }
    return 0;
}