Aritmética de Punteros en C

Author
Por Darío Rivera
Publicado el en Lenguaje C

La artimetica de punteros es la capacidad de realizar operaciones aritmeticas sobre los punteros en C. Gran parte del potencial de C se centra en la manipulacion de punteros y en como se pueden operar. En este artículo verás todo lo relacionado con artimética de punteros.

Asignación de memoria

Como vimos en Tipos de Datos en C, cada tipo de dato ocupa un espacio en la memoria de la computadora dependiendo del tipo de máquina en la que se ejecute. Si queremos por ejemplo saber el tamaño que ocupa un entero podemos ejecutar la siguiente instruccion.

printf("Size of integer is %lu\n", sizeof(int));

La salida será similar a la siguiente.

Size of integer is 4

Lo que quiere decir que en la máquina específica a un entero se le asignan 4 bytes de memoria. Además, tal y como mencionamos en nuestro articulo Arrays en C, el nombre de array es una constante que representa la dirección del primer elemento del array. Esto lo podemos comprobar en el siguiente ejemplo. Observa como utilizamos el operador de desreferencia para acceder al contenido de la variable hacia el cual referencia el puntero (También puedes revisar nuestro artículo Punteros en C si aún no sabes que es un puntero).

#include <stdio.h>

int main()
{
    int arr[] = { 10, 20, 30, 40, 50, 60 };

    int* ptr = arr;
    printf("Value of first element is %d\n", *ptr);

    return 0;
}

Como es de esperarse obtendrás la siguiente salida.

Value of first element is 10

Recuerda que también mencionamos nuestro articulo Arrays en C, que todos los elementos de un array se almacenan de forma contigua en la computadora. Esto lo podemos ver mas de facto en el siguiente programa.

#include <stdio.h>

int main()
{
    int arr[5], i;
  
    for (i = 0; i < 5; i++){
        printf("Address arr[%d] is %p\n", i, &arr[i]);
    }

    return 0;
}

La salida será similar a la siguiente.

Address arr[0] is 0x7ffd65570a10
Address arr[1] is 0x7ffd65570a14
Address arr[2] is 0x7ffd65570a18
Address arr[3] is 0x7ffd65570a1c
Address arr[4] is 0x7ffd65570a20

Nota que ni siquiera es necesario inicializar los valores del array, puesto que el compilador asigna el espacio en memoria. Sin embargo, los valores asignados al array son totalmente aleatorios, para evitar esto, es recomendable inicializar al menos el primer elemento. El compilador rellenará los demás con ceros.

int arr[5] = {0}, i;

En el siguiente diagrama podemos ver los valores del array, la dirección de memoria en donde reside cada elemento y la forma de acceder a cada uno con el nombre del array.



Incremento y decremento de punteros

En C se pueden utilizar los operadores de incremento y/o decremento en punteros. Veamos el siguiente ejemplo de como tradicionamente recorreríamos un array para solicitar letras e imprimirlas.

#include <stdio.h>
#define MAXLETRAS 5

int main()
{
    char array_char[MAXLETRAS];
    int i;

    for (i = 0; i < MAXLETRAS; i++) {
        array_char[i] = getchar();
    }

    for (i = 0; i < MAXLETRAS; i++) {
        putchar(array_char[i]);
    }

    printf("\n");
    return 0;
}

Al ejecutar este programa se abrirá un ciclo en el cual se solicitá ingresar al usuario caracteres mediante la función getchar(). Esta función terminará su ejecución justo después del caractér de salto de línea, por lo cual en teoría podrías escribir más de cinco caracteres pero solamente serán leídos los primeros cinco. Después, se imprimen los caracteres con otro ciclo. A continuación tienes un ejemplo de la ejecución de este programa.

user@server$ ./letras
HOLAMUNDO
HOLAM

La teoría básica no permite entender el anterior ejemplo a la perfección. Veamos ahora que pasa cuando utilizamos aritmética de punteros.

#include <stdio.h>
#define MAXLETRAS 5

