apuntadores y direccionamiento

28
República Bolivariana de Venezuela Ministerio del Poder Popular Para La Defensa Universidad Nacional Experimental Politécnica De Las Fuerzas Armadas UNEFA –Yaracuy Extensión-Nirgua Materia: Programación I Profesor: Integrante:

Upload: sergio-ramos

Post on 20-Jun-2015

1.164 views

Category:

Education


3 download

DESCRIPTION

trabajo de programacion uno apuntadores y direccionamiento de memoria sergio ramos unefa ing de sistemas 7mo semestre

TRANSCRIPT

Page 1: Apuntadores y direccionamiento

República Bolivariana de VenezuelaMinisterio del Poder Popular Para La Defensa

Universidad Nacional Experimental Politécnica De Las Fuerzas ArmadasUNEFA –Yaracuy Extensión-Nirgua

Materia:

Programación I

Profesor: Integrante:

Luis sequera Sergio Ramos

25 de octubre del 2013

Punteros:

Page 2: Apuntadores y direccionamiento

Un puntero es una variable que contiene la dirección de memoria de un dato o de otra variable que contiene al dato. Quiere esto decir, que el puntero apunta al espacio físico donde está el dato o la variable. Un puntero puede apuntar a un objeto de cualquier tipo, como por ejemplo, a una estructura o una función. Los punteros se pueden utilizar para referenciar y manipular estructuras de datos, para referenciar bloques de memoria asignados dinámicamente y para proveer el paso de argumentos por referencias en las llamadas a funciones.

Muchas de las funciones estándares de C, trabajan con punteros, como es el caso del scanf o strcpy. Estas reciben o devuelve un valor que es un puntero. Por Ej. A scanf se le pasa la dirección de memoria del dato a leer (esto es un puntero).

Declarando punteros

Ya se dijo que un puntero es una variable que guarda la dirección de memoria de otra variable, haciendo lógica a esto, decimos que un puntero se declara igual que cualquier otra variable, pero anteponiendo un * (asterisco) antes del nombre de la variable.

Su sintaxis seria:

tipo *Nombre Puntero;

Donde tipo es el tipo de dato al que referenciará este puntero, es decir, que si se necesita guardar la dirección de memoria de un dato int, se necesita un puntero de tipo int.

Explicación

Veamos el siguiente código:

#include <stdio.h> int main(){int a=0; //Declaración de variable entera de tipo enteroint *puntero; //Declaración de variable puntero de tipo enteropuntero = &a; //Asignación de la dirección memoria de a printf("El valor de a es: %d. \nEl valor de *puntero es: %d. \n",a,*puntero);printf("La dirección de memoria de *puntero es: %p",puntero); return 0;

Page 3: Apuntadores y direccionamiento

}

Características de los Apuntadores

Son variables que mantienen direcciones de memoria Poderosos para manejar datos, de manera no posible en otros lenguajes

Permiten el pasaje de parámetros por referencia.

Cuando se utilizan de manera incorrecta, son una fuente de bugs en los programas y frustración en el programador

Introducción

Al ejecutarse un programa, las variables se almacenan en memoria, cada una en su propia y única dirección o localidad. Las variables en general contienen un valor o dato. Por ejemplo cuando se declara:

int count = 5;

El valor "5" es almacenado en memoria y puede ser accedido usando la variable count

Un apuntador es un tipo especial de variable que en lugar de contener un valor o dato, contiene una dirección de memoria. Así como los datos pueden modificarse cuando se trabaja con una variable normal, el valor de la dirección almacenada en un apuntador también puede modificarse. Usualmente, la dirección almacenada en el apuntador es la dirección correspondiente a alguna otra variable del programa.

int *ptr;ptr = &count /* Guarda dirección de count en ptr */    /* el operador unario & retorna la dirección de la variable */

Para tomar el valor o contenido que es almacenado en la localidad de memoriam en el apuntador, es necesario de referenciar al apuntador. Esto se hace usando el operador unario

int total;total = *ptr;    /* The value in the address stored in ptr is assigned to total */

Page 4: Apuntadores y direccionamiento

La mejor manera de aprender apuntadores es con la practica y los ejemplos. Los apuntadores son un típico difícil. No se preocupen si el panorama no esta completamente claro todavía.

