SlideShare a Scribd company logo
1 of 23
República Bolivariana de Venezuela
Ministerio del poder popular para la defensa
Universidad experimental politécnica de la
Fuerza armada Bolivariana.

Profesor:
Luis Sequera.

Integrante:
Mariannis Ortega.
Apuntadores
Los apuntadores son una parte fundamental de C. Si
usted no puede usar los apuntadores apropiadamente
entonces está perdiendo la potencia y la flexibilidad que
C ofrece básicamente. El secreto para C está en el uso
de apuntadores.
C usa los apuntadores en forma extensiva. ¿Por qué?
Es la única forma de expresar algunos cálculos.
Se genera código compacto y eficiente.
Es una herramienta muy poderosa.
C usa apuntadores explícitamente con:
Es la única forma de expresar algunos cálculos.
Se genera código compacto y eficiente.
Es una herramienta muy poderosa.
C usa apuntadores explícitamente con:
Arreglos,
Estructuras y
Funciones
Definición de un apuntador
Un apuntador es una variable que contiene la
dirección en memoria de otra variable. Se pueden tener
apuntadores a cualquier tipo de variable.
El
operador unario o monódico & devuelve
dirección de memoria de una variable.

la

El operador de indirección o de referencia * devuelve
el ``contenido de un objeto apuntado por un apuntador''.
Para declarar un apuntador para una variable entera
hacer:
int *apuntador;
Se debe asociar a cada apuntador un tipo particular.
Por ejemplo, no se puede asignar la dirección de
un short int a un long int.
Para tener una mejor idea, considerar el siguiente
código:
main ()
{
int x = 1, y = 2;
int *ap;
ap = &x;
y = *ap;
x = ap;
*ap = 3;
}
Cuando se compile el código se mostrará el
siguiente
mensaje:
warning: assignment makes integer from pointer without
a cast.
Con el objetivo de entender el comportamiento del
código supongamos que la variable x esta en la localidad
de la memoria 100, y en 200 y ap en 1000. Nota: un
apuntador es una variable, por lo tanto, sus valores
necesitan ser guardados en algún lado.
int x = 1, y = 2;
int *ap;
ap = &x;
100 200 1000
x 1 y 2 ap 100

Las variables x e y son declaradas e inicializadas
con 1 y 2 respectivamente, ap es declarado como un
apuntador a entero y se le asigna la dirección de x (&x).
Por lo que ap se carga con el valor 100.
y = *ap;
100 200 1000
x 1 y 1 ap 100
Después y obtiene el contenido de ap.
En el ejemplo ap apunta a la localidad de memoria 100 - la localidad de x. Por lo tanto, y obtiene el valor de x -el cual es 1.
x = ap;
100

200 1000

x 100 y 1 ap 100
Como se ha visto C no es muy estricto en la
asignación de valores de diferente tipo (apuntador a
entero). Así que es perfectamente legal (aunque el
compilador genera un aviso de cuidado) asigna el valor
actual de ap a la variable x. El valor de ap en ese
momento es 100.
*ap = 3;
100 200 1000
x 3 y 1 ap 100
Finalmente se asigna un valor al contenido de un
apuntador (*ap).

Importante: Cuando un apuntador es declarado
apunta a algún lado. Se debe inicializar el apuntador
antes de usarlo. Por lo que:
main()
{
int *ap;
*ap = 100;
}
Puede generar un error en tiempo de ejecución o
presentar un comportamiento errático.
El uso correcto será:
main()
{
int *ap;
int x;
ap = &x;
*ap = 100;
}
Con los apuntadores se puede realizar también
aritmética entera, por ejemplo:
main()
{
float *flp, *flq;
*flp = *flp + 10;
++*flp;
(*flp)++;
flq = flp;
}
Algo muy importante que debemos notar: Un
apuntador a cualquier tipo de variables es una dirección
en memoria -- la cual es una dirección entera, pero un
apuntador NO es un entero.
La razón por la cual se asocia un apuntador a un
tipo de dato, es porque se debe conocer en cuantos
bytes está guardado el dato. De tal forma, que cuando
se incrementa un apuntador, se incrementa el apuntador
por un ``bloque'' de memoria, en donde el bloque está en
función del tamaño del dato.
Por lo tanto para un apuntador a un char, se agrega
un byte a la dirección y para un apuntador a entero o a
flotante se agregan 4 bytes. De esta forma si a un
apuntador a flotante se le suman 2, el apuntador
entonces se mueve dos posiciones float que equivalen a
8 bytes.
Apuntadores y Funciones
Cuando C pasa argumentos a funciones, los
pasa por valor, es decir, si el parámetro es modificado
dentro de la función, una vez que termina la función el
valor pasado de la variable permanece inalterado.
Hay muchos casos que se quiere alterar el
argumento pasado a la función y recibir el nuevo valor
una vez que la función ha terminado. Para hacer lo
anterior se debe usar una llamada por referencia, en C
se puede simular pasando un puntero al argumento. Con
esto se provoca que la computadora pase la dirección
del argumento a la función.
Para entender mejor lo anterior consideremos la
función swap() que intercambia el valor de dos
argumentos enteros:
void swap(int *px, int *py);
main()
{
int x, y;
x = 10;
y = 20;
printf("x=%dty=%dn",x,y);
swap(&x, &y);
printf("x=%dty=%dn",x,y);
}
void swap(int *px, int *py)
{
inttemp;
temp = *px; /* guarda el valor de la direccion x */
*px = *py; /* pone y en x */
*py = temp; /* pone x en y */
}
Apuntadores y arreglos
Existe una relación estrecha entre los punteros y los
arreglos. En C, un nombre de un arreglo es un índice a
la dirección de comienzo del arreglo. En esencia, el
nombre de un arreglo es un puntero al arreglo.
Considerar lo siguiente:
int a[10], x;
int *ap;
ap = &a[0];

