C

Tutorial C: Punteros

C: Conoce los fundamentos de direcciones de memoria y variables puntero para optimizar recursos y mejorar la eficiencia en programación.

Aprende C GRATIS y certifícate

Fundamentos de direcciones de memoria y variables puntero

Las direcciones de memoria identifican de forma única la ubicación de cada valor en un programa. Cada variable se almacena en un espacio de memoria concreto, y esa dirección se puede representar y gestionar dentro del propio código. En C, esta gestión cuidadosa de ubicaciones permite optimizar recursos y construir estructuras de datos con gran flexibilidad.

Un puntero es una variable que guarda la dirección de memoria en la que reside otra variable. Al trabajar con punteros, se debe tener presente el tipo de dato al que apuntan, pues cada tipo puede ocupar distinto espacio en memoria. Este mecanismo facilita la creación de estructuras dinámicas y la implementación de comportamientos que requieren manipular direcciones de forma controlada.

En un programa C, declarar una variable puntero consiste en especificar el tipo al que hace referencia y añadir un asterisco en la definición. Un ejemplo elemental es el siguiente:

#include <stdio.h>

int main() {
    int *punteroAEntero;
    double *punteroADouble;

    /* 
       Estas variables puntero se han declarado pero 
       aún no se ha asignado ninguna dirección concreta.
    */

    return 0;
}

Entre los usos más habituales de una variable puntero figuran:

  • Pasar información entre funciones sin necesidad de copiar grandes porciones de datos.
  • Manipular arreglos y estructuras con un mayor control sobre el acceso a cada posición.
  • Reservar memoria en tiempo de ejecución para estructuras cuyo tamaño no se conoce de antemano.

Estos conceptos proporcionan la base para comprender otras características de C relacionadas con la gestión de direcciones y con la interacción directa con la memoria.

Operadores & (dirección) y * (contenido)

El operador & se emplea para obtener la dirección de una variable y resulta esencial cuando se desea manipular apuntadores. Por ejemplo, al pasar parámetros a ciertas funciones que necesitan modificar una variable, se puede utilizar & para transmitir su dirección en lugar de su valor. Esto permite un mayor control sobre los cambios que ocurren en la memoria.

Cuando se dispone de un puntero, el operador * (denominado operador de contenido o de indirección) permite acceder al valor apuntado. Es frecuente ver este operador al asignar o leer datos a través de un puntero, lo que posibilita separar la ubicación en memoria del contenido que reside ahí. Dicho acceso controlado habilita cambios precisos en determinados lugares de la memoria sin necesidad de copiar toda la información.

Algunos usos habituales de & y * son:

  • Conseguir la dirección de variables y almacenarlas en punteros para procesarlas en funciones.
  • Modificar valores apuntados por un puntero para aplicar cambios directos en la memoria.
  • Pasar apuntadores a funciones donde se requiera actualizar variables originales.

A continuación se muestra un ejemplo conciso que ilustra la relación entre & y *:

#include <stdio.h>

int main() {
    int valor = 10;
    int *puntero = &valor;  /* Se obtiene la dirección de 'valor' */

    printf("Contenido original: %d\n", *puntero);  /* Se accede al contenido */
    *puntero = 20;  /* Se modifica el valor apuntado */
    printf("Contenido modificado: %d\n", valor);

    return 0;
}

En este fragmento, la variable valor se asocia a puntero utilizando el operador &, mientras que el operador * permite leer y cambiar el contenido en la ubicación de memoria referenciada. Con estas operaciones, se facilita un manejo más flexible de los datos y se evitan copias innecesarias de información.

Punteros y arrays (relación e indexación)

Los arrays en C se almacenan en posiciones de memoria contiguas, lo que posibilita tratarlos de forma similar a un puntero que apunta al primer elemento. Aunque el nombre de un array puede usarse en expresiones como si fuera un puntero, su dirección base no es modificable directamente. Por otro lado, una variable puntero sí puede apuntar a distintos lugares, lo que brinda más flexibilidad al manejar secuencias de datos.

Para acceder a los elementos de un array mediante un puntero, suele aprovecharse la correspondencia entre el operador de subíndice y la aritmética de direcciones. La expresión array[i] es equivalente a *(array + i), donde cada incremento sobre el nombre del array (o sobre un puntero) avanza tantas celdas como el tamaño del tipo base. Con esta equivalencia, se pueden realizar recorridos e iteraciones sobre estructuras de datos con un control más detallado sobre los desplazamientos en memoria.

A continuación, se observa un ejemplo que ilustra la relación entre punteros y arrays, junto con la indexación basada en desplazamientos:

#include <stdio.h>

int main() {
    int valores[] = {1, 2, 3, 4};
    int *puntero = valores;    

    for (int i = 0; i < 4; i++) {
        printf("Elemento %d: %d\n", i, *(puntero + i));
    }

    return 0;
}

En este fragmento, se asigna la dirección base de valores a puntero para posteriormente imprimir cada elemento mediante aritmética de punteros. La suma puntero + i proporciona la ubicación del i-ésimo elemento, lo que ejemplifica la similitud conceptual entre usar valores[i] y *(valores + i). No obstante, siempre conviene administrar los límites de un array con cautela para evitar accesos fuera de rango.

Punteros a funciones

Los punteros a funciones permiten referenciar y llamar a una función a través de una variable que contiene su dirección. Esta capacidad introduce flexibilidad al permitir elegir dinámicamente qué función se invocará en tiempo de ejecución. Un ejemplo habitual es definir distintas operaciones que comparten la misma firma y conmutar entre ellas según sea necesario.

Para declarar un puntero a función, se especifica el tipo de retorno y los tipos de parámetros, incluyendo un nombre entre paréntesis precedido de asterisco. Por ejemplo, si se requiere apuntar a una función que reciba dos valores enteros y devuelva un entero, la declaración puede hacerse de la siguiente manera:

int (*operacion)(int, int);

Una vez declarada la variable, es sencillo asignarle una función compatible. En el siguiente fragmento, se define una función suma y otra función resta, para posteriormente almacenar la dirección de cada una en la variable puntero. El asterisco antes del nombre de la variable al momento de llamar indica que se está invocando la función apuntada:

#include <stdio.h>

int sumar(int a, int b) {
    return a + b;
}

int restar(int a, int b) {
    return a - b;
}

int main() {
    int (*operacion)(int, int);

    operacion = sumar;
    printf("Resultado sumar: %d\n", (*operacion)(5, 3));

    operacion = restar;
    printf("Resultado restar: %d\n", (*operacion)(5, 3));

    return 0;
}

En este ejemplo, la variable puntero se reasigna antes de cada llamada, lo que concentra la lógica de selección en un único lugar. Con dicha mecánica, se pueden implementar estructuras de decisión más compactas y extensibles, ya que basta con incluir nuevas funciones que sigan la misma firma.

En muchas bibliotecas se emplean punteros a funciones para recibir como argumento la rutina encargada de procesar datos. Estas rutinas se conocen a veces como funciones de callback y ofrecen un nivel de abstracción que permite cambiar la forma de procesar la información sin alterar el código principal. Este enfoque es útil en situaciones como el filtrado de datos o la clasificación en un orden específico.

Al definir varios punteros que compartan firma, suele ser buena práctica agruparlos en tablas o arrays de punteros a funciones para simplificar el acceso indexado a comportamientos discretos. Este recurso hace viable crear menús de selección de funciones, donde cada posición del array apunta a una acción distinta.

La asignación, llamada y reubicación de punteros a funciones pueden proporcionar gran adaptabilidad al código, siempre que se administren correctamente los tipos implicados y se garanticen correspondencias exactas entre declaración del puntero y definición de la función. Esto evita errores en tiempo de ejecución y confusiones al compilar.

Aprende C GRATIS online

Todas las lecciones de C

Accede a todas las lecciones de C y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Css

CSS3

Introducción Y Entorno

Sintaxis

CSS3

Sintaxis De Selectores Y Propiedades

Selectores Básicos

CSS3

Sintaxis De Selectores Y Propiedades

Herencia Y Cascada

CSS3

Sintaxis De Selectores Y Propiedades

Pseudo-clases Y Pseudo-elementos

CSS3

Sintaxis De Selectores Y Propiedades

Estilos De Fuente

CSS3

Estilización De Texto Y Fondo

Propiedades De Texto

CSS3

Estilización De Texto Y Fondo

Sombras En Texto Y Cajas

CSS3

Estilización De Texto Y Fondo

Propiedades De Fondo

CSS3

Estilización De Texto Y Fondo

Modelo De Caja

CSS3

Modelo Caja Y Posicionamiento

Propiedades De Posicionamiento

CSS3

Modelo Caja Y Posicionamiento

Propiedad 'Display'

CSS3

Modelo Caja Y Posicionamiento

Elementos 'Float' Y 'Clear'

CSS3

Modelo Caja Y Posicionamiento

Flexbox Para Crear Layouts Y Estructuras

CSS3

Flexbox Y Grid