Ejemplo de Declaración e Inicialización

int main(){    int j;    int k;    int l;    int *pt1;    /* Declares an integer pointer */    int *pt2;    /* Declares an integer pointer */    float values[100];    float results[100];    float *pt3;    /* Declares a float pointer */    float *pt4;    /* Declares a float pointer */

    j = 1;    k = 2;    pt1 = &j;    /* pt1 contains the address of the variable j */     pt2 = &k;    /* pt2 contains the address of variable k */    pt3 = values;        /* pt3 contains the address of the first element of values */    pt3 = &values[0];        /* This is the equivalent of the above statement */

    return 0;}

Dereferenciacion de Apuntadores y Asignación de valores

La dereferenciacion permite la manipulación de los datos contenidos en la dirección de memoria guardada en el apuntador. El apuntador guarda una dirección de memoria. Al de referenciar un apuntador, podemos modificar los datos o valores contenidos en esa dirección de memoria. El operador unario , es utilizado para de referenciar a los apuntadores.

*pt1 =*pt1 + 2;

Esta instrucción suma dos al valor apuntado por pt1. Es decir, la instrucción suma dos a; contenido de la dirección de memoria almacenada por pt1. Entonces, a partir del programa principal, pt1 contiene la dirección de j. La variable "j" fue inicializada con 1. El efecto de la instrucción mostrada arriba es sumar 2 a j.

Page 5: Apuntadores y direccionamiento

El contenido de la dirección almacenada en un apuntador, puede ser asignado a otro apuntador o a otra variable

*pt2 = *pt1;        /* assigns the contents of the memory pointed to by pt1 */    /* to the contents of the memory pointer to by pt2; */k = *pt2;    /* assigns the contents of the address pointer to by pt2 to k. */

Aritmética de Apuntadores

  Parte del poder de los apuntadores proviene de la habilidad de hacer aritmética en los apuntadores mismos. Los apuntadores pueden ser incrementados, decrementados y manipulados utilizando expresiones aritméticas. Consideremos el apuntador "pt3" y el arreglo de float "values" declarados en el programa principal mostrado anteriormente:

pt3 = &values[0];    /* The address of the first element of "values" is stored in pt3*/ pt3++;    /* pt3 now contains the address of the second element of values */

*pt3 = 3.1415927;    /* The second element of values now has pie (actually pi)*/pt3 += 25;    /* pt3 now points to the 27th element of values */*pt3 = 2.22222;    / The 27th element of values is now 2.22222 */

pt3 = values;    /*pt3 points to the start of values, now */

for (ii = 0; ii < 100; ii++){     *pt3++ = 37.0;    /* This sets the entire array to 37.0 */}

pt3 = &values[0];    /* pt3 contains the address of the first element of values */pt4 = &results[0];    /* pt4 contains the address of the first element of results */

for (ii=0; ii < 100; ii++){    *pt4 = *pt3;        /* The contents of the address contained in pt3 are assigned to        the contents of the address contained in pt4 */     pt4++;

Page 6: Apuntadores y direccionamiento

     pt3++;}

 

Pasaje de Parámetros

Por valor: Los valores que se pasan se copian a los parámetros de la función, si el valor de un parámetro se modifica dentro de la función, no se altera su valor en el programa que lo llama.

Por Referencia: Permiten modificar dentro de la función el valor actual de la variable que fue pasada como parámetro. Es decir, el valor de la variable si se altera en el programa que llama.

Podemos distinguir entre parámetros formales y parámetros realesParámetros Formales: Aquellos que van en la definición de la función o un prototipo. Son como variables locales a la función que sirven de comunicación con el exterior. Parámetros Reales: Se colocan en la invocación de la función, pueden ser expresiones o variables del mismo tipo del parámetro formal correspondiente. Deben colocarse en la misma posición del parámetro formal correspondiente, respetando el número tipo y posición de los parámetros formales.

Ejemplo función Swap

void Swap(int x, int y) {

int temp ;temp = x;x = y;y = temp;

}

Con la invocación, Swap(a,b) ? no tendría ningún efecto, pues los parámetros se han pasado por valor. Es decir, no se afectan los valores de a y b.

Debe hacerse pasando los parámetros por referencia

