Pointers y Arrays
Cuando un array se usa en la mayoría de las expresiones, 'decae' en un pointer a su primer elemento — por eso int *p = numbers funciona sin necesitar &. Una vez que p apunta al inicio del array, p + 1 no significa 'suma 1 a la dirección' — significa 'avanza un int', con el compilador escalando automáticamente por sizeof(int).
#include <stdio.h>
int main() {
int numbers[3] = {10, 20, 30};
int *p = numbers;
printf("%d %d %d\n", *p, *(p + 1), *(p + 2));
printf("%d %d %d\n", p[0], p[1], p[2]);
return 0;
}10 20 30 10 20 30
numbers[i] se define literalmente como *(numbers + i) — la indexación con corchetes es aritmética de pointers con una sintaxis más amigable, no una característica de lenguaje separada. Por eso los arrays de C no tienen verificación de límites: numbers[i] es solo un cálculo de dirección, y el compilador no tiene forma de saber si esa dirección todavía está dentro del array.
A continuación: strings — por qué C no tiene un tipo string incorporado, y qué es realmente un string por debajo.
Practice exercise
Dado int numbers[3] = {10, 20, 30} y int *p = numbers, imprime el segundo elemento usando aritmética de pointers (*(p + 1)).