int main()
{
    char array_char[MAXLETRAS];
    char *apun_char = array_char;
    int i;

    for (i = 0; i < MAXLETRAS; i++) {
        *apun_char = getchar();
        apun_char++;
    }

    apun_char = array_char;

    for (i = 0; i < MAXLETRAS; i++) {
        putchar(*apun_char);
        apun_char++;
    }

    printf("\n");
    return 0;
}

Este último ejemplo es equivalente al anterior, sin embargo utiliza una lógica distinta proveniente de punteros. Vamos paso a paso,

char *apun_char = array_char;

En la anterior instrucción se crea una variable de tipo puntero llamada apun_char. A esta variable se le asigna el nombre del array array_char, lo que significa que se asigna la dirección de memoria del primer elemento del array. La siguiente instrucción es equivalente.

char *apun_char = &array_char[0];

En el primer ciclo for, utilizamos esta instrucción para desreferenciar el puntero y poder asignar el valor al elemento del array.

*apun_char = getchar();

Esta instrucción, en la primera iteración del ciclo es equivalente a la siguiente:

array_char[0] = getchar();

La siguiente instrucción del ciclo utiliza el operador de incremento con la variable puntero.

apun_char++;

Esto por definición es equivalente a ejecutar

apun_char += 1;

Sin embargo, esto no significa de manera explícita que se suma un entero a la variable apun_char (la cual es una dirección de memoria). Este constructor en C se aplica de manera diferente dependiendo el tamaño de la variable, es decir, si char ocupara 1 byte, en efecto se sumaría uno. Supondiendo q la primera dirección es 7ffd65570a10, así sería el cálculo

7ffd65570a10 + 1 = 7FFD65570A11

Sin embargo, si el tipo char ocupara 4 bytes, el incremente sería el siguiente

7ffd65570a10 + 4 = 7FFD65570A14

El compilador de C se ocupa de realizar este tipo de cálculos sobre los punteros, por lo cual, el incremento se puede ver como el desplazamiento a la siguiente dirección de memoria en donde reside el posterior elemento del array independientemente de su tamaño.

Suma y resta de punteros

En realidad en C solo se pueden realizar las operaciones aritméticas de suma y resta. El incremento y decremento son en esencia suma y resta de una posición como vimos anteriormente. Veamos ahora como podemos sumar y restar otros valores diferentes a uno.

#include <stdio.h>

int main()
{

    int array_int[5] = {10, 20, 30, 40, 50};
    int *apun_int = &array_int[2];

    apun_int = apun_int - 2;

    printf("Primero: %d\n", *apun_int);

    apun_int += 2;

    printf("Tercero: %d\n", *apun_int);

    printf("Quinto : %d\n", *(apun_int + 2));

    return 0;
}

En el ejemplo anterior se asigna en primera instancia la dirección del tercer elemento del array al puntero apun_int. Seguidamente se restan 2 posiciones.

apun_int = apun_int - 2;

Ya hemos visto que el compilador de C automáticamente calcula el tamaño del tipo de dato y le resta 2 veces este valor. Es decir, que matemáticamente esta operación es en realidad la siguiente para un tamaño de entero de 4 bytes, y una dirección de memoria para el tercer elemento de 7ffe8bd9b3c8.

7ffe8bd9b3c8 - 8 = 7FFE8BD9B3C0

Finalmente observa que C también permite realizar la resta del puntero y después desreferenciar este resultado.

*(apun_int + 2)

Eso es debido a que siempre que estemos sumando o restando números a un puntero, en realidad estamos moviendo posiciones. El compilador de C intermante realizará las operaciones indicadas anteriormente.


Acerca de Darío Rivera

Author

Application Architect at Elentra Corp . Quality developer and passionate learner with 10+ years of experience in web technologies. Creator of EasyHttp , an standard way to consume HTTP Clients.

LinkedIn Twitter Instagram

Sólo aquellos que han alcanzado el éxito saben que siempre estuvo a un paso del momento en que pensaron renunciar.