void Swap(int *apx, int *apy) {

Page 7: Apuntadores y direccionamiento

int temp ;temp = *apx;*apx = *apy;*apyy = temp ;

}

main(){

int a = 20, b = 100;Swap(&a, &b); // Los valores de a y b se intercambian, a = 100, b = 20

}

Igual que cuando usamos un &, en la lectura de datos con scanf, igual de esta forma lo usamos acá, tal vez te acordaras que decíamos que las cadenas de caracteres (%s) no usaban este operador, esto es por que en una cadena de caracteres es un arreglo de caracteres, por lo que el primer carácter es la dirección de inicio de la cadena.

El operador *, nos permite acceder al valor de la dirección del puntero, en este caso nos permite acceder al valor que contiene a. De esta forma "a" y "*puntero" muestran el mismo dato, pero esto no quiere decir que sea lo mismo, uno es un entero el otro un puntero.

La impresión con %p, es para poder imprimir la dirección de memoria en valor hexadecimal (0x...), también podemos imprimir: ...%p",&a) y funciona de la misma forma, y es lógico que tanto a, como puntero tienen la misma dirección de memoria.

Diferentes direcciones

Tal vez se noto que en cada ejecución la dirección de memoria cambia, esto es por que es el sistema operativo es quien esta encargado de administrar la memoria y es este quien dice que espacios podrá tomar el programa.

Esto quiere decir que uno no puede asignarle una dirección de memoria a un puntero directamente, es decir yo no puedo hacer lo siguiente.

int *puntero=0xbfc5b1c8;

Page 8: Apuntadores y direccionamiento

Esto no puedo ni debo hacerlo ya que yo no se que esta haciendo esta dirección de memoria, si el sistema la tiene o no disponible, etc... Pero si puedo hacer esto:

int *puntero=NULL;

NULL, es el espacio en memoria con dirección 0, esto quiere decir que existe, lo que significa que le asignamos una dirección valida al puntero, pero el valor que tiene NULL no se nos permite modificarlo, ya que pertenece al sistema.

Operadores

Ya anteriormente se ponían algunos ejemplos de como asignar la dirección de memoria a un puntero y de como acceder al valor de este.

Operador de Dirección (&): Este nos permite acceder a la dirección de memoria de una variable.

Operador de Indireccion (*): Además de que nos permite declarar un tipo de dato puntero, también nos permite ver el VALOR que esta en la dirección asignada.

Incrementos (++) y Decrementos (--): Te darás cuenta que puedes usar un puntero como si de un array se tratase, es por esto que permite estos operadores.

De lo anterior podemos decir que:

int a; Es Igual int *puntero=&a; printf("%d",a); Es Igual printf("%d",*puntero);

Operaciones Aritméticas

Un puntero nos permite sumar o restar números enteros, pero su funcionamiento ahora es de posiciones, es decir nos permitirá movernos a la siguiente dirección de memoria.

A un puntero no se le puede realizar multiplicaciones, divisiones, sumas o restas con otro puntero ó con un valor de tipo coma flotante (float, double...). Esto es por que un puntero no es un valor es una dirección.

En un puntero se pueden realizar varias operaciones de tipo enteras, pero en dependencia de como se usen sus resultados pueden ser muy diferentes, a continuación les muestro algunas de estas operaciones:

Page 9: Apuntadores y direccionamiento

//Definamos estas variables:int x[100],b,*pa,*pb;//...x[50]=10; //Le asignamos el valor de 10, al arreglo #50pa=&x[50]; //Le asignamos al puntero pa, la dirección de memoria que tiene x[50] //Ahora mostramos algunas posibles operaciones: b = *pa+1; //Esto es como decir el valor que tiene el arreglo de x[50] sumarle 1. //Esto es igual a: b=x[50]+1; => Su valor seria igual a 11. b = *(pa+1); //Esto primero pasa a la siguiente dirección de memoria y luego lo referencia //El resultado es: b = x[51]; pb = &x[10]; //al puntero pb se le asigna la dirección de x[10] *pb = 0; //Al valor que tiene el puntero se le asigna 0 //Esto es igual que decir: x[10] = 0 *pb += 2; //El valor del puntero se incrementa en dos unidades, es decir x[10] = 2 (*pb)--; //El valor del puntero se decrementa en una unidad. x[0] = *pb--; //A x[0] se le pasa el valor de x[10] y el puntero pb, pasa a apuntar a x[9] //recorda, que -- es post-incremento, primero asignara y luego restara.