/* ap apunta a la direccion de a[0] */

x = *ap;
/* A x se le asigna el contenido de ap
(a[0] en este caso) */
*(ap + 1) = 100; /* Se asigna al segundo elemento de
'a' el valor 100 usando ap*/
Como se puede observar en el ejemplo la
sentencia a[t] es idéntica a ap+t. Se debe tener cuidado
ya que C no hace una revisión de los límites del arreglo,
por lo que se puede ir fácilmente más alla del arreglo en
memoria y sobreescribir otras cosas.
C sin embargo es mucho más sútil en su relación
entre arreglos y apuntadores. Por ejemplo se puede
teclear solamente:
ap = a; en vez de ap = &a[0]; y también *(a + i) en vez de a[i],
esto es, &a[i] es equivalente con a+i.
Y como se ve en el ejemplo, el direccionamiento de
apuntadores se puede expresar como:
a[i] que es equivalente a *(ap + i)
Sin embargo los apuntadores y los arreglos son
diferentes:
Un apuntador es una variable. Se puede hacer ap =
a y ap++.
Un arreglo NO ES una variable. Hacer a = ap y a++ ES
ILEGAL.
Este parte es muy importante, asegúrese haberla
entendido.
Con lo comentado se puede entender como los
arreglos son pasados a las funciones. Cuando un arreglo
es pasado a una función lo que en realidad se le esta
pasando es la localidad de su elemento inicial en
memoria.
Por lo tanto:
strlen(s) es equivalente a strlen(&s[0])
Esta es la razón por la cual se declara la función
como:
intstrlen(char
s[]); y
es intstrlen(char *s);

una

declaración

equivalente