Css Grid Para Crear Layouts Y Estructuras

CSS3

Flexbox Y Grid

Animaciones Y Transiciones

CSS3

Técnicas Modernas Y Metodologías

Variables En Css

CSS3

Técnicas Modernas Y Metodologías

Diseño Responsive Con Media Queries

CSS3

Técnicas Modernas Y Metodologías

Metodologías De Escritura En Css

CSS3

Técnicas Modernas Y Metodologías

Introducción A Javascript

JavaScript

Sintaxis

Tipos De Datos

JavaScript

Sintaxis

Variables

JavaScript

Sintaxis

Operadores

JavaScript

Sintaxis

Estructuras De Control

JavaScript

Sintaxis

Funciones

JavaScript

Sintaxis

Funciones Cierre (Closure)

JavaScript

Sintaxis

Funciones Flecha

JavaScript

Programación Funcional

Filtrado Con Filter() Y Find()

JavaScript

Programación Funcional

Transformación Con Map()

JavaScript

Programación Funcional

Reducción Con Reduce()

JavaScript

Programación Funcional

Clases Y Objetos

JavaScript

Programación Orientada A Objetos

Excepciones

JavaScript

Programación Orientada A Objetos

Encapsulación

JavaScript

Programación Orientada A Objetos

Herencia

JavaScript

Programación Orientada A Objetos

Polimorfismo

JavaScript

Programación Orientada A Objetos

Array

JavaScript

Estructuras De Datos

Conjuntos Con Set

JavaScript

Estructuras De Datos

Mapas Con Map

JavaScript

Estructuras De Datos

Manipulación Dom

JavaScript

Dom

Selección De Elementos Dom

JavaScript

Dom

Modificación De Elementos Dom

JavaScript

Dom

Eventos Del Dom

JavaScript

Dom

Callbacks

JavaScript

Programación Asíncrona

Promises

JavaScript

Programación Asíncrona

Async / Await

JavaScript

Programación Asíncrona

Introducción A Typescript

TypeScript

Introducción Y Entorno

Variables Y Constantes

TypeScript

Sintaxis

Operadores

TypeScript

Sintaxis

Control De Flujo

TypeScript

Sintaxis

Funciones

TypeScript

Sintaxis

Funciones Flecha

TypeScript

Sintaxis

Clases Y Objetos

TypeScript

Programación Orientada A Objetos

Interfaces

TypeScript

Programación Orientada A Objetos

Encapsulación

TypeScript

Programación Orientada A Objetos

Herencia

TypeScript

Programación Orientada A Objetos

Polimorfismo

TypeScript

Programación Orientada A Objetos

Inmutabilidad

TypeScript

Programación Funcional

Funciones Puras

TypeScript

Programación Funcional

Funciones De Primera Clase

TypeScript

Programación Funcional

Funciones De Alto Orden

TypeScript

Programación Funcional

Tipos Literales

TypeScript

Tipos De Datos Avanzados

Tipos Genéricos

TypeScript

Tipos De Datos Avanzados

Tipos De Unión E Intersección

TypeScript

Tipos De Datos Avanzados

Tipos De Utilidad

TypeScript

Tipos De Datos Avanzados

Módulos

TypeScript

Namespaces Y Módulos

Namespaces

TypeScript

Namespaces Y Módulos

Resolución De Módulos

TypeScript

Namespaces Y Módulos

Introducción A C#

Sintaxis

Creación De Proyecto C#

Sintaxis

Variables Y Constantes

Sintaxis

Tipos De Datos

Sintaxis

Operadores

Sintaxis

Control De Flujo

Sintaxis

Funciones

Sintaxis

Clases Y Encapsulación

Programación Orientada A Objetos

Objetos

Programación Orientada A Objetos

Constructores Y Destructores

Programación Orientada A Objetos

Herencia

Programación Orientada A Objetos

Polimorfismo

Programación Orientada A Objetos

Excepciones

Excepciones

Arrays Y Listas

Colecciones Y Linq

Diccionarios

Colecciones Y Linq

Conjuntos, Colas Y Pilas

Colecciones Y Linq

Uso De Consultas Linq

Colecciones Y Linq

Delegados

Programación Asíncrona

Eventos

Programación Asíncrona

Lambdas

Programación Asíncrona

Uso De Async Y Await

Programación Asíncrona

Tareas

Programación Asíncrona

Introducción A C

Introducción Y Entorno

Primer Programa En C

Introducción Y Entorno

Estructura Básica De Un Programa En C

Sintaxis

Operadores Y Expresiones

Sintaxis