Punteros Constantes

Es posible que hayas pensado como declarar un puntero como una constante, tal vez pensaste en un #define, o en un atributo const. Bueno es posible usar el atributo const, pero para un puntero hay que hacerlo de otra forma.

FORMA ERRADA:

int a=10,b=20;const int *p = &a; //objeto constante y puntero variable*p = 15; // ERROR: el valor apuntado por p es constante.p=&b; //Correcto: p pasa a apuntar a un nuevo objeto.

Pero de esta forma no es muy útil declararlo, pues el que hicimos constante fue el valor al que apunte p, es decir, mejor hubiésemos hecho que el puntero fuese una constante.

Page 10: Apuntadores y direccionamiento

FORMA CORRECTA:

int a=10,b=20;int * const p = &a; //objeto variable y puntero constante*p = 15; // Correcto: El valor apuntado es variable.p=&b; //ERROR: p es constante.

Punteros Genericos

Un puntero a cualquier tipo de dato puede convertirse a un puntero del tipo void *. Por esto un puntero a void *, recibe el nombre de puntero genérico.

En C, se permite la conversión implícita de punteros, pero en C++ esto no es posible, asi que por compatibilidad y buena practica recomendamos usar la conversion explicita (cast).

Supongamos:

int *puntero;funcion (*puntero);....void funcion (void *p) int *q;q=(int *)p; //En C se podria hacer q = p;

Es decir que un puntero a void se puede usar sin importar el tipo de dato, recuerden que uno no puede trabajar con punteros que referencia a un tipo de dato diferente, como lo es un puntero a char, con un puntero a int.

Punteros y Matrices

Anteriormente decíamos que una matriz es una secuencia de espacios en memoria, que nos permitían alojar datos en cada uno y un puntero es una variable que guarda la dirección de memoria, también decíamos como recorre las direcciones de memoria con los operadores ++ y --.

Acá veremos como puede usarse un puntero como si de una matriz se tratase, luego de que veas que es técnicamente lo mismo, te preguntaras por que usar punteros, pero estos son muy necesarios y únicos que nos permiten realizar cosas que con un array normal, no se puede, como asignarle memoria dinámica, etc...

Page 11: Apuntadores y direccionamiento

#include <stdio.h> int main(){ int arreglo[10]={0,2,3,5,5,6,7,8,9,0}; //Declarar e inicializar un array.int *puntero = &arreglo[0]; //Le damos la dirección de inicio del arrayint i; //variable contadora... for (i=0;i<10;i++)printf("%d\n",*(puntero+i)); //imprimimos los valores del puntero. return 0;}

Se nota que usé *(puntero+i), así como explica la sección de operaciones aritméticas, pero también podemos acceder de otras maneras como lo son:

arreglo[i] => Accede como un arreglo normal

(arreglo+i) => Tambien accede como un arreglo normal.

puntero[i] => Accedemos al valor de puntero sub i

(puntero+i) => Accedemos al valor de puntero + 1

Punteros a cadenas de caracteres

Ya hemos visto el uso que se le puede dar a un puntero como si de un array se tratase, entonces usando esta misma lógica podemos hacer un arreglo de caracteres usando punteros.

char *nombre="Gustavo A. Chavarria";//Es como un arreglo de 20 caracteresprintf("%s",nombre);

Sin embargo al tratarse de una constante de caracteres no podemos modificarla luego de definir sus valores. Como por ejemplo no podemos remplazar un carácter, o leer un nuevo valor.

gets(nombre); //ERROR en ejecución

Para poder modificar el valor de este puntero, este tendría que apuntar a una dirección que no sea una constante, como un array.

char nombre[]="Gustavo A. Chavarria"; //declaramos un arreglo de caractereschar *puntero=nombre;//Asignamos al puntero el comienzo del array

Page 12: Apuntadores y direccionamiento

printf("%s \nIngrese otro nombre: ",puntero);//Escribimos en pantalla nombre...gets(puntero); //leemos otro nombreprintf("%s",puntero); //escribimos el nuevo nombre...

Esta vez pudiste notar que si se pudo remplazar el valor del nombre, pero aun la cantidad de caracteres esta limitada por el arreglo original, mas adelante veremos como solucionar esto con memoria dinámica.

