Punteros & Arreglos
Programación II
Ing. Matías J. Magni
Lenguaje C
PUNTERO:
“Un puntero es una variable que almacena 
la dirección de memoria de otra variable”
Ing. Matías J. Magni
PUNTEROS Y DIRECCIONES
✔ Una computadora típica tiene un arreglo de celdas de 
memoria consecutivamente ordenadas que pueden ser 
manipuladas individualmente o en grupos contiguos.
✔ 1 byte puede ser un char.
✔ 1 par de celdas de 1 byte puede ser un short.
✔ 4 bytes adyacentes pueden formar un long.
✔ Un puntero es un grupo de celdas que pueden 
almacenar una dirección.
Ing. Matías J. Magni
PUNTEROS Y DIRECCIONES
✔ Si c es un char y p es un puntero que apunta a c, podemos 
representarlo gráficamente de la siguiente manera:
✔ El operador unario & nos da la dirección de una variable: 
p = &c; // asigna la dirección de c a la variable p
✔ Se dice entonces que “p apunta a c”.
✔ & solo aplica a objetos en memoria: variables y elementos de un array.
✔ & NO aplica a expresiones, constantes o variables registradas.
Ing. Matías J. Magni
PUNTEROS Y DIRECCIONES
✔ El operador unario * es el operador de indirección o desreferencia.
✔ Aplicando * a un puntero me devuelve el valor de la variable a la que apunta.
int x = 1, y = 2, z[10];
int *ip; // ip es un puntero a int
ip = &x; // ip ahora apunta a x
y = *ip; // y ahora vale 1
*ip = 0; // x ahora es 0
ip = &z[0]; // ip ahora apunta a z[0]
✔ Podemos incrementar la variable a la cual apunta ip en 1 de la sig. manera: *ip
+= 1 o ++*p o (*ip)++
✔ iq = ip // copia el contenido de ip a iq haciendo que iq
apunte a donde quiera que ip esté apuntando
Ing. Matías J. Magni
PUNTEROS Y ARGUMENTOS 
DE FUNCIONES
✔ Ya que C pasa argumentos a funciones por valor, no hay una forma 
directa para que la función altere una variable en esa llamada.
✔ La única forma de lograr esto es pasando punteros en los argumentos 
de la función:
swap(&a, &b);
void swap(int *px, int *py) { // interchange *px and
*py
int temp;
temp = *px;
*px = *py;
*py = temp;
}
Ing. Matías J. Magni
PUNTEROS Y ARGUMENTOS 
DE FUNCIONES
Pictóricamente:
Ing. Matías J. Magni
PUNTEROS Y ARREGLOS
✔ Existe una fuerte relación entre punteros y arreglos.
✔ Cualquier cosa que hagamos con arreglos puede ser logrado con 
punteros y de una manera más performante.
✔ La declaración: int a[10]; define un arreglo de tamaño 10 que es 
un bloque de 10 objetos consecutivos llamados a[0],a[1],…,a[9].
✔ La notación a[i] se refiere al i­ésimo elemento del arreglo. Si pa es 
un puntero a un entero declarado como: int *pa; luego la 
asignación pa = &a[0]; hace que pa apunte al elemento 0 de a, o 
sea, pa contiene la dirección de a[0].
Ing. Matías J. Magni
PUNTEROS Y ARREGLOS
✔ Ahora la asignación: x = *pa; copiará el contenido de a[0] a x.
✔ Si pa apunta a un elemento particular de un array, por definición pa+1 apunta al 
próximo elemento, pa+i apunta a i elementos después de pa y pa-i apunta a i 
elementos antes.
✔ Si pa apunta a a[0], *(pa+1) se refiere al contenido de a[1], pa+i es la dirección 
de a[i] y *(pa+i) es el contenido de de a[i].
✔ La expresión: pa = &a[0]; es idéntica a: pa = a;
✔ La expresión: a[i] también puede ser escrita como: *(a+i)
✔ Las expresiones: &a[i] y a+i también son idénticas.
✔ Estas expresiones: char s[] y char *s también son idénticas.
Ing. Matías J. Magni
✔ Hay 2 rutinas para asignar almacenamiento:
✔ alloc(n): devuelve un puntero a n posiciones de caracteres 
consecutivos, el cual puede ser utilizado para almacenar caracteres.
✔ afree(p): libera el almacenamiento que fue adquirido para poder 
ser utilizado más tarde.
✔ Usamos un puntero llamado allocp que apunta al próximo 
elemento libre. Cuando a alloc se le piden n caracteres, se 
fija si queda espacio libre en allocbuf. Si es así, alloc 
devuelve el valor actual de allocp (por ej. el principio del 
bloque vacío) y lo incrementa en n para apuntar al próximo 
espacio vacío. Si no hay lugar, alloc devuelve 0. afree(p) 
asigna p a allocp, si p está dentro de allocbuf.
Ing. Matías J. Magni
ARITMETICA
✔ Si p y q apuntan a miembros del mismo arreglo entonces 
las relaciones ==, !=, <, >=, etc., funcionan.
Por ej: p < q 
Es verdadero si p apunta a algún elemento anterior al que 
apunta q.
Ing. Matías J. Magni
ARITMETICA
✔ Un string constante escrito como:
“I am a string”
Es un arreglo de caracteres. El arreglo está determinado por el caracter 
nulo '0' para que el programa puede encontrar el fin.
✔ Si pmessage es declarado como:
char *pmessage;
Entonces la declaración:
pmessage = "now is the time";
✔ asigna a pmessage un puntero al arreglo de caracteres.
✔ Hay una importante diferencia entre estas definiciones:
➢ char amessage[] = "now is the time"; //an arreglo
➢ char *pmessage = "now is the time"; // un puntero
Ing. Matías J. Magni
PUNTEROS A CARACTERES 
➢ amessage es un arreglo lo suficientemente grande como para 
almacenar la secuencia de caracteres y '0'. Caracteres 
individuales dentro del arreglo pueden ser modificados pero 
amessage siempre apuntará al mismo almacenamiento.
➢ pmessage es un puntero, inicializado para apuntar a un string 
constante, el puntero puede ser modificado para apuntar a 
cualquier lado pero el resultado es indefinido si se trata de 
modificar el contenido del string.
Gráficamente:
✔
Ing. Matías J. Magni
PUNTEROS A CARACTERES 
✔ Los punteros pueden ser almacenados en 
arreglos por el hecho de ser variables.
✔ Para ilustrar este hecho vamos a ver un 
ejemplo de un programa que ordena líneas 
de texto en orden alfabético. Para ello 
utilizaremos un arreglo de punteros.
Ing. Matías J. Magni
ARREGLOS DE PUNTEROS
PUNTEROS A PUNTEROS 
✔ 2 líneas pueden ser comparadas pasando sus 
punteros a strcmp().
✔ El proceso de ordenamiento cuenta de 3 
pasos:
1) Leer todas las líneas de entrada.
2) Ordenarlas.
3) Imprimirlas en orden.
Ing. Matías J. Magni
ARREGLOS DE PUNTEROS
PUNTEROS A PUNTEROS 
#include <string.h>
// max #lines to be sorted
#define MAXLINES 5000
// pointers to text lines
char *lineptr[MAXLINES];
int readlines(char *lineptr[], int nlines);
void writelines(char *lineptr[], int nlines);
// sort input lines
void qsort(char *lineptr[], int left, int
right);
int main() {
// number of input lines read
int nlines = readlines(lineptr, MAXLINES);
if (nlines >= 0) {
qsort(lineptr, 0, nlines-1);
writelines(lineptr, nlines);
return 0;
} else {
printf("error: input too big to sortn");
return 1;
}
}
// max length of any input line
#define MAXLEN 1000
int getline(char *, int);
char *alloc(int); // readlines: read input lines
int readlines(char *lineptr[], int maxlines) {
int len, nlines;
char *p, line[MAXLEN];
nlines = 0;
while ((len = getline(line, MAXLEN)) > 0)
if (nlines >= maxlines || p = alloc(len) ==
NULL)
return -1;
else {
line[len-1] = '0'; /* delete newline */
strcpy(p, line);
lineptr[nlines++] = p;
}
return nlines;
}
// writelines: write output lines
void writelines(char *lineptr[], int nlines) {
int i;
for (i = 0; i < nlines; i++)
printf("%sn", lineptr[i]);
}
ARREGLOS DE PUNTEROS
PUNTEROS A PUNTEROS 
Ing. Matías J. Magni
✔ char *lineptr[MAXLINES];
Arreglo de MAXLINES elementos, cada elemento es un puntero a char. 
lineptr[i] es un puntero a caracteres y *lineptr[i] es el caracter al cual 
apunta, el primer caracter de la i­ésima línea de texto almacenada.
✔ writelines() puede ser escrita como:
/* writelines: write output lines */
void writelines(char *lineptr[], int nlines) {
while (nlines-- > 0)
printf("%sn", *lineptr++);
}
✔ Inicialmente, *lineptr apunta a la primer línea, cada elemento lo avanza 
al siguiente puntero de línea mientras que nlines es decrementado.
Ing. Matías J. Magni
ARREGLOS DE PUNTEROS
PUNTEROS A PUNTEROS 
✔ Ahora podemos proceder al ordenamiento:
// qsort: sort v[left]...v[right] en orden incremental
void qsort(char *v[], int left, int right) {
int i, last;
void swap(char *v[], int i, int j);
if (left >= right) return; // No hacer nada si el array contiene menos de 2
elementos
swap(v, left, (left + right)/2);
last = left;
for (i = left+1; i <= right; i++) {
if (strcmp(v[i], v[left]) < 0) {
swap(v, ++last, i);
}
}
swap(v, left, last);
qsort(v, left, last-1);
qsort(v, last+1, right);
}
Ing. Matías J. Magni
ARREGLOS DE PUNTEROS
PUNTEROS A PUNTEROS 
✔ La rutina para intercambiar líneas se vería así:
/* swap: intercambiar v[i] por v[j] */
void swap(char *v[], int i, int j) {
char *temp;
temp = v[i];
v[i] = v[j];
v[j] = temp;
}
Ing. Matías J. Magni
ARREGLOS DE PUNTEROS
PUNTEROS A PUNTEROS 
✔ C provee arreglos multidimensionales rectangulares, aunque en la práctica 
sean menos usados que los arreglos de punteros.
✔ Consideremos el problema de la conversión de fechas:
✔ Vamos a definir 2 funciones:
● day_of_year(): convierte el mes y el día en el día del año.
● month_day(): convierte el día del año en el mes y el día.
month_day(1988, 60, &m, &d);
Pone a m en 2 y d en 29 (29 de Febrero).
✔ La información que maneja es una tabla con los días de cada mes.
static char daytab[2][13] = {
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
Ing. Matías J. Magni
ARREGLOS 
MULTIDIMENSIONALES 
int day_of_year(int year, int
month, int day) {
int i, leap;
leap = year%4 == 0 && year%100 != 
0 || year%400 == 0;
for (i = 1; i < month; i++)
day += daytab[leap][i];
return day;
}
void month_day(int year, int
yearday, int *pmonth, int
*pday) {
int i, leap;
leap = year%4 == 0 && year%100 != 
0 || year%400 == 0;
for (i = 1; yearday > daytab[leap][i]; 
i++)
yearday ­= daytab[leap][i];
*pmonth = i;
*pday = yearday;
}
ARREGLOS 
MULTIDIMENSIONALES 
Ing. Matías J. Magni
✔ Consideremos el problema de escribir una función month_name(n):
/* month_name: devuelve el nombre del n-ésimo mes */
char *month_name(int n) {
static char *name[] = {
"Illegal month",
"January", "February", "March",
"April", "May", "June",
"July", "August", "September",
"October", "November", "December"
};
return (n < 1 || n > 12) ? name[0] : name[n];
}
✔ La declaración de nombre, el cual es un arreglo de punteros a caracteres es igual a lineptr en el ejemplo 
de ordenamiento. El inicializador es una lista de cadena de caracteres, cada una es asignada a la position 
correspondiente en el arreglo.
✔ Los caracteres del i­ésimo string son colocados en algún lugar y un puntero a ellos es almacenado en 
name[i].
✔ Ya que el tamaño del arreglo no es especificado, el compilador cuenta los inicializadores y llena el número 
correcto.
Ing. Matías J. Magni
INICIALIZACION DE 
ARREGLOS DE PUNTEROS 
✔ Los novatos en  C, a menudo, confunden la diferencia 
entre un arreglo de 2 dimensiones y un arreglo de 
punteros.
✔ Dadas las siguientes definiciones:
int a[10][20];
int *b[10];
La diferencia radica en que en el primer arreglo las 
filas únicamente pueden tener 20 caracteres de largo. 
En cambio, en el arreglo de punteros, las filas pueden 
ser de tamaño variable.
Ing. Matías J. Magni
PUNTEROS vs. ARREGLOS 
MULTIDIMENSIONALES 
✔ char *name[] = { "Illegal month",
"Jan", "Feb", "Mar" };
✔ char aname[][15] = { "Illegal
month", "Jan", "Feb", "Mar" };
Ing. Matías J. Magni
PUNTEROS vs. ARREGLOS 
MULTIDIMENSIONALES 
✔ Cuando main() es llamado, se le pasan 2 argumentos:
✔ argc: contador de argumentos.
✔ argv: vector de argumentos. Puntero a un arreglo de 
caracteres que contienen los argumentos, uno por string.
✔ argv[0]: nombre del programa.
✔ Si argc = 1, el programa no tiene argumentos.
✔ Si argc = n, el programa tiene n­1 argumentos.
Ing. Matías J. Magni
ARGUMENTOS DE LINEA 
DE COMANDOS 
● 1° versión de cómo el comando echo 
trata a argv como un arreglo de 
punteros: 
#include <stdio.h>
main(int argc, char *argv[]) {
int i;
for (i = 1; i < argc; i++) {
printf("%s%s", argv[i],
(i < argc-1) ? " " : "");
}
printf("n");
return 0;
}
● Como argv es un puntero a un arreglo 
de punteros, podemos manipular el 
puntero en vez del índice del arreglo: 
#include <stdio.h>
main(int argc, char *argv[]) {
while (--argc > 0) {
printf("%s%s", *++argv,
(argc > 1) ? " " : "");
}
printf("n");
return 0;
}
ARGUMENTOS DE LINEA 
DE COMANDOS 
Ing. Matías J. Magni
✔ Es posible asignar punteros a funciones. Ej:
void qsort(void *v[], int left, int right, int (*comp)(void *, void *)) {
int i, last;
void swap(void *v[], int, int);
if (left >= right) // no hacer nada si el array contiene menos de 2 elementos
return;
swap(v, left, (left + right)/2);
last = left;
for (i = left+1; i <= right; i++) {
if ((*comp)(v[i], v[left]) < 0) {
swap(v, ++last, i);
}
}
swap(v, left, last);
qsort(v, left, last-1, comp);
qsort(v, last+1, right, comp);
}
Ing. Matías J. Magni
PUNTEROS A FUNCIONES 
✔ El 4° parámetro de qsort es:
int (*comp)(void *, void *)
✔ comp es un puntero a una función que tiene 2 
argumentos void * y devuelve un int.
✔ Los paréntesis son necesarios, sin ellos la 
declaración:
int *comp(void *, void *) /* ERROR */
nos dice que comp() es una función que retorna un 
puntero a un int.
Ing. Matías J. Magni
PUNTEROS A FUNCIONES 
✔ Hay diferencia entre estas declaraciones:
✔ int *f(); // f: función que devuelve
un puntero a int
✔ int (*pf)(); // pf: puntero a una
función que devuelve un int
✔ * es un operador prefijo y tiene precedencia 
más baja que (), así que los paréntesis son 
necesarios para reforzar la asociación correcta.
Ing. Matías J. Magni
DECLARACIONES 
COMPLICADAS 
✔ Primer dcl:
char **argv
argv: pointer to char
int (*daytab)[13]
daytab: pointer to array[13] of int
int *daytab[13]
daytab: array[13] of pointer to int
void *comp()
comp: function returning pointer to void
void (*comp)()
comp: pointer to function returning void
char (*(*x())[])()
x: function returning pointer to array[] of pointer to function
returning char
char (*(*x[3])())[5]
x: array[3] of pointer to function returning pointer to array[5] of char
Ing. Matías J. Magni
DECLARACIONES 
COMPLICADAS 
✔ dcl se basa en la gramática que especifica un declarador. Los declaradores tienen la siguiente sintaxis:
declarator:
pointeropt direct-declarator
direct-declarator:
identifier
(declarator)
direct-declarator [ constant-expressionopt ]
direct-declarator ( parameter-type-list )
direct-declarator ( identifier-listopt )
pointer:
* type-qualifier-listopt
* type-qualifier-listopt pointer
type-qualifier-list:
type-qualifier
type-qualifier-list type-qualifier
Ing. Matías J. Magni
DECLARACIONES 
COMPLICADAS 
✔ Esta es una forma simplificada:
dcl: optional *'s direct-dcl
direct-dcl name
(dcl)
direct-dcl()
direct-dcl[optional size]
✔ Consideremos esta declaración:
(*pfa[])()
pfa será identificada como un nombre y como una direct-dcl. Entonces pfa[] es 
también una direct-dcl. Entonces *pfa[] es reconocida como una dcl y 
(*pfa[]) es una direct-dcl. Por lo tanto (*pfa[])() es una direct-dcl y 
también una dcl. También podemos ilustrar el parseo con un árbol como este: 
(donde direct-dcl ha sido abreviada a una dir-dcl).
Ing. Matías J. Magni
DECLARACIONES 
COMPLICADAS 
Ing. Matías J. Magni
DECLARACIONES 
COMPLICADAS 
El corazón del programa dcl es un par de funciones dcl y dirdcl que parsean una 
declaración de acuerdo a su gramática. Ya que la gramática es definida recursivamente, las 
funciones se llaman entre ellas recursivamente a medida que reconocen partes de la 
declaración. El programa es conocido como recursive­descent parser.
BIBLIOGRAFIA
● Brian W. Kernighan & Dennis M. Ritchie 
(1988). The C programming Language. 
Prentice Hall.
Ing. Matías J. Magni

Punteros y Arreglos