Control De Flujo

Sintaxis

Arrays Y Manejo De Cadenas

Sintaxis

Funciones

Funciones Y Punteros

Punteros

Funciones Y Punteros

Gestión De Memoria Dinámica

Funciones Y Punteros

Estructuras En C

Estructuras, Uniones Y Tipos

Uniones Y Enumeraciones

Estructuras, Uniones Y Tipos

Introducción A Tailwind Css

Tailwind CSS

Introducción Y Entorno

Instalación De Tailwind Css

Tailwind CSS

Introducción Y Entorno

Fundamentos Del Sistema De Utility-first

Tailwind CSS

Fundamentos

Fundamentos Del Diseño Responsive

Tailwind CSS

Fundamentos

Tipografía Y Fuentes En Tailwind Css

Tailwind CSS

Clases De Utilidad

Clases De Tamaño De Tailwind Css

Tailwind CSS

Clases De Utilidad

Utilidades De Espaciado Y Alineación De Tailwind Css

Tailwind CSS

Clases De Utilidad

Clases De Colores Y Fondo De Tailwind Css

Tailwind CSS

Clases De Utilidad

Clases De Bordes De Tailwind Css

Tailwind CSS

Clases De Utilidad

Hover, Focus Y Estado De Tailwind Css

Tailwind CSS

Clases De Utilidad

Transiciones Y Animaciones De Tailwind Css

Tailwind CSS

Clases De Utilidad

Contenedores Y Columnas En Tailwind Css

Tailwind CSS

Layout

Flexbox En Tailwind Css

Tailwind CSS

Layout

Grid En Tailwind Css

Tailwind CSS

Layout

Evaluación Test Tailwind Css

Tailwind CSS

Evaluación

Evaluación Código Tailwind Css

Tailwind CSS

Evaluación

Introducción A React Y Su Ecosistema

React

Introducción Y Entorno

Instalar React Y Crear Nuevo Proyecto

React

Introducción Y Entorno

Introducción A Jsx

React

Componentes

Introducción A Componentes

React

Componentes

Componentes Funcionales

React

Componentes

Eventos En React

React

Componentes

Props Y Manejo De Datos Entre Componentes

React

Componentes

Renderizado Condicional

React

Componentes

Renderizado Iterativo Con Bucles

React

Componentes

Manejo De Clases Y Estilos

React

Componentes

Introducción A Los Hooks

React

Hooks

Estado Y Ciclo De Vida De Los Componentes

React

Hooks

Hooks Estado Y Efectos Secundarios

React

Hooks

Hooks Para Gestión De Estado Complejo Y Contexto

React

Hooks

Hooks Optimización Y Concurrencia

React

Hooks

Introducción A React Router

React

Navegación Y Enrutamiento

Definición Y Manejo De Rutas

React

Navegación Y Enrutamiento

Rutas Anidadas Y Rutas Dinámicas

React

Navegación Y Enrutamiento

Navegación Programática Redirección

React

Navegación Y Enrutamiento

Nuevos Métodos Create De React Router

React

Navegación Y Enrutamiento

Solicitudes Http Con Fetch Api

React

Interacción Http Con Backend

Solicitudes Http Con Axios

React

Interacción Http Con Backend

Estado Local Con Usestate Y Usereducer

React

Servicios Y Gestión De Estado

Estado Global Con Context Api

React

Servicios Y Gestión De Estado

Estado Global Con Redux Toolkit

React

Servicios Y Gestión De Estado

Custom Hooks Para Servicios Compartidos

React

Servicios Y Gestión De Estado

Evaluación Test React

React

Evaluación

Aprendizaje Automático

scikit-learn

Introducción Y Entorno

Introducción E Instalación

scikit-learn

Introducción Y Entorno

Introducción Al Preprocesamiento De Datos

scikit-learn

Preprocesamiento De Datos

Identificación Y Tratamiento De Valores Faltantes

scikit-learn

Preprocesamiento De Datos

Escalado De Datos

scikit-learn

Preprocesamiento De Datos

Normalización De Datos

scikit-learn

Preprocesamiento De Datos

Codificación De Variables Categóricas

scikit-learn

Preprocesamiento De Datos

Ingeniería De Características

scikit-learn

Preprocesamiento De Datos

Selección De Características

scikit-learn

Preprocesamiento De Datos

Extracción De Características

scikit-learn

Preprocesamiento De Datos

Particionamiento De Datos

scikit-learn

Preprocesamiento De Datos

Preprocesamiento De Datos Desbalanceados

scikit-learn

Preprocesamiento De Datos