Matrices de Punteros

Es muy probable que ya te hayas dicho: Si un puntero es una variable, ¿Puedo tener un arreglo de puntero? La respuesta seria NO Exactamente.

Como ya habíamos explicado antes, un puntero trabaja de la misma forma que un arreglo, tanto que podemos decir que un puntero es un arreglo, entonces si definimos un arreglo de un arreglo, nos daría como resultados un arreglo bidimensional.

En la practica es mucho mas común utilizar un arreglo de punteros que un arreglo bidimensional.

Hay que recordar que siguen siendo punteros, es decir solo apuntan a una dirección de memoria, por ende hay que asignarles la dirección de memoria a cada uno de los elementos del puntero. Sin embargo podemos asignar en un solo ciclo todas las filas.

Ejemplo:

#include <stdio.h> int main(){ int *puntero[5]; //Arreglo de punteroint a[5][5]; //Array bidimensional.int i; for (i=0;i<5;i++)puntero[i]=a[i]; //Asignamos las filas al puntero. //Pueden imprimir también en un ciclo//También pueden acceder mediante un ciclo anidado a la variables del puntero[i][j] }

Page 13: Apuntadores y direccionamiento

Punteros a Punteros

Es una variable que contiene la dirección de memoria de un puntero, el cual a su vez contiene la dirección de memoria de un tipo de dato. Recuerden que un puntero sigue siendo un espacio en memoria, pero en vez de almacenar un valor almacena una dirección.

Si decimos que:

int a=0; //Supongamos que es una variable cuya dirección es 0x1601int *puntero1=&a; //El puntero tiene guardada la dirección de a, //pero este tiene como dirección 0x1890int **puntero2=&puntero1; //**puntero 2 guarda la dirección 0x1890

Ahora se entiende mejor. Al uso de punteros se le llama variables con niveles de Indireccion, ya que no apuntan directamente a un valor, sino que apuntan a alguien que lo tiene. Basándonos en esto podemos decir que *puntero1 es un puntero con un nivel de Indireccion y *puntero2 es un puntero con dos niveles de Indireccion.

En general estos tipos de datos son usados con matrices multidimensionales, es por esto que decidí no dejar ejemplos, pues su uso es bastante fácil de implementar.

Matrices de punteros a cadenas de caracteres

No hablaremos sobre este tema, debido a que es prácticamente una matriz de caracteres que se usa mediante punteros, y esto es técnicamente lo mismo que hemos estado viendo anteriormente.

También podemos usar punteros a punteros y guardar múltiples cadenas.

Ejemplos

Tenga en cuenta el siguiente bloque de código que declara 2 punteros

/*1*/ struct MyStruct {/*2*/ int m_aNumber;/*3*/ float num2;/*4*/ };/*5*/ /*6*/ int * pJ2;/*7*/ struct MyStruct * pAnItem;

Las primeras 4 líneas definen la estructura. La línea 6 declara una variable que apuntará a un entero, y la línea 7 declara una variable que apunta a algo de la

Page 14: Apuntadores y direccionamiento

estructura MyStruct. Entonces declarar un puntero es algo que apunta a algo de algún tipo, más que contener el tipo. El asterisco (*) se coloca antes del nombre de la variable.

En las siguientes líneas de código, var1 es un puntero a un entero largo (long) mientras var2 es un entero largo (long) y no un puntero a un entero largo. En la segunda línea se declara p3 como un puntero a un puntero de un entero.

long * var1, var2; int ** p3;

Los punteros se usan habitualmente como parámetros de funciones. El siguiente código muestra como declarar una función que usa un puntero como argumento. Teniendo en cuenta que C pasa todos los argumentos por valor, para poder modificar un valor desde el código de la función, se debe usar un puntero al valor a modificar. También se usan punteros a estructuras como argumentos de una función aún cuando la estructura no sea modificada dentro de la función. Esto es realizado para evitar copiar todo el contenido de la estructura dentro de la pila.

int MyFunction( struct MyStruct *pStruct );

Asignando valores a punteros

Continuamos con el proceso de asignar valores a los punteros, para esto utilizamos el operador & o 'la dirección de'.