ya que char s[] es igual que char *s.
La función strlen() es una función de la biblioteca
estándar que regresa la longitud de una cadena. Se
muestra enseguida la versión de esta función que podría
escribirse:
intstrlen(char *s)
{
char *p = s;
while ( *p != '0' )
p++;
return p - s;
}
Se muestra enseguida una función para copiar una
cadena en otra. Al igual que en el ejercicio anterior
existe en la biblioteca estándar una función que hace lo
mismo.
voidstrcpy(char *s, char *t)
{
while ( (*s++ = *t++) != '0' );
En los dos últimos ejemplos se emplean apuntadores y
asignación por valor.
Nota: Se emplea el uso del carácter nulo con la
sentencia while para encontrar el fin de la cadena.
Arreglos de apuntadores
En C se pueden tener arreglos de apuntadores ya
que los apuntadores son variables.
A continuación se muestra un ejemplo de su
uso: ordenar las líneas de un texto de diferente
longitud.
Los
arreglos
de
apuntadores
son
una
representación de datos que manejan de una forma
eficiente y conveniente líneas de texto de longitud
variable.
¿Cómo se puede hacer lo anterior?
1. Guardar todas las líneas en un arreglo de
tipo char grande. Observando que n marca el fin de
cada línea. Ver figura 8.1.
2. Guardar los apuntadores en un arreglo diferente donde
cada apuntador apunta al primer caracter de cada línea.
3. Comparar dos líneas usando la función de la biblioteca
estándar strcmp().
4. Si dos líneas están desacomodadas -- intercambiar
(swap) los apuntadores (no el texto).
Arreglos de apuntadores (Ejemplo de ordenamiento de
cadenas).
Con lo anterior se elimina:
el manejo complicado del almacenamiento.
alta sobrecarga por el movimiento de líneas.
DIRECCIONES DE MEMORIA
En informática, una dirección de memoria es un
identificador para una localización de memoria con la
cual
un programa
informático o
un
dispositivo
de hardware pueden
almacenar
un dato para
su
posterior reutilización.
Una forma común de describir la memoria
principal de un ordenador es como una colección de
celdas que almacenan datos e instrucciones. Cada celda
está identificada unívocamente por un número o
dirección de memoria.
Para poder acceder a una ubicación específica de
la memoria, la CPU genera señales en el bus de
dirección, que habitualmente tiene un tamaño de 32 bits
en la mayoría de máquinas actuales. Un bus de
dirección de 32 bits permite especificar a la CPU
=
4.294.967.296 direcciones de memoria distintas.
Debido a la estructura de 32 bits de
un procesador común como los de Intel, las direcciones
de memoria se expresan a menudo en hexadecimal. Por
ejemplo,
para
no
tener
que
escribir
111111010100000000000010101100 podemos escribir
3F5000AC en hexadecimal.
Modos de direccionamiento.
En aplicaciones informáticas las direcciones son
asignadas por el sistema operativo a cada programa en
ejecución, asegurándose éste, comúnmente por medio de
un daemon, que las direcciones utilizadas por un ejecutable u
otro proceso no se solapen o se escriba en posiciones
protegidas de memoria, por ejemplo, en el sector de
arranque.
Los sistemas operativos actuales son comúnmente
diferenciados según el ancho de palabra soportado por sus
registros, es decir 32 y 64 bits. Estas cifras se refieren a la
máxima capacidad que dichos sistemas operativos pueden
direccionar, así un sistema de 32 bits podría acceder y
direccionar, sin utilizar memoria virtual, un máximo de
232 posiciones de memoria, usualmente designadas por un
código hexadecimal. Debido a esto, el rango de valores
naturales que pueden ser almacenados en 32 bits es de 0
hasta 4.294.967.295 (0h - FFFFFFFFh), que vienen a ser los
famosos 4 gigabytes de capacidad límite de los sistemas
operativos de 32 bits.
Para los sistemas de 64 bits, siguiendo el razonamiento
anterior, obtendríamos 264 posibilidades, lo que se traduce en
un
rango
de
valores
desde
0
hasta
18.446.744.073.709.551.615 (0h- FFFFFFFFFFFFFFFFh),
18,4 exabytes o
18.400.000.000.000
de
gigabytes
direccionables.
En los lenguajes de programación, se puede acceder a
las direcciones de memoria utilizando punteros. Si bien
algunos sistemas operativos y lenguajes actuales no permiten
acceder a determinadas direcciones de memoria (o incluso,
lenguajes como Java que no implementan punteros), esto no
significa que dichas direcciones no existan o no sean
correctas, por ejemplo, la posición de memoria 0h es una
posición válida y correcta y es normal que se trabaje sobre
ella por ejemplo cuando se modifica la tabla descriptora de
interrupciones.
Pero cuando se trabaja en modo protegido, los
programas ejecutándose como aplicaciones de usuario no
tienen acceso a algunas posiciones (entre ellas la 0h), pero
en el sistema operativo DOS que trabaja en modo real, se
puede acceder a toda la memoria disponible con un simple
programa de usuario.
Ejercicio
Ejercicio 1: Crear un programa llamado paresImpares que
cree un array de 100 números aleatorios del 1 al 1000. Una
vez creado, mostrar el contenido y después organizarlo de
forma que estén juntos los elementos pares y los impares.
Después, volver a mostrar el array
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#define TAM 100

void escribirArray(int a[], int tamanio);

int main(){
clrscr();
int a[TAM];
int par[TAM]={0};
int impar[TAM]={0};
int i,j;

srand(time(NULL));

/*Relleno inicial del array a*/
for(i=0;i<TAM;i++){

a[i]=rand()%100+1;

}

/* Escritura del contenido del primer array*/

printf(" Primer array:n");
printf("n");
escribirArray(a,TAM);

/* Grabación de los pares y los impares en los otros
arrays*/

for(i=0;i<TAM;i++){

if(a[i]%2==0)

par[i]=a[i];
else

impar[i]=a[i];

}

/* Escritura del array par e impar*/
printf("n");
printf(" Par:n");
printf("n");
escribirArray(par,TAM);
printf("n");
printf(" Impar:n");
printf("n");
escribirArray(impar,TAM);

/* Mezcla de ambos arrays en el array a
y escritura del resultado final*/
j=0;/*j es el índice del array a.
Sólo se mueve cuando se insertan en él números*/

/*recorrido del array par e impar e inserción de los números
pares que contenga (todos los que sean distintos de 0)
se añade al array a desde la primera posición del mismo*/

for(i=0;i<TAM;i++){

if(par[i]!=0) {

a[j]=par[i];
j++;

}

}

/*recorrido del array impar e inserción de los números
impares que contenga desde la posición en la que quedó
j*/
for(i=0;i<TAM;i++){

if(impar[i]!=0) {

a[j]=impar[i];
j++;

}

}

/*Resultado final*/
printf("n");
printf(" Resultado final:n");
printf("n");
escribirArray(a,TAM);
getch();
} /* fin main */
/* Escribe el contenido de un array de enteros por pantalla */

void escribirArray(int a[], int tamanio){

int i;
for(i=0;i<tamanio;i++){

printf("%d ",a[i]);

}

printf("n");
}

Ejercicio 2: Crear un programa que contenga una función
llamada copiarArray ) que reciba dos arrays y el tamaño de
los mismos (deben de ser del mismo tamaño) y que consiga
copia en el segundo array el contenido del primero
#include <conio.h>
#include <stdio.h>
void copiarArray(int a[], int b[], int tamanio);
void escribirArray(int a[], int tamanio);