Introducción A La Regresión

scikit-learn

Regresión

Regresión Lineal

scikit-learn

Regresión

Regresión Knn Kneighborsregressor

scikit-learn

Regresión

Regresión Svm Con Svr

scikit-learn

Regresión

Regresión Con Árboles Decisiontreeregressor

scikit-learn

Regresión

Regresión Con Algoritmos De Conjunto

scikit-learn

Regresión

Introducción A La Clasificación

scikit-learn

Clasificación

Clasificación Con Regresión Logística

scikit-learn

Clasificación

Clasificación Knn Kneighborsclassifier

scikit-learn

Clasificación

Clasificación Svm Con Svc

scikit-learn

Clasificación

Clasificación Con Árboles Decisiontreeclassifier

scikit-learn

Clasificación

Clasificación Con Algoritmos De Conjunto

scikit-learn

Clasificación

Reducción De La Dimensionalidad Con Pca

scikit-learn

Aprendizaje No Supervisado

Clustering Con Kmeans

scikit-learn

Aprendizaje No Supervisado

Clustering Jerárquico

scikit-learn

Aprendizaje No Supervisado

Clustering De Densidad Con Dbscan

scikit-learn

Aprendizaje No Supervisado

Preprocesamiento De Textos Para Nlp

scikit-learn

Nlp

Representación De Texto Y Extracción De Características

scikit-learn

Nlp

Clasificación De Texto Con Scikit Learn

scikit-learn

Nlp

Análisis De Sentimiento

scikit-learn

Nlp

Técnicas Avanzadas De Extracción De Características

scikit-learn

Nlp

Introducción Al Análisis De Series Temporales

scikit-learn

Series Temporales

Preprocesamiento De Datos De Series Temporales

scikit-learn

Series Temporales

Ingeniería De Características Para Series Temporales

scikit-learn

Series Temporales

Transformación Y Escalado De Series Temporales

scikit-learn

Series Temporales

Validación Y Evaluación De Modelos En Series Temporales

scikit-learn

Series Temporales

Validación Y Evaluación De Modelos

scikit-learn

Validación De Modelos

Técnicas De Validación Cruzada

scikit-learn

Validación De Modelos

Métricas De Regresión

scikit-learn

Validación De Modelos

Métricas De Clasificación

scikit-learn

Validación De Modelos

Ajuste De Hiperparámetros

scikit-learn

Validación De Modelos

Introducción A Pipelines

scikit-learn

Pipelines Y Despliegue

Creación De Pipelines Básicos

scikit-learn

Pipelines Y Despliegue

Preprocesamiento De Datos Con Pipelines

scikit-learn

Pipelines Y Despliegue

Pipelines Y Validación Cruzada

scikit-learn

Pipelines Y Despliegue

Pipelines Con Columntransformer

scikit-learn

Pipelines Y Despliegue

Exportar E Importar Pipelines

scikit-learn

Pipelines Y Despliegue

Introducción E Instalación De Opencv

OpenCV

Introducción Y Entorno

Carga Y Visualización De Imágenes

OpenCV

Manipulación Imágenes

Operaciones Básicas En Imágenes

OpenCV

Manipulación Imágenes

Detección De Bordes Y Contornos

OpenCV

Procesamiento Y Análisis

Histograma Y Ecualización

OpenCV

Procesamiento Y Análisis

Preprocesamiento Para Machine Learning

OpenCV

Aprendizaje Automático

Clasificación De Imágenes Con Ml

OpenCV

Aprendizaje Automático

Introducción A Docker

Docker

Introducción Y Entorno Docker

Instalación De Docker

Docker

Introducción Y Entorno Docker

Descargar Imágenes De Hub.docker.com

Docker

Imágenes Docker

Crear Imágenes Con Dockerfile

Docker

Imágenes Docker

Contenedores Docker

Docker

Contenedores Docker

Volúmenes Docker

Docker

Volúmenes Docker

Redes Docker

Docker

Redes Docker

Creación De Archivos Docker Compose

Docker

Docker Compose

Docker Compose Para Varios Servicios

Docker

Docker Compose

Accede GRATIS a C y certifícate

Certificados de superación de C

Supera todos los ejercicios de programación del curso de C y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.

Objetivos de aprendizaje de esta lección

  • Comprender la gestión de memoria en C a través de direcciones y punteros.
  • Diferenciar el uso de operadores & y * en el manejo de memoria.
  • Relacionar arrays y punteros, optimizando el acceso a datos.
  • Implementar punteros a funciones para mejorar la flexibilidad y modularidad del código.