int myInt; int *pPointer; struct MyStruct dvorak; struct MyStruct *pKeyboard; pPointer = &myInt; pKeyboard = &dvorak;

Aquí, pPointer apuntara a myInt y pKeyboard apuntara a dvorak.

Los punteros también pueden ser asignados a referencias de memorias dinámicas creadas comúnmente por las funciones malloc() y calloc().

#include <stdlib.h> ... struct MyStruct *pKeyboard; ... pKeyboard = malloc(sizeof(struct MyStruct)); ...

Page 15: Apuntadores y direccionamiento

La función malloc retorna un puntero de memoria asignada de manera dinámica (o NULL si falla). El tamaño de esta memoria es definido de modo que pueda contener la estructura MyStruct

El siguiente código es un ejemplo mostrando un puntero siendo asignado a una referencia y se retorna el valor del puntero en la función.

static struct MyStruct val1, val2, val3, val4;...struct MyStruct *ASillyFunction( int b ){ struct MyStruct *myReturn; if (b == 1) myReturn = &val1; else if (b==2) myReturn = &val2; else if (b==3) myReturn = &val3; else myReturn = &val4; return myReturn;} ... struct MyStruct *strPointer; int *c, *d; int j; ... c = &j; /* puntero asignado usando el operador & */ d = c; /* asignando un puntero a otro */ strPointer = ASillyFunction( 3 ); /* puntero retornado de la función */

Cuando se retorna un puntero de una función, se tiene que tener cuidado de que el valor apuntado no sea de una referencia de una variable local a la función, porque estos valores desaparecen después de salir de la función y el puntero estaría mal referenciado, causando errores en tiempo de ejecución en el programa. La memoria creada dinámicamente dentro de la función puede devolverse como puntero, el valor de retorno aunque es creado en una variable local la dirección como tal se devuelve por valor dejando esta información valida.

Direccion 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.

Page 16: Apuntadores y direccionamiento

Modos de direccionamiento

Los llamados modos de direccionamiento son las diferentes maneras de especificar en informática un operando dentro de una instrucción en lenguaje ensamblador.

Un modo de direccionamiento especifica la forma de calcular la dirección de memoria efectiva de un operando mediante el uso de la información contenida en registros y / o constantes, contenida dentro de una instrucción de la máquina o en otra parte.

Los modos de direccionamiento podrían clasificarse en:

Propios e impropios: en los direccionamientos propios el operando está localizado en una dirección concreta de memoria, mientras que en los impropios en otros lugares tales como los registros del procesador, etc. Entre los modos de direccionamiento descritos en los párrafos siguientes, los tres primeros pueden considerarse impropios, mientras que los demás son direccionamientos propios.

Direccionamiento implícito: En este modo, llamado también inherente, el operando se especifica en la misma definición de la instrucción. El modo implícito se usa para hacer referencia a operandos de dos tipos:

Registros: En el caso de que el código de operación se refiera en particular a un registro.

Operandos en la pila: En el caso de que la operación se realice siempre sobre el dato situado en la cima de pila. El primer caso es típico de las organizaciones de un solo acumulador. Generalmente en un ordenador de este tipo todas las instrucciones que actúan sobre el acumulador utilizan direccionamiento implícito.En el segundo caso están la mayoría de las instrucciones de los ordenadores con organización de pila. Estas operaciones llevan implícitos los operandos que son los elementos de la cima de pila. Esto se debe a que en este tipo de máquinas la mayoría de las operaciones no tienen campos de dirección. También están en este caso las instrucciones PUSH y POP de la mayoría de los ordenadores cuyo operando implícito también es, como en el caso anterior, la cima de pila.

Direccionamiento inmediato (o literal): En este modo es el operando el que figura en la instrucción no su dirección. En otras palabras el campo de operando contiene él mismo, sin transformación alguna, la información sobre la que hay que operar. Este modo es útil para inicializar registros o palabras de memoria con un valor constante.

Direccionamiento directo por registro: Se mencionó anteriormente que el campo de dirección de una instrucción puede especificar una palabra de memoria o un registro del procesador. Cuando se da este último caso se dice que el operando está especificado con direccionamiento directo por registro, en tal caso,

Page 17: Apuntadores y direccionamiento

