Академический Документы
Профессиональный Документы
Культура Документы
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.
PÁGINA: 1
Lenguaje C
• Actúa como índice (físico) de arreglos:
int arr[5] = {2, 4, 6, 8, 10}, n = 5, i, *p = arr, q = p+n;
p q arr
0x7ff...00 0x7ff...24 2 4 6 8 10
Direcciones: 0X7ff...00 0x7ff...24
Indices: 0 1 2 3 4
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 no al apuntador pn sino a *pn (valor apuntado por pn):
int n = 65, int *pn;
pn = &n;
Escrito en una sola línea:
int n = 65, int *pn = &n; // *pn es de tipo int, y pn = &n (toma como valor la dirección de 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; tiene un valor hexadecimal, el cual es la dirección de
otra variable. pn puede ser interpretado como un variable espía de n, con las siguientes capacidades:
Sabe la dirección de n: pn → 0x7ff...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
PÁGINA: 2
Lenguaje C
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
El operador de indirección * tiene mayor prioridad que el operador de multiplicación *, ejemplo:
int n=2, *pn= &n, b;
b = *pn * 2; // es equivalente a: b = *pn*2; b = 2 * *pn; b = 2**pn; b = 2 * * pn; (no importa la cantidad de espacios)
No abuse de la escritura confusa, use espacios en blanco y paréntesis para aclararla. Más adelante estudiaremos las reglas de priori-
zación.
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;
void main(void){
int m = 4, var1, var2, var3;
m = fun(m, &var1, &var2, &var3); // se pasa por valor; var1, var2 y var3 se pasan por referencia
printf("%d %d %d %d\n", m, var1, var2, var3);
}
Salida: 0 1 2 3
Respuesta: scanf( ) opera así:
scanf(“formato”, lista de direcciones de memoria<hexadecimal>)
lee los datos y los almacena en las direcciones de memoria suministradas
por lo tanto, dentro de fun( ) se debe escribir:
scanf("%d %d %d", var1, var2, var3); // ya que var1, var2 … ya son direcciones de memoria
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.
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
PÁGINA: 5
Lenguaje C
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)
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
PÁGINA: 6
Lenguaje C
arr[1] *(arr+1) parr[1] *(parr+1) 20
arr[2] *(arr+2) parr[2] *(parr+2) 30
arr[1] = 80; *(arr+1) = 80; parr[1] = 80; *(parr+1) = 80; 80
n = fun(arr, 3); n = fun(arr, 3); n = fun(parr, 3); n = fun(parr, 3); Llama a la función fun()
int fun(int arr[ ], int n){ int fun(int *arr, int n) { int fun(int parr[], int n){ int fun(int *parr, int n) { Define a la función fun()
arr[0] = 2; *arr = 2; parr[0] = 2; *parr = 2;
arr[1] = 3; *(arr+1) = 3; parr[1] = 3; *(parr+1) = 3;
*(arr+2) = 4; arr[2] = 4; *(parr+2) = 4; parr[2] = 4;
return arr[2]; return *(arr+2); return parr[2]; return *(parr+2);
} } } }
Los elementos del arreglo toman dos formas equivalentes: arr[i] == *(arr+i).
El apuntador parr se parece a un “arreglo libre”: no tiene elementos y apunta a cualquier variable o arreglo. Mientras parr apunte a un
arreglo de 1 dimensión arr, por ejemplo:
int arr[3] = {10, 20, 30}, *parr = arr;
Se puede referir a un elemento i de 4 modos equivalentes:
arr[i] = = parr[i] = = *(arr+i) = = *(parr+i) // el desarrollador controla de no salirse del rango del arr
Si pa apunta a una variable simple:
int a = 10, *pa = &pa;
Se puede utilizar: pa[i] == *(pa+i) // Compila bien; pero ya no tiene sentido lógico: en tiempo de ejecución estamos invadiendo un
// área no controlada
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 *(parr+n) == parr[n] == arr[n] == *(arr+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
PÁGINA: 7
Lenguaje C
Un apuntador puede tener 3 aplicaciones:
1) Como apuntador a una variable o arreglo
int arr[3], *parr = arr, a, *pa = &a; // parr y pa son apuntatodes
pa = &parr[1]; // ahora pa apunta al elento 1: 0x7ff...4.
parr < pa // 0x7ff...0 < 0x7ff...4
*parr < *pa // 1 < 2
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[i] = = parr[i] = = *(arr+i) = = *(parr+i) // para i = 2:
arr[2] = = parr[2] = = *(arr+2) = = *(parr+2) = 3
PÁGINA: 8
Lenguaje C
void reporte(int n , int arr[ ]){ void reporte(int n, int *p){
int i=0; // Los desplazamientos son lógicos int *pmax = p+n; // Los desplazamientos son físicos
while(i<n) printf("%d ", arr[i++]); while(p<pmax) printf("%d ", *p++);
printf("\n");
}
void suma2(int n, int arr[ ]){ void suma2(int n, int *p){
int i=0; // i es variable de recorrido int *pmax = p+n; // p es variable de
while(i<n) arr[i++] +=2; recorrido
while(p<pmax) *p++ +=2;
}
Salida:
Datos iniciales: 0 1 2
Datos + 2 :234
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
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
PÁGINA: 9
Lenguaje C
*/
#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.
PÁGINA: 10
Lenguaje C
Apuntador a arreglos de dos dimensiones
int arr[2][3] = {2, 4, 6, 8, 10, 12}, *parr = &arr[0][0]; // arr[0][0] tiene la misma dirección que arr
Equivalente:
int arr[2][3] = {2, 4, 6, 8, 10, 12}, *parr = arr[0]; // el vector fila arr[0] apunta a la misma dirección que arr
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).
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
PÁGINA: 11
Lenguaje C
void mitad(int n, int *p){ void mitad(int m, int n, int *p){ void mitad(int m, int n, int arr[][n]){
// entran los valores 4 y 0x7ff...2 // entran los valores 2, 2 y 0x7ff...2 // entran los valores 2, 2, 0x7ff...2
int *pmax = p + n; int *pmax = p + m*n; int i, j;
while(p<pmax) *p++ /=2; while(p < pmax) *p++ /=2; for(i=0; i < m; i++)
for(j=0; j < n; j++) arr[i][j] /=2;
} } }
Salida: 1 2 3 4
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 apunte a la dirección correcta.
¿Cuál de las dos notaciones utilizar? La notación lógica es la que manda porque es lógica; cuando los desplazamientos lógicos son con-
tinuos -sin saltos- ambas notaciones coinciden, entonces se recomienda usar la notación física porque es más rápida. Hay casos en que
se usan ambas, ejemplo, transponer una matriz en otra.
PÁGINA: 12
Lenguaje C
Atento: Un apuntador a un arreglo puede salirse del rango del arreglo, ejemplo, imprimir un arreglo bidimensional 2 veces:
#include<stdio.h>
void prin2(int m, int n , int *pa);
void main(void){
int a[2][3] = {1, 2, 3, 4, 5, 6}, *pa= a[0]; // supongamos que pa apunta a 0xff..a
prin2(2, 3 , pa); // argumentos: 2, 3, 0xff..a
}
void prin2(int m, int n, int *pa){ // valores: 2, 3, 0xff..a
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, se salió del rango de a[2][3]
p = pa; // p vuelve a apuntar a la posición inicial: 0xff..a
}
}
Salida (2 veces):
123
123
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
Ejemplo de uso específico: Particionar un espacio en la RAM como arreglo de 1, 2 y 3 dimensione
PÁGINA: 13
Lenguaje C
Apuntador a apuntador
Un puntero es una variable como cualquier otra, por lo tanto se puede definir un puntero a un 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 2
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
PÁGINA: 14
Lenguaje C
Paso 3:
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 n, int m, int arr[][m]); void ordenar(int n, int m, int *parr[m]);
void reportar(int n, int m, int arr[][m]); void reportar(int n, int m, int *parr[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(n, m, arr); reportar(n, m, parr);
ordenar(n, m, arr); ordenar (n, m, parr);
printf("\nArreglo ordenado:\n"); printf("\nArreglo ordenado:\n");
reportar(n, m, arr); reportar(n, m, parr);
} }
void reportar(int n, int m, int arr[ ][m]) { void reportar(int n, int m, int **p) {
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 n, int m, int arr[][m]) { void ordenar(int n, int m, int **p) {
int i, j, imin, amin; int **pmax = p+n, **pj, **pimin, amin, *temp;
for(i=0; i<n-1; i++){ for(; p<pmax-1; p++) {
imin = i; pimin = 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; pimin = pj;
} }
if(imin>i){ // intercambio de filas if(pimin>p){ // intercambio de apuntadores
temp = *pimin;
arr[imin][0] = arr[i][0]; *pimin = *p;
arr[i][0] = amin; *p = temp;
amin = arr[imin][1];
arr[imin][1] = arr[i][1];
arr[i][1] = amin;
} }
} }
} }
Salida:
Arreglo original Arreglo ordenado:
17 3 10 1
16 6 16 6
18 6 17 3
10 1 18 6
19 2 19 2
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 de 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.
// Funciona bien // Funciona con warning // Funciona bien
#include<stdio.h> #include<stdio.h> #include<stdio.h>
void mitad(int m, int n, int (*p)[n]); void mitad(int m, int n, int *p); void mitad(int m, int n, int *p);
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];
mitad(3, 2, a); mitad(3, 2, a); mitad(3, 2, p);
} } }
void mitad(int m, int n, int (*p)[n]){ void mitad(int m, int n, int *p){ void mitad(int m, int n, int *p){
// p es puntero a arreglo de n ele- // p es puntero y apunta a arreglo // p es puntero y apunta a la dirección inicial
mentos 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
PÁGINA: 16
Lenguaje C
Apuntador de tipo void (genérico)
La palabra reservada void (se pronuncia void en inglés y significa vacío, nulo, vacante, nada, ...) se utiliza para crear un puntero que
apuntará a cualquier tipo de dato, ejemplo:
float fl=2.4, *pfl = &fl; float fl=2.4;
void *pvoid = pfl; // apunta a fl void *pvoid = &fl; // apunta a fl
pfl = (float *)pVoid; // Para asignar a otro tipo se requiere casting
printf("%.2f %.2f\n", *pfl, *(float *)pvoid);
Salida: 2.40 2.40
Ejemplo:
int in = 1; float fl = 2.4;
// asignar a void asignar void imprimir
pvoid = ∈ int *pin = (int *)pvoid; printf("%d\n", *pin); // = 1
pvoid = &fl; float *pfl = (float *)pvoid; printf("%lf\n", *pfl); // = 2.4
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:
int *p;
…
free(p); // p es un apuntador a cualquier tipo de dato, free() libera la memoria y asigna p = NULL.
¡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: 18