Académique Documents
Professionnel Documents
Culture Documents
Para resolver los problemas mencionados, es necesario entender el funcionamiento de la Random Acces Memory (RAM) que utiliza un
programa al momento de ejecución. Supongamos que se declara:
int m = 66, n = 65; // se almacenan en binario: m = 1001001, n = 1000001, se interpretan en decimal: m = 66, n = 65.
Físicamente estas variables se alojan en la RAM (la cual es como una línea recta):
Memoria RAM:
m n
1001001 1000001
Dirección física en base hexadecimal: 0x7ff...0 0x7ff...8 (direcciones físicas de m y n)
Estas direcciones cambian cada vez que se ejecuta el programa
Atento: Fisicamente lo único que existe en la RAM es lo que está escrito en negro; lo escrito en rojo no existe, pero se escribe para acla-
rar al lector, los componente involucrados:
Podemos definir un nuevo tipo de variable que contenga información de la dirección física de m al momento de ejecución:
p m n
0x7ff...0 1001001 1000001
0x7ff...0 0x7ff...8
m y n son variables con información de datos: m = 66, n = 65;
p es una variable con información de la información, esto se llama metainformación.
p es una variable como cualquier otra, y se le aplican las mismas reglas.
p está especializada en manejar direcciones de variables (sus datos son las variables), lo cual le da poderes y limitaciones adicionales.
p puede ser pensada como una supervariable (jefa), como un hacker que conoce los datos de una variable y puede cambiar su valor, sin
que se entere.
for(; i<n ; i++ ) pritnf(“%d ”, arr[i]); // índice lógico: imprime los elementos de arr.
for(; p<q; p++) pritnf(“%d ”, *p); // índice físico : la línea 3 hace lo mismo que la instrucción anterior.
Salida: 2 4 6 8 10
2 4 6 8 10
• Al conocer la información la física (ejemplo de una tabla) permite ordenarla lógicamente por filas sin moverla físicamente si
alterar la tabla:
Resumen de operadores
Operador Descripción
& Operador de dirección (referencia) : p = &m = dirección de m = 0x7ff...0
* Operador de indirección (dereferencia): *p = *0x7ff...0 = *&m = m = 66
Ejemplos:
printf(“%p", p); // note el formato %p, no lo confunda con la p de apuntador
Salida: 0x7ff...0
printf(“%d", *p); // note el formato %d, para enteros
Salida: 66
Definición de apuntador
Sea una variable:
int n = 65; // En tiempo de ejecución n es aloja en una posición hexadecimal, ejemplo: 0x7ff...c .
RAM n
65
0x7ff...c
Aplicando el operador de referencia, se cumple:
&n = 0x7ff...c
Supongamos una variable pn, de tipo hexadecimal, podemos asignar:
pn = &n; // = 0x7ff...c
Aplicando el operador de desreferencia, se cumple:
*pn // = *0x7ff...c = 65
¿Cómo se define pn?, podría ser:
hexa pn; // puede ser; pero NO hay tipo hexadecimal en C
Mas bien aplicaremos un artificio muy elegante, definiendo un apuntador pn:
int *pn = &n; // *pn es de tipo int, y pn = &n
Sobre la RAM:
RAM pn n
0x7ff...c 65
0x7ff...0 0x7ff...c
Un apuntador pn es una variable, como cualquier otra; ocupa 8 bytes de memoria; almacena un valor hexadecimal, el cual es la direc -
ción de otra variable. pn puede ser interpretada como un variable espía de n, con las siguientes capacidades:
Sabe la dirección de n: pn → 0x7ff...c
PÁGINA: 2
Lenguaje C
Sabe el valor de n : *pn → 65
Puede cambiar el valor de n: *pn = 66; // asigna 66 a n.
Definición de apuntador
int n;
int *pn = &n; // *pn es el valor (de tipo int) apuntado por pn
Se lee así:
El valor apuntado por pn es de tipo int
int *pn = &n;
valor de pn = dirección de n
pn = 0x7ff...C; // No compila: no podemos manejar directamente a la RAM; esta es tarea del sistema operativo.
*pn = 0x7ff...c; // No compila: *pn es de tipo entero, no hexadecimal
n = 0x6ff...c; // No compila: n es de tipo entero, no hexadecimal
2) Sea:
int n = 65, *pn = &n;
Tipo de variable Propiedad Ejemplo
Apuntador &y* son opuestos *&pn = pn = 0x7ff...0
&*pn = pn = 0x7ff...0
Distinto de apuntador & y * no son opuestos *&n = n = 65
&*n No compila: no se define *n porque n es int.
PÁGINA: 3
Lenguaje C
No equivalentes pn = &m; // apunta a otra variable
para asignar valor &n = &m; // error: no se asigna posición de memoria a una variable
n y *pn Equivalentes para n = 65; *pn = 65;
asignar y escribir n += 2; *pn += 2;
valores printf(“%d", n); printf(“%d", *pn); // son equivalentes
p1 p2 n m
0x7ff...a 0x7ff...a 2 8
0x7ff...a
#include<stdio.h>
void main(void){
int n=2 , *p1= &n, *p2= &n, m = 8; // declaración y asignación de valores
if(p1==p2) printf("%d %d\n", *p1, *p2); // compara valores de apuntadores
}
Salida:
2 2
Observaciones:
El valor de n puede cambiar de 3 modos:
n = 4;
*p1 = 4;
*p2 = 4;
Aritmética de apuntadores
Dado que un apuntador apunta a una dirección sobre una línea, sólo se puede desplazar hacia adelante o atrás, mediante sumas y
restas:
Asignación de valores a parr
parr; // == 0x7ff...0. Apunta al primer byte de arr[0]
parr++; // parr apunta a la siguiente posición de tipo int: avanza 4 bytes: 0x7ff...4
// si parr apuntara a un tipo long, sumará 8 bytes, para char sumará 1 byte; etc.
PÁGINA: 4
Lenguaje C
parr += n; // parr apunta n posiciones más adelante
parr--; // parr apunta a la posición anterior
parr -= n; // parr apunta n posiciones previas
parr += 2*3+1; // = parr + 7; apunta 7 posiciones int (= 7*4 bytes) más adelante.
Atento:
parr = arr; // parr apunta a arr[o]
parr++; // parr apunta a arr[1]
parr++; // parr apunta a arr[2]
parr++; // parr apuntaría a arr[3]: no hay error de compilación; pero en tiempo de ejecución hay una invasión a un dato
// desconocido, lo cual es un error de lógica y puede corromper los datos.
pa = &a; // pa apunta a a.
pa++; // pa apuntaría un dato desconocido en tiempo de ejecución, lo cual es un error de lógica; aunque compile sin
// error
Operadores utilizados
Operador Descripción Asociatividad
() Paréntesis (llamar a funciones) izquierda-a-derecha
[] Corchetes (arreglos)
. selección de miembro vía objeto (ver capítulo de estructuras o clases)
-> selección de miembro vía apuntador (ver capítulo de estructuras)
++ -- Post incremento/decremento
++ -- Pre incremento/decremento Derecha-a-izquierda
+- Operadores unarios más/menos
!~ Negación lógica/bitwise complemento
(type) Casting: convierte a tipo (type) un dato de otro tipo
* Desreferencia, indirección de dirección de memoria
& referencia, dirección de variable
sizeof Determine el tamaño en bytes
Pre y postoperadores, funcionan como siempre; atento, aplique las precedencias a *p:
Post operador Pre operador
p++; // aumenta 1 a p; ++p; // aumenta 1 a p;
printf("%p\n", p++); // printf("%p\n", p); p++; printf("%p\n", ++p); // ++p; printf("%p\n", p);
printf("%p\n", (p+1)++); // error de compilación, no se puede printf("%p\n", ++(p+1)); // error de compilación
// postoperar a una expresión (p+1)
PÁGINA: 5
Lenguaje C
Apuntador constante
int a=10, b=20;
int * const pa = &a; // pa apunta solo a la variable a.
*pa = 15; // Correcto: asigna 15 a a, el valor apuntado es variable.
pa=&b; // ERROR: El valor de p es constante
pa a b
0x7ff...a 10 20
0x7ff...a
parr es un apuntador que apunta (toma su dirección) a cualquier variable simple o arreglo (no es constante, salvo especificación),
no tiene elementos; sin embargo se comporta como un arreglo a partir de la posición apuntada: parr[m] == *(parr+m).
Si parr apunta a arr se puede usar la notación *(arr+n) == arr[n] == parr[n] == *(parr+n); el programador es responsable de
mantenerse en el rango de arr.
2) El valor de parr y &parr son diferentes; el valor de arr y &arr son iguales;
parr tiene una dirección y apunta a otra dirección, no puede apuntarse a sí mismo
printf("%p\n", &parr); // dirección de parr: 0x7fa...b
printf("%p\n", parr); // apunta a 0x7ff...0
2) Como arreglo “libre” de una dimensión: no tiene elementos y apunta a cualquier arreglo:
int arr[3], *parr = arr;
Se puede referir a un elemento i de 4 modos equivalentes:
arr[2] = = parr[2] = = *(arr+2) = = *(parr+2) = 3
arr[i] = = parr[i] = = *(arr+i) = = *(parr+i)
PÁGINA: 7
Lenguaje C
Int i, j; // Recorre arreglo con variables i y j Int *p, *q, *pmax; // Recorre arreglo con apuntadores p y q
i = 0; // para iniciar el arreglo *p = arr; // apunta al inicio del arreglo
*pmax = p + n; // apunta una posición más allá del fin
j = n -1; // finaliza el arrglo *q = pmax -1; // apunta a la posición final: arr[2]
for(; i < n; i++, j--) for(; p < pmax; p++, q--)
printf("Subiendo: %d Bajando: %d\n", arr[i], arr[j]); printf("Subiendo: %d Bajando: %d\n", *p, *q);
} }
Salida:
Subiendo: 1 Bajando: 3
Subiendo: 2 Bajando: 2
Subiendo: 3 Bajando: 1
Atento: El arreglo arr, base de los datos es fijo, tiene una posición de inicio y un número de elementos; es responsabilidad del
programador:
No perder la posición de inicio
No excederse ni a la izquierda ni a la derecha de arr.
Las variables i y j permiten el recorrido de arr Los apuntadores *p y *q recorren físicamente el arreglo
Atento: Un apuntador se mueve con libertad y puede abandonar el rango del arreglo apuntado, ejemplo, imprimir un arreglo 2 veces:
void main(void){
int a[3] = {1, 2, 3}, *pa= a, i;
for(i=0; i<3; i++) printf("%d ", *pa++); printf("\n"); // los desplazamientos *pa++ hacen que pa abandone el rango de a
pa = a; // se debe reapuntar al inicio de a.
for(i=0; i<3; i++) printf("%d ", *pa++); printf("\n");
}
Salida:
123
123
PÁGINA: 8
Lenguaje C
Ventajas y desventajas de los apuntadores
Tipo de variable Ventaja/desventaja
apuntada
Variables simples : Desventaja: ocupa doble espacio de memoria, la notación es un poco más compleja.
int, float, etc. Ventaja: El paso de valores por referencia para funciones utiliza apuntadores.
Arreglo de una Similar; pero el apuntador apunta a cualquier dirección del mismo tipo, el arreglo es como un apuntador
dimensión constante.
Variables complejas: Ventaja: permiten adaptarse a la memoria lineal del computador y manejarla flexiblemente en tiempo de
arreglos de más de 1 ejecución; para cada caso específico, el programador desarrolla los algoritmos para: alojar, cambiar de tamaño,
dimensión, liberar, acceder, pasar a funciones, ordenar, proteger/desproteger, etc. datos; lo cual no puede hacer un
estructuras, pila, lenguaje; ejemplo: una matriz de estudiantes, puede ser vista tal cual es físicamente, y al mismo tiempo ser vista
colas, etc. ordenada por código, apellido, etc; esto da gran flexibilidad y se hace rápidamente ya que al ordenar solo se
mueven apuntadores y no datos.
Ventaja: Las funciones que reciben arreglos no son paramétricas a partir de la segunda dimensión; pero si usan
apuntadores, sí lo son; aunque algunas veces resulte complejo.
Ventaja: generalmente es más rápido recorrer los datos con apuntadores que con índices.
Memoria dinámica Ventaja: se maneja con apuntadores.
Ejemplo: Dados:
1: int n;
2: float *f1, *f2;
3: f2 = f1 +n;
Demostrar que f2 – f1 = n* sizeof(float)
Hagamos algunas aclaraciones:
1) En computación no se demuestra proposiciones (semántica); sino que opera con casos concretos, por lo tanto
reemplazaremos demostrar por verificar.
2) n es un valor leído/asignado, por ejemplo: n = 2.
3) No se puede asignar:
f2 = f1 + n; // f1 es indeterminado, no apunta a nada, debemos apuntar a alguna variable cualquiera.
Vamos a reformular el ejercicio:
Dados:
1: int n = 2;
2: float f, *f1 = &f, *f2;
3: f2 = f1 +n; // la dirección apuntada por f1 es aumentada en n
Verificar que: f2 – f1 = n* sizeof(float) // sizeof(float) = 4 = tamaño de variable flotante.
/* 06_02.c : Dados dos punteros float *f1, *f2; si hacemos f2 = f1 + n,
verificar que respecto a f1, la dirección de f2 aumenta: n*sizeof(float) bytes
*/
#include<stdio.h>
void main(void){
float f, *f1 = &f, *f2;
int n = 2;
f2 = f1 + n;
printf("n = f2 - f1 : %lu posiciones\n", f2 -f1);
printf("sizeof(float) : %lu bytes\n", sizeof(float));
printf("Primera dirección apuntada: %p (f1)\n", f1);
printf("Segunda dirección apuntada: %p (f2 = f1 + n; n = 2)\n", f2);
printf(" --------------\n");
printf("Aumento de dirección : %lu bytes\n", (long int)f2 - (long int)f1);
}
Salida:
n = f2 – f1 : 2 posiciones
sizeof(float) : 4 bytes
Primera dirección apuntada : 0x7fff329c5ee0 (f1)
Segunda dirección apuntada: 0x7fff329c5ee8 (f2 = f1 + 2)
–-------------------
Aumento de dirección : 8 bytes
Matriz Línea
j→
i 2 4 6 8 2 4 6 8 10 12 14 16
10 12 14 16
Cada fila i de la matriz aporta 4 datos a la línea, j aporta j datos: la posición [i][j] ocupa la posición i*4+j en la línea.
En general, para un arreglo arr[m][n], el elemento arr[i][j] ocupa la posición líneal: i*n+j en la RAM.
Posiciones: 0 1 2 3 4 5
parr arr[0][0] arr[0][1] arr[0][2] arr[1][0] arr[1][1] arr[1][2]
0x7234… 2 4 6 8 10 12
0x7234...
arr[i][j] es equivalente a parr[i*3+j], y a *(parr+ i*3+j).
PÁGINA: 10
Lenguaje C
void mitad(int n){ // entrada valor 2 void mitad(int *n){ // entrada valor 0x7ff...2
n /=2; *n /=2;
} }
Salida: n = 2 Salida: n = 1
parr++ y ++parr recorren el arreglo/matriz para adelante; parr--, --parr para atrás.
Un arreglo de una o más dimensiones mantiene su dirección de inicio FIJA -no la pierde nunca-; mientras que un apunta-
dor simplemente apunta a una dirección y puede apuntar a la siguiente: p++; o a la anterior: p--; el programador debe
controlar que el apuntador no se escape del rango del arreglo apuntado.
¿Cuál de las dos notaciones utilizar? La notación lógica es la que manda; pero cuando los desplazamientos lógicos son continuos -sin
saltos- ambas notaciones coinciden, se usa la notación física porque es más rápida. Hay casos en que se usan ambas, ejemplo,
transponer una matriz en otra.
// 06_04.c: Transponer una matriz en otra: Programa paramétrico
PÁGINA: 11
Lenguaje C
#include<stdio.h>
void imp(int *pa, int m, int n);
void tran(int *pa, int *pb, int m, int n);
void main(void){
int a[2][3] = {1,2,3,4,5,6}, b[3][2], i, j, *pa= &a[0][0], *pb = &b[0][0];
printf("Matriz inicial\n");
imp(pa, 2, 3);
tran(pa, pb, 2, 3);
printf("Matriz transpuesta\n");
imp(pb, 3, 2);
}
void imp(int *p, int m, int n){
int i, j;
for(i=0; i<m; i++){
for(j=0; j<n; j++) printf("%d ", *p++); // desplazamiento lineal de puntero
printf("\n");
}
}
// Transpuesta
// b[i][j] = a[j][i]
// b: se recorre linealmente (físico): b[i][j] ← *pb++
// a: se DEBE recorrer por índices (lógico): a[j][i] = *(pa+j*n+i)
void tran(int *pa, int *pb, int m, int n){
int i, j;
for(i=0; i<n; i++)
for(j=0; j<m; j++) *pb++ = *(pa+j*n+i); // notación física y lógica
}
Salida:
Matriz inicial Matriz transpuesta
123 14
456 25
36
Atento: Un apuntador se mueve con libertad y puede abandonar el rango del arreglo apuntado, ejemplo, imprimir un arreglo
bidimensional 2 veces:
#include<stdio.h>
void prin2(int *pa, int m, int n);
void main(void){
int a[2][3] = {1, 2, 3, 4, 5, 6}, *pa= &a[0][0]; // supongamos que pa apunta a 0xff..a
prin2(pa, 2, 3); // argumentos: 0xff..a, 3, 2
}
void prin2(int *pa, int m, int n){ // valores: 0xff..a, 3, 2
int i, j, k=0, *p = pa; // pa y p apuntan a 0xff..a
while(k++<2){
for(i=0; i<m; i++){
for(j=0; j<n; j++) printf("%d ", *p++); // p aumenta
printf("\n");
} // p apunta a 0xff..a + m*n*4, salió del rango de a[2][3]
p = pa; // p apunta a 0xff..a
}
}
Salida (2 veces):
123
123
PÁGINA: 12
Lenguaje C
Arreglo de apuntadores
Un puntero es una variable como cualquier otra, por lo tanto se puede definir arreglos de punteros, ejemplo:
int *ptr[4], n = 6;
ptr[0] = ptr[1] = &n; // ambos apuntan a n
*ptr[1]; // = 6
ptr n
0xff..c 0xff..c 6
Posiciones 0 1 2 3 0xff..c
// 2 dimensiones: 2 x 6
int *q[2]; // arreglo de punteros
q[0] = &a[0], q[1] = &a[6];
for(i=0; i<2; i++)
for(j=0; j<6; j++) printf("%d ", q[i][j]);
printf(" : 2 dimensiones 2 x 6\n");
// de 3 dimensiones: 2 x 3 x 2
int *r[2][3]; // matriz de punteros
r[0][0] = &a[0], r[0][1] = &a[2], r[0][2] = &a[4];
r[1][0] = &a[6], r[1][1] = &a[8], r[1][2] = &a[10];
for(i=0; i<2; i++)
for(j=0; j<3; j++)
for(k=0; k<2; k++) printf("%d ", r[i][j][k]);
printf(" : 3 dimensiones 2 x 3 x 2\n");
}
Salida:
1 2 3 4 5 6 7 8 9 10 11 12 : 1 dimensión de 12 elementos
1 2 3 4 5 6 7 8 9 10 11 12 : 2 dimensiones de 2 x 6
1 2 3 4 5 6 7 8 9 10 11 12 : 3 dimensiones 2 x 3 x 2
Apuntador a apuntador
Un puntero es una variable como cualquier otra, por lo tanto podemos definir un puntero a puntero:
Sintaxis:
tipo **nombreApuntador;
ejemplo:
int a = 3, *p1 = &a, **p2 = &p1;
p2 p1 a
0x7234.. 0x8174.. 3
0x65734.. 0x7234.. 0x8174..
Ejemplo: Ordenar las filas de una matriz arr[5][2] por la primera columna (utilizar el método de mínimos), lo haremos en dos versiones:
• Utilizando arreglos: se mueven las filas de la matriz
• Utilizando apuntadores (no se mueven las filas de la matriz), en 3 pasos:
1) Se crea un arreglo de apuntadores *p[ ] a las filas de la matriz;
2) Se ordena ascendentement a p en base a la primera columna de arr, la matriz no es alterada;
3) Se accede a los valores de la matriz a través de los apuntadores: *p[i]
Paso 1 paso 3
parr arr parr arr
parr[0] 17 3 parr[0] 17 3
parr[1] 16 6 parr[1] 16 6
parr[2] 18 6 parr[2] 18 6
parr[3] 10 1 parr[3] 10 1
parr[4] 19 2 parr[4] 19 2
Recorrido de arreglos:
parr arr
parr[0] 17 3
parr[1] 16 6
parr[2] pi 18 6
parr[3] 10 1
parr[4] 19 2
pj
Ordenar ascendentemente una matriz por la primera columna utilizando el método del mínimo
// 06_06a.c: utilizando arreglos // 06_06b.c: utilizando apuntadores
#include<stdio.h> #include<stdio.h>
void ordenar(int arr[][2], int n, int m); void ordenar(int *parr[], int n, int m);
void reportar(int arr[][2], int n, int m); void reportar(int *parr[], int n, int m);
void main(void){ void main(void){
int n = 5, m = 2, arr[5][2] = {17, 3, 16, 6, 18, 6, 10, int n = 5, m = 2, arr[5][2] = {17, 3, 16, 6, 18, 6, 10, 1, 19, 2};
1, 19, 2};
int i, *parr[n];
for(i=0; i<n; i++) parr[i] = arr[i];
printf("Arreglo original:\n"); printf("Arreglo original:\n");
reportar(arr, n, m); reportar(parr, n, m);
ordenar(arr, n, m); ordenar (parr, n, m);
printf("\nArreglo ordenado:\n"); printf("\nArreglo ordenado:\n");
PÁGINA: 14
Lenguaje C
reportar(arr, n, m); reportar(parr, n, m);
} }
void reportar(int arr[ ][2], int n, int m) { // restricción void reportar(int **p, int n, int m) {
int i, j; int **pmax = p+n, *pj, *pjmax;
for(i=0; i<n; i++) { for(;p<pmax; p++){
for(j=0; j<m; j++) printf("%d\t", arr[i][j]); for(pj=*p, pjmax=pj+m; pj<pjmax;pj++) printf("%d\t", *pj);
printf("\n"); printf("\n");
} }
} }
void ordenar(int arr[][2], int n, int m) { // restricción void ordenar(int **p, int n, int m) {
int i, j, imin, amin; int **pmax = p+n, **pj, **imin, amin, *temp;
for(i=0; i<n-1; i++){ for(; p<pmax-1; p++) {
imin = i; imin = p;
amin = arr[i][0]; amin = **p;
for(j=i+1; j<n; j++) // mínimo valor for(pj = p+1; pj<pmax; pj++) // mínimo valor
if (amin > arr[j][0]){ if (amin > **pj){
amin = arr[j][0]; amin = **pj;
imin = j; imin = pj;
} }
if(imin>i){ // intercambio de filas if(imin>p){ // intercambio de apuntadores
temp = *imin;
arr[imin][0] = arr[i][0]; *imin = *p;
arr[i][0] = amin; *p = temp;
amin = arr[imin][1];
arr[imin][1] = arr[i][1];
arr[i][1] = amin;
} }
} }
} }
Salida:
Arreglo original
17 3
16 6
18 6
10 1
19 2
Arreglo ordenado:
10 1
16 6
17 3
18 6
19 2
Note: la restricción en la segunda dimensión No hay restricción en la segunda dimensión
PÁGINA: 15
Lenguaje C
Ejemplos:
int *p[2];
* y [2] son adyacentes a p, [2] tiene mayor prioridad que * (regla 2)
→ arreglo de 2 punteros a enteros.
int (*p)[2];
(*p) Es un puntero
(*p)[2] Es un puntero a un arreglo de 2 elementos de tipo entero
/* 06_07.c: Algunas formas, entre otras, para acceder a los datos de una matriz utilizando punteros y las precedencias
de *, ( ) y [ ].
Al llamar a una función, hay varias formas de apuntar a una matriz, sólo el apuntador lineal funciona perfecto: Es para-
métrico, no produce warning, ni error en tiempo de ejecución.
*/
// Funciona pero no es paramétrica // Funciona con warning // Funciona perfecto
#include<stdio.h> #include<stdio.h> #include<stdio.h>
void mitad(int (*p)[2], int m, int n); void mitad(int *p, int m, int n); void mitad(int *p, int m, int n);
void main(void){ void main(void){ void main(void){
int a[3][2] = {2, 4, 6, 8, 10, 12}; int a[3][2] = {2, 4, 6, 8, 10, 12}; int a[3][2] = {2, 4, 6, 8, 10, 12}, *p = &a[0][0];
mitad(a, 3, 2); mitad(a, 3, 2); mitad(p, 3, 2);
} } }
void mitad(int (*p)[2], int m, int n){ void mitad(int *p, int m, int n){ void mitad(int *p, int m, int n){
// p es puntero a arreglo de 2 ele- // p es puntero y apunta a arreglo // p es puntro y apunta a la dirección inicial
mentos en la segunda dimensión de 2 dimensiones // lo mismo sucede para más de 2 dimensiones
int i, j; int *pmax = p+m*n; int *pmax = p+m*n;
for(i=0; i<m; i++) while(p<pmax){ while(p<pmax){
for(j=0; j<n; j++){ *p /=2; *p /=2;
p[i][j] /=2; printf("%d ", *p++); printf("%d ", *p++);
printf("%d ", p[i][j]);
} } }
printf("\n"); printf("\n"); printf("\n");
} } }
Salida: 1 2 3 4 5 6
Ejemplo: La función free( ), para liberar memoria dinámica apuntada por un puntero p de cualquier tipo, tiene la sintaxis:
void free(void *ptr);
Se puede usar:
free(p); // p es un apuntador a cualquier tipo de dato, free() libera la memoria y asigna p = NULL.
PÁGINA: 16
Lenguaje C
¡Apuntaste bien!!!!
Fuente: https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQFyz_WY0xkicaiWFizB_rnTM9D-9X8wB9QhPv5jS2h4mQGdPMC6g
En computación no somos tan dramáticos, si no apuntas bien, solo tendrás invisibles errores de lógica que ya sabes como corregirlos. Si
no quieres equivocarte, te recomiendo: 1) Enamórate del tema y cuéntale a tu novi@ para que no se ponga celos@, 2) Haz una sesión
personal especial y trata de entender todos los detalles, y 3) Reúnete con los amigos para tirar flechas, perdón, para resolver problemas
de apuntadores. ¡Ya casi eres un profesional!
Ejercicios:
1) Sea la definición siguiente:
char a[2][3] = {'a', 'b', 'c', 'd', 'e', 'f'}, *pa = &a[0][0];
Imprima la matriz:
a b c
d e f
Imprima de 3 maneras con las funciones:
void imprimirMat (char a[][3], int m, int n); // Notación de arreglos
void imprimirPtr1(char *pa, int m, int n); // Notacion de apuntadores y recorrido con índices
void imprimirPtr2(char *pa, int m, int n); // Notacion de apuntadores y recorrido con apuntadores
Matriz duplicada:
2 4 6 8 10 12 14 16 18 20 22 24
4) Una matriz p[4][12] representa una producción de los 12 meses en 4 años. Lea la matriz y use punteros para calcular el promedio de
producción en cada año y en los 12 meses:
Años
1 2 3 4
Promedio: xx xx xx xx
Meses
1 2 3 4 … 12
Promedio: xx xx xx xx xx
5) Lea las temperaturas t diarias de un lugar tropical, termine cuando t <= 0 o haya leido 100 días; cada vez que lee t imprima todas
las t.
PÁGINA: 17