Punteros - Cinvestav

Anuncio
Punteros
CURSO DE PROGRAMACIÓN EN C
Centro de Investigación y de Estudios Avanzados del IPN.
CINVESTAV - Tamaulipas.
Febrero 2016
[Curso de programación en C] - Punteros
1/30
Punteros
Las celdas de memoria dentro de la computadora están numeradas
consecutivamente. Cada dato almacenado ocupa una o más celdas
contiguas de memoria.
El número de celdas de memoria requeridas para almacenar un dato
depende de su tipo, e.g. un caracter se almacenará normalmente en
un byte; un entero usualmente necesita dos bytes contiguos; etc.
El número asociado con cada celda de memoria es conocido como la
dirección de la celda.
h
1700
[Curso de programación en C] - Punteros
38
1000
34
1002
3
1004
o
1701
l
1702
a
1703
\0
1704
2/30
Asignaciones de Variables Puntero
Las variables puntero pueden apuntar a variables numéricas de caracter, arrays, funciones o a otras variables puntero. Los punteros, como
cualquier otra variable, deben ser declarados.
tipo *ptvar;
A una variable puntero se le puede asignar la dirección de una
variable ordinaria
pv = &v;
A una variable puntero se le puede asignar la dirección de otra
variable puntero, siempre que ambas sean «apunten» al mismo
tipo de datos
pv = px;
A una variable puntero se le puede asignar un valor nulo
pv = NULL;
[Curso de programación en C] - Punteros
3/30
Operador Dirección &
Si v es una variable que representa un determinado dato, el compilador
asignará celdas de memoria para esta variable. Si se conoce la localización (dirección) de la primera celda de memoria, entonces es posible
accesar al dato.
int v;
123
1974
La dirección de memoria de v se determina mediante la expresión &v,
donde & es un operador unario, llamado el operador dirección.
&v → 1974
[Curso de programación en C] - Punteros
4/30
Operador Indirección *
Para asignar el valor de &v a la variable pv, ésta debe ser de tipo
puntero, ya que «apunta» a la posición de memoria donde se aloja v.
int v;
123
1974
XXXXX
int *pv;
pv = &v;
pv → 1974
El dato representado por v puede ser accedido mediante la expresión
*pv, donde * es un operador unario, llamado el operadar indirección
que opera sólo sobre una variable puntero.
*pv → 123
*pv y v representan el mismo dato
[Curso de programación en C] - Punteros
5/30
Ejemplo de los Operadores Dirección e Indirección
Operadores dirección (&) e indirección (*)
#include <stdio.h>
int main( ){
___int v = 5;
___int *pv; /* puntero a un entero */
___pv = &v; /* asigna dirección de v a pv */
___printf("v = %d\t &v = %p\n",v, &v);
___printf("*pv = %d\t pv = %p\n",*pv, pv);
___return 0;
}
*pv = 5
*pv = 5
[Curso de programación en C] - Punteros
&v = 0xbfcddab8
pv = 0xbfcddab8
6/30
Memoria Dinámica
La función malloc sirve para solicitar un bloque de memoria del tamaño
suministrado como parámetro. Devuelve un puntero a la zona de memoria
concedida. Si malloc es incapaz de conceder el bloque (e.g. no hay memoria
suficiente), devuelve un puntero nulo.
void* malloc(unsigned numero_de_bytes);
La función malloc devuelve un puntero inespecífico, en C estos punteros
sin tipo se declaran como void*. Un puntero void* puede convertirse a
cualquier otra clase de puntero.
Cuando una zona de memoria reservada con malloc ya no se necesita, puede
ser liberada mediante la función free.
[Curso de programación en C] - Punteros
7/30
Array de Enteros con Memoria Dinámica
Si x es un array unidimensional de 10 elementos enteros, es posible definir x
como una variable puntero en vez de como un array.
int *x;
Debido a que x no tiene asignado automáticamente un bloque de memoria
cuando se define como una variable puntero, se puede usar la función de
biblioteca malloc para asignar suficiente memoria para x como sigue:
x = (int *) malloc(10 * sizeof(int));
[Curso de programación en C] - Punteros
8/30
Operador ++ Sobre Variables Puntero
Cuando se utiliza el operador ++ sobre una variable puntero, se puede
modificar el valor del dato o la dirección al cual «apunta».
Operador ++ en una variable puntero
#include <stdio.h>
int main( ){
___int v = 5;
___int *pv; /* puntero a un entero */
___pv = &v; /* asigna dirección de v a pv */
___printf("v = %d\t &v = %p\n",v, &v);
___printf("*pv = %d\t pv = %p\n",*pv, pv);
___++*pv; /* dato al cual apunta */
___++pv; /* dirección de memoria a la cual «apunta» */
___printf("*pv = %d\t pv = %p\n",*pv, pv);
___return 0;
}
*pv = 5
*pv = 5
*pv = 6
&v = 0xbfcddab8
pv = 0xbfcddab8
pv = 0xbfcddabc
[Curso de programación en C] - Punteros
9/30
Precedencia de los operadores ++ * en variables de tipo puntero
#include<stdio.h>
#include<stdlib.h>
int main( ){
___int *pv, i;
___pv = (int*) malloc (3 * sizeof(int));
___pv[0] = 10; pv[1] = 15; pv[2] = 20;
___imprimir(3,pv);
___printf(“ %d\n”,++*pv); imprimir(3,pv);
___printf(“ %d\n”,*pv++); imprimir(3,pv);
___printf(“ %d\n”,*++pv); imprimir(3,pv);
___free(pv-=2);
___return 0;
}
void imprimir(int n, int *p ){
___int i;
___printf(“pv = %p\n”,p)
___for(i=0; i<n; ++i) printf(“ %d\t”,p[i]);
___printf(“\n\n”);
}
[Curso de programación en C] - Punteros
10/30
Precedencia de los operadores ++ * en variables de tipo puntero
#include<stdio.h>
#include<stdlib.h>
int main( ){
___int *pv, i;
___pv = (int*) malloc (3 * sizeof(int));
___pv[0] = 10; pv[1] = 15; pv[2] = 20;
___imprimir(3,pv);
___printf(“ %d\n”,++*pv); imprimir(3,pv);
___printf(“ %d\n”,*pv++); imprimir(3,pv);
___printf(“ %d\n”,*++pv); imprimir(3,pv);
___free(pv-=2);
___return 0;
}
void imprimir(int n, int *p ){
___int i;
___printf(“pv = %p\n”,p)
___for(i=0; i<n; ++i) printf(“ %d\t”,p[i]);
___printf(“\n\n”);
}
[Curso de programación en C] - Punteros
salida
pv = 0x88f2008
10 15 20
11
pv = 0x88f2008
11 15 20
11
pv = 0x88f200c
15 20 135153
20
pv = 0x88f2010
20 135153 0
10/30
Punteros y Arrays Unidimensionales
El nombre de un array es un puntero al primer elemento, por tanto, si x
es un array unidimensional, la dirección del primer elemento se puede
expresar como &x[0] o simplemente x.
float x[4]; XX
0.4
2810
0.2
2814
0.1
2818
0.3
2822
&x[0] → 2810
x → 2810
[Curso de programación en C] - Punteros
11/30
Punteros y Arrays Unidimensionales
El nombre de un array es un puntero al primer elemento, por tanto, si x
es un array unidimensional, la dirección del primer elemento se puede
expresar como &x[0] o simplemente x.
float x[4]; XX
0.4
2810
0.2
2814
0.1
2818
0.3
2822
&x[0] → 2810
x → 2810
modificarprobabilidades(x);
modificarprobabilidades(&x[0]);
void modificarprobabilidades(float x[]){
___instrucciones;
}
[Curso de programación en C] - Punteros
11/30
Punteros en Arrays Unidimensionales
La dirección del segundo elemento del array se puede escribir como
&x[1] o como (x + 1). Por tanto, la dirección del elemento i-ésimo
del array se puede expresar &x[i] o como (x + i).
float x[4]; XX
0.4
2810
0.2
2814
0.1
2818
0.3
2822
&x[2] → 2818
x + 2 → 2818
[Curso de programación en C] - Punteros
12/30
Punteros en Arrays Unidimensionales
La dirección del segundo elemento del array se puede escribir como
&x[1] o como (x + 1). Por tanto, la dirección del elemento i-ésimo
del array se puede expresar &x[i] o como (x + i).
float x[4]; XX
0.4
2810
0.2
2814
0.1
2818
0.3
2822
&x[2] → 2818
x + 2 → 2818
¿Cuál es la diferencia?
*x + 2
*(x + 2)
[Curso de programación en C] - Punteros
12/30
Transformar un Puntero a una Posición de Arreglo
strchr es una función de la biblioteca string.h que localiza un carácter en una cadena, buscando desde el principio y retorna un puntero
en la primer ocurrencia de ese caracter.
Obtener posición del arreglo a partir de un puntero
#include <stdio.h>
#include <string.h>
int main(){
___char cadena[] = “Hola Mundo”;
___char *pch;
___printf (“Localizando el caracter ’M’ en %s\n”, cadena);
___pch = strchr(cadena,’M’);
___if(pch! = NUll)
______printf(“Índice = %d\n”,pch-cadena);
___return 0;
}
[Curso de programación en C] - Punteros
13/30
Modificación de Valores con Punteros
Si se desea se puede asignar el valor de un elemento del array a otro
mediante un puntero
34
1974
56
1976
78
1978
95
1980
int numero[4] = {34, 56, 78, 95};
int *p1;
p1 = &numero[1];
numero[2] = *p1;
p1 = numero + 1;
*(numero + 3) = *p1;
[Curso de programación en C] - Punteros
14/30
Operaciones con Punteros
A una variable puntero se le puede sumar o restar un valor entero,
pero el resultado debe ser interpretado cuidadpsamente.
Si px es una variable puntero que representa la dirección de x,
se pueden escribir expresiones como ++px, −−px, (px + 3),
(px + i) y (px - i), donde i es una variable entera.
Cada expresión representará una dirección localizada a cierta distancia
de la posición original. La distancia es el resultado del producto de
la cantidad entera por el número de bytes que ocupa cada elemento
al cual apunta px.
[Curso de programación en C] - Punteros
15/30
Operaciones con Punteros
Las comparaciones de variables puntero pueden ser de utilidad cuando ambas variables «apunten» a elementos de un mismo array. Las
comparaciones posibles son <, >, <=, >=, ! =. Además, una variable
puntero puede ser comparada con cero (NULL).
float x[4]; XX
0.4
2810
0.2
2814
0.1
2818
0.3
2822
px = &x[1];
py = &x[3];
[Curso de programación en C] - Punteros
16/30
Operaciones con Punteros
Las comparaciones de variables puntero pueden ser de utilidad cuando ambas variables «apunten» a elementos de un mismo array. Las
comparaciones posibles son <, >, <=, >=, ! =. Además, una variable
puntero puede ser comparada con cero (NULL).
float x[4]; XX
0.4
2810
0.2
2814
0.1
2818
0.3
2822
px = &x[1];
py = &x[3];
Algunas comparaciones válidas:
(px
(px
(px
(px
< py)
!= py)
> py)
== NULL)
[Curso de programación en C] - Punteros
(2814
(2814
(2814
(2814
< 2822)
!= 2822)
> 2822)
== NULL)
16/30
Operaciones Permitidas con Punteros - Resumen
Operación
Asignar la dirección de una variable ordinaria
Asignar el valor de otra variable puxntero
Asignar un valor nulo (cero)
Sumar o restar una cantidad entera
Una variable puntero puede ser restada de otra
Comparar 2 punteros (si «apuntan» al mismo tipo)
Ejemplo
pv = &v;
pv = px;
pv = NULL;
pv + 3; ++pv; −−pv;
px - pv;
(px < py) (px == py)
Una variable puntero no puede ser multiplicada por una constante, no se pueden sumar dos punteros y así sucesivamente.
A una variable ordinaria no se le puede asignar una dirección
arbitraria, e.g.
&x = pv;
&x = NULL;
&x = &y;
[Curso de programación en C] - Punteros
17/30
Punteros de Arrays
Un array bidimensional es una colección de arrays unidimensionales.
Por tanto, se puede definir un array bidimensional como un puntero
a un grupo de arrays unidimensionales contiguos, de esta forma,
se puede escribir una declaración de array bidimensional como:
tipo (*ptvar)[expresión2];
en vez de:
tipo array[expresión1][expresión2];
[Curso de programación en C] - Punteros
18/30
Punteros a Arrays Bidimensionales de Enteros
Si x es un array bidimensional de enteros con 10 filas y 20 columnas, x se
puede declarar como:
int (*x)[20]; en vez de int x[10][20];
[Curso de programación en C] - Punteros
19/30
Punteros a Arrays Bidimensionales de Enteros
Si x es un array bidimensional de enteros con 10 filas y 20 columnas, x se
puede declarar como:
int (*x)[20]; en vez de int x[10][20];
De esta forma x apunta al primero de los arrays, i.e. la primer fila (fila 0).
Entonces, (x + 1) apunta al segundo array que es la segunda fila (fila 1) y
así sucesivamente,
[Curso de programación en C] - Punteros
19/30
Arrays de Punteros
Un array multidimensional puede ser expresado como un array de punteros. Cada puntero indica el principio de un array de dimensión (n-1).
En términos generales, un array bidimensional se puede definir como
un array unidimensional de punteros escribiendo:
tipo *ptvar[expresión1];
El nombre del array precedido por un asterisco no está encerrado entre paréntesis en este tipo de declaración. Así la regla de precedencia
de derecha a izquierda asocia el primer par de corchetes con array. El
asterisco que lo precede establece que el array contendrá punteros.
[Curso de programación en C] - Punteros
20/30
Acceso a un Elemento de una Matriz Usando Punteros
Suponga que x es un array bidimensional de 10 filas y 20 columnas, x
se puede declarar como:
int *x[10];
[Curso de programación en C] - Punteros
21/30
Acceso a un Elemento de una Matriz Usando Punteros
Suponga que x es un array bidimensional de 10 filas y 20 columnas, x
se puede declarar como:
int *x[10];
El elemento en la fila 2, columna 5, puede ser accedido escribiendo
x[2][5] o *(x[2] + 5).
[Curso de programación en C] - Punteros
21/30
Memoria Dinámica - Arrays de Punteros
Ejemplo 1
#include <stdio.h>
#include <stdlib.h>
#define MAXFIL 10
int main(){
___int fila, nc;
___int *a[MAXFIL];
___printf(“¿Cuántos elementos? ”);
___scanf(“ %d”,&nc);
___for(fila = 0; fila < MAXFIL; ++fila)
______a[fila] = (int*) malloc(nc * sizeof(int));
.
___.
.
}
[Curso de programación en C] - Punteros
22/30
Memoria Dinámica - Arrays de Punteros
Ejemplo 2
int **MemoryMatrix(int nrows, int ncols){
___int **matrix, i;
___matrix = (int**) malloc(nrows * sizeof(int*));
___if(!matrix){
______fprintf(stderr,“Error in Memory Matrix!\n”);
______exit(-1);
___}
___for(i = 0; i < nrows; i++){
______matrix[i] = (int*) malloc(ncols * sizeof(int));
______if(!matrix[i]){
_________fprintf(stderr,“Error in Memory Matrix!\n”);
_________exit(-1);
______}
___}
___return matrix;
}
[Curso de programación en C] - Punteros
23/30
Paso de Punteros a una Función
El «paso» de punteros a una función se conoce como pasar argumentos por referencia. Cuando un argumento se pasa por referencia,
la dirección o posición del dato es pasada a la función. Un prototipo de
función por referencia es el siguiente:
tipo nombrefuncion(tipo *arg1 , ..., tipo *argn );
También se pueden omitir los nombres de los argumentos:
tipo nombrefuncion(tipo *, ..., tipo *);
[Curso de programación en C] - Punteros
24/30
Paso de Parámetros por Referencia
void PasoValor(int n); /* Prototipo de función */
void PasoReferencia(int *n); /* Prototipo de función */
_
int main(){
___int num = 10;
___printf(“Antes de funciones num = %d\n”, num);
___PasoValor(num);
___printf(“Después de paso por valor num = %d\n”, num);
___PasoReferencia(&num);
___printf(“Después de paso por referencia num = %d\n”, num);
___return 0;
}
_
void PasoValor(int n){
___n = 0;
___return;
}
_
void PasoReferencia(int *n){
___*n = 0;
___return;
}
[Curso de programación en C] - Punteros
25/30
Paso de Elementos de Arrays a Funciones por Referencia
Cuando se pasa el elemento de un array a una función se pasa una
«copia», por lo que si éste se modifica en la función el elemento original del array no es alterado, sin embargo, si se pasa por referencia,
el valor original del elemento del arreglo puede ser modificado.
Paso por referencia en los elementos de un array
#include<stdio.h>
void resetearpromedio(float *pf){
___*pf = 0.0;
___return;
}
int main(){
___int i;
___float promedio[5];
.
___.
.
___resetearpromedio(&porcentajes[i]);
.
___.
.
}
[Curso de programación en C] - Punteros
26/30
Reordenación de una Cadena de Caracteres
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void reordenar(int n, char *x[]);
int main(){
___int i, n = 0;
___char *x[10];
___printf(“Introducir una cadena en una nueva línea:\n”);
___printf(“Escribir \”FIN\” para terminar\n\n”);
___do{
______x[n] = (char*) malloc (12 * sizeof(char));
______printf(“cadena %d: ”, n + 1);
______scanf(" %s", x[n]);
___}while(strcmp(x[n++],“FIN”)&&n<11);
___reordenar(−−n, x);
___imprimir(n, x);
}
[Curso de programación en C] - Punteros
27/30
Reordenación de una Cadena de Caracteres
void reordenar(int n, char *x[]){
___char *temp;
___int i, elem;
___for(elem = 0; elem < n - 1; ++elem)
______for(i = elem + 1; i < n; ++i)
_________if(strcmp(x[elem], x[i]) > 0){
____________/* intercambiar las dos cadenas */
____________temp = x[elem];
____________x[e1em] = x[i];
____________x[i] = temp;
_________}
___return;
}
void imprimir(int n, char *x[]){
___for(i = 0; i < n; ++i)
______printf("\ncadena %i: %s", i + 1, x[i]);
}
[Curso de programación en C] - Punteros
28/30
Ejercicios para aplicar los temas
Los CAST ORE S son una casa club formada por 4 equipos (ei | i = [1 − 4]),
donde cada equipo tiene de 2 a 3 miembros, i.e |ei | ∈ {2, 3}. Todos los
miembros de cada equipo realizan la venta de productos, por lo que generan
“utilidades” o “deudas” (representadas con el signo negativo).
Cada principio de mes la casa club se reune y seleccionan un
representante de cada equipo para realizar las aportaciones, de esta forma,
el total de combinaciones diferentes para las aportaciones está dada por la
ecuación:
4
Y
|ei |
i=1
Al club le interesa conocer todas las combinaciones de representantes de equipo que generan tanto el mínimo como el máximo de utilidades.
[Curso de programación en C] - Punteros
29/30
Ejercicios para aplicar los temas (Punteros)
S7_1 Codificar un programa con las siguientes características:
Generar un vector ε donde se especifique la cantidad de miembros de cada equipo ei .
Generar una matriz A de 4 filas, donde cada fila ai tiene un apuntador a un arreglo de flotantes tamaño |ei | que contiene las aportaciones de los miembros de cada equipo.
Generar un vector υ el cual irá evaluando TODAS las combinaciones (índices) de los representantes de cada equipo.
Utilizar una función de evaluación que reciba las 4 aportaciones
de los representantes de equipo y calcule las utilidades.
Imprimir todas las combinaciones de representantes que generen el mínimo y máximo de utilidades, bajo el siguiente patrón:
Utilidad Mínima|Máxima: $
Miembro_e1 ($), Miembro_e2 ($), Miembro_e3 ($),
Miembro_e4 ($)
[Curso de programación en C] - Punteros
30/30
Ejercicios para aplicar los temas
Ejemplo de entrada:
./Castores -e1 Hugo 300 Paco 200 Luis 100 -e2 Daisy 0
Mimi 500 -e3 Aurora -20.5 Ariel 10 -e4 Adam 20 Erick 50
Ejemplo de salida:
Utilidad Mínima: $99.5
Luis($100.0), Daisy($0.0), Aurora($-20.5), Adam($20)
Utilidad Máxima: $860.0
Hugo($300.0), Mimi($500.0), Ariel($10.0), Erick($50.0)
[Curso de programación en C] - Punteros
31/30
Descargar