int main(){clrscr();

/* Comprobación de la función*/

int uno[]={2,4,5,6,7,8,9,11};
int dos[8];
printf("n");
printf("Array uno:n");
printf("n");
escribirArray(uno,8);
copiarArray(uno,dos,8);
printf("n");
printf("Array dos:n");
printf("n");
escribirArray(dos,8);

getch();

}

/* Escribe el contenido de un array de enteros por pantalla */

void escribirArray(int a[], int tamanio){

int i;
for(i=0;i<tamanio;i++)

printf("%d ",a[i]);

printf("n");
}

/* Copia el contenido del array b en el array a. Se supone
que ambos arrays son del mismo tamanio*/

void copiarArray(int a[], int b[], int tamanio){

int i;
for(i=0;i<tamanio;i++){

b[i]=a[i];

}

}

More Related Content

What's hot

What's hot (20)

Punteros yo
Punteros yoPunteros yo
Punteros yo
 
Introducción a los Punteros en el lenguaje C
Introducción a los Punteros en el lenguaje CIntroducción a los Punteros en el lenguaje C
Introducción a los Punteros en el lenguaje C
 
Cadenas de Caracteres
Cadenas de CaracteresCadenas de Caracteres
Cadenas de Caracteres
 
Arreglos en c++2020
Arreglos en c++2020Arreglos en c++2020
Arreglos en c++2020
 
Arquitectura resumen capitulos 5 y 6 efrain saransig
Arquitectura resumen capitulos 5 y 6 efrain saransigArquitectura resumen capitulos 5 y 6 efrain saransig
Arquitectura resumen capitulos 5 y 6 efrain saransig
 
Lenguajec 1
Lenguajec 1Lenguajec 1
Lenguajec 1
 
Pe15 cadenacaracteres
Pe15 cadenacaracteresPe15 cadenacaracteres
Pe15 cadenacaracteres
 
BIBLIOTECAS EN PROGRAMACION
BIBLIOTECAS EN PROGRAMACIONBIBLIOTECAS EN PROGRAMACION
BIBLIOTECAS EN PROGRAMACION
 
Estructuras dinámicas de datos
Estructuras dinámicas de datosEstructuras dinámicas de datos
Estructuras dinámicas de datos
 
Ejercicios punteros cadenas-vectores
Ejercicios punteros cadenas-vectoresEjercicios punteros cadenas-vectores
Ejercicios punteros cadenas-vectores
 
Tema 7 www.fresymetal.com
Tema 7 www.fresymetal.comTema 7 www.fresymetal.com
Tema 7 www.fresymetal.com
 
Lenguaje de programación c
Lenguaje de programación cLenguaje de programación c
Lenguaje de programación c
 
arrays
arraysarrays
arrays
 
Matematicos
MatematicosMatematicos
Matematicos
 
Dn12 u3 a18_vsrg
Dn12 u3 a18_vsrgDn12 u3 a18_vsrg
Dn12 u3 a18_vsrg
 
Tema7 dinamicas
Tema7 dinamicasTema7 dinamicas
Tema7 dinamicas
 
Instrucciones lenguaje assembler
Instrucciones lenguaje assemblerInstrucciones lenguaje assembler
Instrucciones lenguaje assembler
 
Funciones en Lenguaje C
Funciones en Lenguaje CFunciones en Lenguaje C
Funciones en Lenguaje C
 
Introduccion c
Introduccion cIntroduccion c
Introduccion c
 
Fp tema 6 punteros
Fp tema 6 punterosFp tema 6 punteros
Fp tema 6 punteros
 

Similar to Apuntadores C

Trabajo programacion 1 jose silva
Trabajo programacion 1 jose silvaTrabajo programacion 1 jose silva
Trabajo programacion 1 jose silvaJhosse Ant Siilva
 
Apuntadores
ApuntadoresApuntadores
Apuntadoresluisabn
 
Guia De Practica 3
Guia De Practica 3Guia De Practica 3
Guia De Practica 3louis
 