el operando reside en uno de los registros del procesador que es seleccionado por un campo deregistro de k bits en la instrucción. Este campo de k bits puede especificar uno de2 k registros.

Este modo es típico de los ordenadores con organización de registros de uso general.

Ejercicios:

1) _ Crear un programa que mediante un menú admita reservar o cancelar asientos de un avión, así como mostrar qué asientos están ocupados y libreas actualmente.

El array tendrá 10 filas y 4 columnas

Solucion:

#include <conio.h>

#include <stdio.h>

#define N_FILAS 10

#define N_COLUMNAS 4

#define LIBRE 0

#define OCUPADO 1

/* Función que muestra el menú del programa y retorna

el número de menú elegido por el usuario */

int mostrarMenu();

void reservarAsiento(int a[][N_COLUMNAS]);

void rechazarAsiento(int a[][N_COLUMNAS]);

void mostrarOcupacion(int a[][N_COLUMNAS]);

int main(){

clrscr();

/* Los asientos inicialmente están libres*/

Page 18: Apuntadores y direccionamiento

int asiento[N_FILAS][N_COLUMNAS]={0};

int resp=mostrarMenu();

while(resp!=4){

switch(resp){

case 1:

reservarAsiento(asiento);

break;

case 2:

rechazarAsiento(asiento);

break;

case 3:

mostrarOcupacion(asiento);

break;

}

resp=mostrarMenu();

}

getch();

}

int mostrarMenu(){

int resp;

printf("\n\n");

printf("1) Reservar Asiento\n");

printf("2) Rechazar Asiento\n");

printf("3) Mostrar Ocupacion\n");

printf("4) Salir\n");

Page 19: Apuntadores y direccionamiento

scanf("%d",&resp);

return resp;

}

void reservarAsiento(int a[][N_COLUMNAS]){

int fila, col;

printf("Se encuentran disponibles solo 9 filas y 4 columnas de asientos\n");

printf("Escriba la fila del asiento a reservar: ");scanf("%d",&fila);

printf("Escriba la columna del asiento a reservar: ");scanf("%d",&col);

if(a[fila][col]==OCUPADO) printf("Ese asiento ya está reservado\n");

else {

a[fila][col]=OCUPADO;

printf("Reserva realizada\n");

}

}

void rechazarAsiento(int a[][N_COLUMNAS]){

int fila, col;

printf("Escriba la fila del asiento a cancelar: ");scanf("%d",&fila);

printf("Escriba la columna del asiento a cancelar: ");scanf("%d",&col);

if(a[fila][col]==LIBRE) printf("Ese asiento no está ocupado\n");

else {

a[fila][col]=LIBRE;

printf("Cancelación realizada\n");

}

}

void mostrarOcupacion(int a[][N_COLUMNAS]){

Page 20: Apuntadores y direccionamiento

int i,j;

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

for(j=0;j<N_COLUMNAS;j++){

printf("fila %d, columna %d ",i,j);

if(a[i][j]==LIBRE) printf("Libre\n");

else printf("Ocupado\n");

}

}

}

2) _ Crear un programa que cree un array con 1000 letras mayúsculas aleatorias y que cuenta cuántas veces aparece cada letra en el array

Solucion:

#include <conio.h>

#include <stdio.h>

#include <stdlib.h>

#define TAM 1000

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

int main(){

char letra[TAM];

/* Array que contendrá las letras de la A a la Z*/

char AaZ[26];

/* contadores*/

int i,j,cont;

srand(time(NULL));

/* Relleno del array de letras */

for(i=65;i<=90;i++){

Page 21: Apuntadores y direccionamiento

AaZ[i-65]=i;

}

/*Relleno inicial del array aleatorio*/

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

/*genera caracteres del ASCII 65 al 90,

de la letra A a Z */

letra[i]=rand()%26+65;

}

/* Escritura del contenido del primer array*/

printf("Array inicial:\n");

escribirArray(letra,TAM);

/* El bucle recorre el array que contiene las letras de la A

a la Z y cuenta cada vez que aparece cada letra en el array de letras

aleatorias*/

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

cont=0;

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

if(AaZ[i]==letra[j]) cont++;

}

printf("La letra %c aparece %i veces\n",65+i,cont);

}

getch();

}

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

Page 22: Apuntadores y direccionamiento

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

int i;

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

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

}

printf("\n");

}