Apuntadores y direccionamiento
Apuntadores y direccionamientoApuntadores y direccionamiento
Apuntadores y direccionamientoSergio Ramos
 
Algoritmos y lenjuage de programacion
Algoritmos y  lenjuage de programacionAlgoritmos y  lenjuage de programacion
Algoritmos y lenjuage de programacionYsaac Ruiz
 
Estructura de Datos
Estructura de DatosEstructura de Datos
Estructura de Datosluna_72
 
Tema 8 www.fresymetal.com
Tema 8 www.fresymetal.comTema 8 www.fresymetal.com
Tema 8 www.fresymetal.comFresyMetal
 
Administración de memoria y apuntadores
Administración de memoria y apuntadoresAdministración de memoria y apuntadores
Administración de memoria y apuntadoresFranklin Chavez
 
Clase 11- fundamentos de la programacion
Clase 11- fundamentos de la programacionClase 11- fundamentos de la programacion
Clase 11- fundamentos de la programaciondiego MC
 
PUNTEROS (APUNTADORES) EN C++
PUNTEROS (APUNTADORES) EN C++PUNTEROS (APUNTADORES) EN C++
PUNTEROS (APUNTADORES) EN C++die_dex
 
Gestión Dinámica de la Memoria
Gestión Dinámica de la MemoriaGestión Dinámica de la Memoria
Gestión Dinámica de la MemoriaMago Julio Cesar
 

Similar to Apuntadores C (20)

nodo
nodonodo
nodo
 
Trabajo programacion 1 jose silva
Trabajo programacion 1 jose silvaTrabajo programacion 1 jose silva
Trabajo programacion 1 jose silva
 
Apuntadores
ApuntadoresApuntadores
Apuntadores
 
Guia De Practica 3
Guia De Practica 3Guia De Practica 3
Guia De Practica 3
 
Apuntadores y direccionamiento
Apuntadores y direccionamientoApuntadores y direccionamiento
Apuntadores y direccionamiento
 
Material iii parcial
Material iii parcialMaterial iii parcial
Material iii parcial
 
Algoritmos y lenjuage de programacion
Algoritmos y  lenjuage de programacionAlgoritmos y  lenjuage de programacion
Algoritmos y lenjuage de programacion
 
Estructura de Datos
Estructura de DatosEstructura de Datos
Estructura de Datos
 
08 - Punteros en lenguaje C
08 - Punteros en lenguaje C08 - Punteros en lenguaje C
08 - Punteros en lenguaje C
 
Tema 8 www.fresymetal.com
Tema 8 www.fresymetal.comTema 8 www.fresymetal.com
Tema 8 www.fresymetal.com
 
Programacion
ProgramacionProgramacion
Programacion
 
Programacion
ProgramacionProgramacion
Programacion
 
Administración de memoria y apuntadores
Administración de memoria y apuntadoresAdministración de memoria y apuntadores
Administración de memoria y apuntadores
 
Cod intermedio
Cod intermedioCod intermedio
Cod intermedio
 
Clase 11- fundamentos de la programacion
Clase 11- fundamentos de la programacionClase 11- fundamentos de la programacion
Clase 11- fundamentos de la programacion
 
PUNTEROS (APUNTADORES) EN C++
PUNTEROS (APUNTADORES) EN C++PUNTEROS (APUNTADORES) EN C++
PUNTEROS (APUNTADORES) EN C++
 
Gestión Dinámica de la Memoria
Gestión Dinámica de la MemoriaGestión Dinámica de la Memoria
Gestión Dinámica de la Memoria
 
Librerias de dev c++
Librerias de dev c++Librerias de dev c++
Librerias de dev c++
 
Apunfun1
Apunfun1Apunfun1
Apunfun1
 
2 punteros y lenguaje c
2 punteros y lenguaje c2 punteros y lenguaje c
2 punteros y lenguaje c
 

Apuntadores C

  • 1. República Bolivariana de Venezuela Ministerio del poder popular para la defensa Universidad experimental politécnica de la Fuerza armada Bolivariana. Profesor: Luis Sequera. Integrante: Mariannis Ortega.
  • 2. Apuntadores Los apuntadores son una parte fundamental de C. Si usted no puede usar los apuntadores apropiadamente entonces está perdiendo la potencia y la flexibilidad que C ofrece básicamente. El secreto para C está en el uso de apuntadores. C usa los apuntadores en forma extensiva. ¿Por qué? Es la única forma de expresar algunos cálculos. Se genera código compacto y eficiente. Es una herramienta muy poderosa. C usa apuntadores explícitamente con: Es la única forma de expresar algunos cálculos. Se genera código compacto y eficiente. Es una herramienta muy poderosa. C usa apuntadores explícitamente con: Arreglos, Estructuras y Funciones
  • 3. Definición de un apuntador Un apuntador es una variable que contiene la dirección en memoria de otra variable. Se pueden tener apuntadores a cualquier tipo de variable. El operador unario o monódico & devuelve dirección de memoria de una variable. la El operador de indirección o de referencia * devuelve el ``contenido de un objeto apuntado por un apuntador''. Para declarar un apuntador para una variable entera hacer: int *apuntador; Se debe asociar a cada apuntador un tipo particular. Por ejemplo, no se puede asignar la dirección de un short int a un long int. Para tener una mejor idea, considerar el siguiente código: main () { int x = 1, y = 2; int *ap; ap = &x; y = *ap; x = ap; *ap = 3; }
  • 4. Cuando se compile el código se mostrará el siguiente mensaje: warning: assignment makes integer from pointer without a cast. Con el objetivo de entender el comportamiento del código supongamos que la variable x esta en la localidad de la memoria 100, y en 200 y ap en 1000. Nota: un apuntador es una variable, por lo tanto, sus valores necesitan ser guardados en algún lado. int x = 1, y = 2; int *ap; ap = &x; 100 200 1000 x 1 y 2 ap 100 Las variables x e y son declaradas e inicializadas con 1 y 2 respectivamente, ap es declarado como un apuntador a entero y se le asigna la dirección de x (&x). Por lo que ap se carga con el valor 100. y = *ap; 100 200 1000 x 1 y 1 ap 100 Después y obtiene el contenido de ap. En el ejemplo ap apunta a la localidad de memoria 100 - la localidad de x. Por lo tanto, y obtiene el valor de x -el cual es 1.
  • 5. x = ap; 100 200 1000 x 100 y 1 ap 100 Como se ha visto C no es muy estricto en la asignación de valores de diferente tipo (apuntador a entero). Así que es perfectamente legal (aunque el compilador genera un aviso de cuidado) asigna el valor actual de ap a la variable x. El valor de ap en ese momento es 100. *ap = 3; 100 200 1000 x 3 y 1 ap 100 Finalmente se asigna un valor al contenido de un apuntador (*ap). Importante: Cuando un apuntador es declarado apunta a algún lado. Se debe inicializar el apuntador antes de usarlo. Por lo que: main() { int *ap; *ap = 100; } Puede generar un error en tiempo de ejecución o presentar un comportamiento errático.
  • 6. El uso correcto será: main() { int *ap; int x; ap = &x; *ap = 100; } Con los apuntadores se puede realizar también aritmética entera, por ejemplo: main() { float *flp, *flq; *flp = *flp + 10; ++*flp; (*flp)++; flq = flp; } Algo muy importante que debemos notar: Un apuntador a cualquier tipo de variables es una dirección en memoria -- la cual es una dirección entera, pero un apuntador NO es un entero. La razón por la cual se asocia un apuntador a un tipo de dato, es porque se debe conocer en cuantos bytes está guardado el dato. De tal forma, que cuando se incrementa un apuntador, se incrementa el apuntador
  • 7. por un ``bloque'' de memoria, en donde el bloque está en función del tamaño del dato. Por lo tanto para un apuntador a un char, se agrega un byte a la dirección y para un apuntador a entero o a flotante se agregan 4 bytes. De esta forma si a un apuntador a flotante se le suman 2, el apuntador entonces se mueve dos posiciones float que equivalen a 8 bytes. Apuntadores y Funciones Cuando C pasa argumentos a funciones, los pasa por valor, es decir, si el parámetro es modificado dentro de la función, una vez que termina la función el valor pasado de la variable permanece inalterado. Hay muchos casos que se quiere alterar el argumento pasado a la función y recibir el nuevo valor una vez que la función ha terminado. Para hacer lo anterior se debe usar una llamada por referencia, en C se puede simular pasando un puntero al argumento. Con esto se provoca que la computadora pase la dirección del argumento a la función. Para entender mejor lo anterior consideremos la función swap() que intercambia el valor de dos argumentos enteros: void swap(int *px, int *py); main() { int x, y; x = 10;
  • 8. y = 20; printf("x=%dty=%dn",x,y); swap(&x, &y); printf("x=%dty=%dn",x,y); } void swap(int *px, int *py) { inttemp; temp = *px; /* guarda el valor de la direccion x */ *px = *py; /* pone y en x */ *py = temp; /* pone x en y */ } Apuntadores y arreglos Existe una relación estrecha entre los punteros y los arreglos. En C, un nombre de un arreglo es un índice a la dirección de comienzo del arreglo. En esencia, el nombre de un arreglo es un puntero al arreglo. Considerar lo siguiente: int a[10], x; int *ap; ap = &a[0]; /* ap apunta a la direccion de a[0] */ x = *ap; /* A x se le asigna el contenido de ap (a[0] en este caso) */ *(ap + 1) = 100; /* Se asigna al segundo elemento de 'a' el valor 100 usando ap*/ Como se puede observar en el ejemplo la sentencia a[t] es idéntica a ap+t. Se debe tener cuidado
  • 9. ya que C no hace una revisión de los límites del arreglo, por lo que se puede ir fácilmente más alla del arreglo en memoria y sobreescribir otras cosas. C sin embargo es mucho más sútil en su relación entre arreglos y apuntadores. Por ejemplo se puede teclear solamente: ap = a; en vez de ap = &a[0]; y también *(a + i) en vez de a[i], esto es, &a[i] es equivalente con a+i. Y como se ve en el ejemplo, el direccionamiento de apuntadores se puede expresar como: a[i] que es equivalente a *(ap + i) Sin embargo los apuntadores y los arreglos son diferentes: Un apuntador es una variable. Se puede hacer ap = a y ap++. Un arreglo NO ES una variable. Hacer a = ap y a++ ES ILEGAL. Este parte es muy importante, asegúrese haberla entendido. Con lo comentado se puede entender como los arreglos son pasados a las funciones. Cuando un arreglo es pasado a una función lo que en realidad se le esta pasando es la localidad de su elemento inicial en memoria. Por lo tanto: strlen(s) es equivalente a strlen(&s[0])
  • 10. Esta es la razón por la cual se declara la función como: intstrlen(char s[]); y es intstrlen(char *s); una declaración equivalente ya que char s[] es igual que char *s. La función strlen() es una función de la biblioteca estándar que regresa la longitud de una cadena. Se muestra enseguida la versión de esta función que podría escribirse: intstrlen(char *s) { char *p = s; while ( *p != '0' ) p++; return p - s; } Se muestra enseguida una función para copiar una cadena en otra. Al igual que en el ejercicio anterior existe en la biblioteca estándar una función que hace lo mismo. voidstrcpy(char *s, char *t) { while ( (*s++ = *t++) != '0' ); En los dos últimos ejemplos se emplean apuntadores y asignación por valor. Nota: Se emplea el uso del carácter nulo con la sentencia while para encontrar el fin de la cadena.
  • 11. Arreglos de apuntadores En C se pueden tener arreglos de apuntadores ya que los apuntadores son variables. A continuación se muestra un ejemplo de su uso: ordenar las líneas de un texto de diferente longitud. Los arreglos de apuntadores son una representación de datos que manejan de una forma eficiente y conveniente líneas de texto de longitud variable. ¿Cómo se puede hacer lo anterior? 1. Guardar todas las líneas en un arreglo de tipo char grande. Observando que n marca el fin de cada línea. Ver figura 8.1. 2. Guardar los apuntadores en un arreglo diferente donde cada apuntador apunta al primer caracter de cada línea. 3. Comparar dos líneas usando la función de la biblioteca estándar strcmp(). 4. Si dos líneas están desacomodadas -- intercambiar (swap) los apuntadores (no el texto). Arreglos de apuntadores (Ejemplo de ordenamiento de cadenas). Con lo anterior se elimina: el manejo complicado del almacenamiento. alta sobrecarga por el movimiento de líneas.
  • 12. DIRECCIONES DE MEMORIA En informática, una dirección de memoria es un identificador para una localización de memoria con la cual un programa informático o un dispositivo de hardware pueden almacenar un dato para su posterior reutilización. Una forma común de describir la memoria principal de un ordenador es como una colección de celdas que almacenan datos e instrucciones. Cada celda está identificada unívocamente por un número o dirección de memoria. Para poder acceder a una ubicación específica de la memoria, la CPU genera señales en el bus de dirección, que habitualmente tiene un tamaño de 32 bits en la mayoría de máquinas actuales. Un bus de dirección de 32 bits permite especificar a la CPU = 4.294.967.296 direcciones de memoria distintas. Debido a la estructura de 32 bits de un procesador común como los de Intel, las direcciones de memoria se expresan a menudo en hexadecimal. Por ejemplo, para no tener que escribir 111111010100000000000010101100 podemos escribir 3F5000AC en hexadecimal.
  • 13. Modos de direccionamiento. En aplicaciones informáticas las direcciones son asignadas por el sistema operativo a cada programa en ejecución, asegurándose éste, comúnmente por medio de un daemon, que las direcciones utilizadas por un ejecutable u otro proceso no se solapen o se escriba en posiciones protegidas de memoria, por ejemplo, en el sector de arranque. Los sistemas operativos actuales son comúnmente diferenciados según el ancho de palabra soportado por sus registros, es decir 32 y 64 bits. Estas cifras se refieren a la máxima capacidad que dichos sistemas operativos pueden direccionar, así un sistema de 32 bits podría acceder y direccionar, sin utilizar memoria virtual, un máximo de 232 posiciones de memoria, usualmente designadas por un código hexadecimal. Debido a esto, el rango de valores naturales que pueden ser almacenados en 32 bits es de 0 hasta 4.294.967.295 (0h - FFFFFFFFh), que vienen a ser los famosos 4 gigabytes de capacidad límite de los sistemas operativos de 32 bits. Para los sistemas de 64 bits, siguiendo el razonamiento anterior, obtendríamos 264 posibilidades, lo que se traduce en un rango de valores desde 0 hasta 18.446.744.073.709.551.615 (0h- FFFFFFFFFFFFFFFFh), 18,4 exabytes o 18.400.000.000.000 de gigabytes direccionables.
  • 14. En los lenguajes de programación, se puede acceder a las direcciones de memoria utilizando punteros. Si bien algunos sistemas operativos y lenguajes actuales no permiten acceder a determinadas direcciones de memoria (o incluso, lenguajes como Java que no implementan punteros), esto no significa que dichas direcciones no existan o no sean correctas, por ejemplo, la posición de memoria 0h es una posición válida y correcta y es normal que se trabaje sobre ella por ejemplo cuando se modifica la tabla descriptora de interrupciones. Pero cuando se trabaja en modo protegido, los programas ejecutándose como aplicaciones de usuario no tienen acceso a algunas posiciones (entre ellas la 0h), pero en el sistema operativo DOS que trabaja en modo real, se puede acceder a toda la memoria disponible con un simple programa de usuario. Ejercicio Ejercicio 1: Crear un programa llamado paresImpares que cree un array de 100 números aleatorios del 1 al 1000. Una vez creado, mostrar el contenido y después organizarlo de forma que estén juntos los elementos pares y los impares. Después, volver a mostrar el array #include <conio.h> #include <stdio.h> #include <stdlib.h>
  • 15. #define TAM 100 void escribirArray(int a[], int tamanio); int main(){ clrscr(); int a[TAM]; int par[TAM]={0}; int impar[TAM]={0}; int i,j; srand(time(NULL)); /*Relleno inicial del array a*/
  • 16. for(i=0;i<TAM;i++){ a[i]=rand()%100+1; } /* Escritura del contenido del primer array*/ printf(" Primer array:n"); printf("n"); escribirArray(a,TAM); /* Grabación de los pares y los impares en los otros arrays*/ for(i=0;i<TAM;i++){ if(a[i]%2==0) par[i]=a[i];
  • 17. else impar[i]=a[i]; } /* Escritura del array par e impar*/ printf("n"); printf(" Par:n"); printf("n"); escribirArray(par,TAM); printf("n"); printf(" Impar:n"); printf("n"); escribirArray(impar,TAM); /* Mezcla de ambos arrays en el array a y escritura del resultado final*/
  • 18. j=0;/*j es el índice del array a. Sólo se mueve cuando se insertan en él números*/ /*recorrido del array par e impar e inserción de los números pares que contenga (todos los que sean distintos de 0) se añade al array a desde la primera posición del mismo*/ for(i=0;i<TAM;i++){ if(par[i]!=0) { a[j]=par[i]; j++; } } /*recorrido del array impar e inserción de los números impares que contenga desde la posición en la que quedó j*/
  • 19. for(i=0;i<TAM;i++){ if(impar[i]!=0) { a[j]=impar[i]; j++; } } /*Resultado final*/ printf("n"); printf(" Resultado final:n"); printf("n"); escribirArray(a,TAM); getch(); } /* fin main */
  • 20. /* Escribe el contenido de un array de enteros por pantalla */ void escribirArray(int a[], int tamanio){ int i; for(i=0;i<tamanio;i++){ printf("%d ",a[i]); } printf("n"); } Ejercicio 2: Crear un programa que contenga una función llamada copiarArray ) que reciba dos arrays y el tamaño de los mismos (deben de ser del mismo tamaño) y que consiga copia en el segundo array el contenido del primero #include <conio.h> #include <stdio.h>
  • 21. void copiarArray(int a[], int b[], int tamanio); void escribirArray(int a[], int tamanio); int main(){clrscr(); /* Comprobación de la función*/ int uno[]={2,4,5,6,7,8,9,11}; int dos[8]; printf("n"); printf("Array uno:n"); printf("n"); escribirArray(uno,8); copiarArray(uno,dos,8); printf("n"); printf("Array dos:n"); printf("n");
  • 22. escribirArray(dos,8); getch(); } /* Escribe el contenido de un array de enteros por pantalla */ void escribirArray(int a[], int tamanio){ int i; for(i=0;i<tamanio;i++) printf("%d ",a[i]); printf("n");
  • 23. } /* Copia el contenido del array b en el array a. Se supone que ambos arrays son del mismo tamanio*/ void copiarArray(int a[], int b[], int tamanio){ int i; for(i=0;i<tamanio;i++){ b[i]=a[i]; } }