Descargar

PARTE II:

 

    MEMORIA DIN�MICA

    Cap�tulo VII: Memoria Principal y Aritm�tica de Punteros

    Al iniciar �sta segunda parte, vamos a comentar algunos aspectos que son de suma importancia y que el lector debe conocer y manejar, como lo es el uso y funcionamiento de la memoria, dentro de la computadora.

    Debemos iniciar diciendo que, La Memoria Principal; se compone de un conjunto de celdas b�sicas dotadas de una determinada organizaci�n.

    Ya que, la Memoria, es el lugar donde se guardan datos y programas.

    Tipos de Memoria

    La Memoria RAM

    Es aquella memoria que �se volatiliza� al apagar el equipo. A mayor cantidad de RAM, m�s ventanas se pueden abrir, m�s programas funcionando simult�neamente y menos bloqueos de la PC. Existen varios tipos de RAM, seg�n su forma de encapsulado.

    M�DULOS DIP (Dual Inline Package): eran chips de memoria de forma rectangular y chata. Presentaban dos l�neas de pines en sus laterales. Una muesca o punto sobre el chip indicaban cu�l es la pata n� 1 para evitar colocar el chip al rev�s en el z�calo de la mother. Hoy no se utilizan memorias RAM en formato DIP, pero s� todav�a como cach� en motherboards u otras tarjetas.

    M�DULOS SIP (Single Inline Package): se trataba de m�dulos de memoria RAM cuyos chips de memoria se encontraban soldados sobre una peque�a placa de circuito impreso que hac�a contacto con la motherboard con una sola hilera de pines soldados en uno de sus bordes. Los pines calzaban en un z�calo colocado en la mother.

    M�DULOS SIMM (Single Inline Memory Module): son m�dulos de memoria que tambi�n tienen una sola hilera de pines. Una peque�a placa de circuito tiene soldada en una o ambas caras varios chips de memoria. Estos m�dulos de memoria se presentan en dos versiones. Existen:

    -SIMM de 30 pines: organizan la cantidad total de memoria en renglones de a 8 bits. (Mother 486)

    -SIMM de 72 pines: organizan la cantidad total de memoria en renglones de a 32 bits. (Mother 486 o Pentium)

    M�DULOS DIMM (Double Inline Memory Module): similares a los SIMM, aunque poseen 168 pines y organizan la memoria en renglones de a 64 bits. Hay m�dulos DIMM de 168 pines para 16, 32, 64, 128, 256 y hasta 512 MBytes. (Mother Pentium o Pentium II en adelante).

    M�DULOS DDR (Double Data Rate Synchronous DRAM): esta tecnolog�a transmite al doble de la velocidad del bus del sistema. Estas memorias se presentan en forma de m�dulos de 184 contactos o pines.

    Z�calos y Bancos

    Un banco es un conjunto de z�calos para insertar chips individuales (como los DIP, o SIP), o m�dulos de memoria RAM (SIMM de 30, SIMM de 72 o DIMM de 128 pines).

    Una motherboard posee m�s de un banco de memoria para agregar m�s memoria a la m�quina sin tener que retirar la que estaba instalada. Cada banco de memoria puede poseer 1, 2 � 4 z�calos.

    Un banco organiza la cantidad total de memoria en renglones sucesivos seg�n el ancho del bus de datos del microprocesador. Por ejemplo, en un Intel 486 (bus de datos de 32 bits), para colocar memorias en los bancos deben respetarse las siguientes reglas:

    1.- Un banco de memoria debe tener en todos sus z�calos la misma cantidad de m�dulos.

    2.- Debe llenarse primero el banco 0, luego el banco 1, y as� sucesivamente (excepto si la motherboard posee autobanking).

    3.- Un banco debe tener m�dulos de la misma velocidad. No se puede colocar una memoria SIMM de 60 nanosegundos junto con otra de distinta velocidad.

    Memoria Cach�

    Estas memorias son de tipo est�ticas. Son muy veloces (10 ns) y tambi�n caras, ya que su proceso de fabricaci�n es mucho m�s complejo. Con una memoria cach� el micro lee una direcci�n de memoria y mientras procesa la informaci�n el cach� lee las restantes posiciones de memoria principal consecutivas. Cuando el micro necesite leer la pr�xima direcci�n de memoria, su contenido se encontrar� en cach�. De esta manera, se acelera mucho la velocidad de procesamiento.

    Una declaraci�n de variable como:        

    int var;

    produce una asociaci�n entre el nombre 'var' y un espacio de almacenamiento en memoria. Por lo tanto hay dos elementos relacionados con el nombre 'var': un valor que se puede almacenar alli y una direcci�n de memoria para la variable, algunos autores se refieren a estos dos aspectos como el "rvalue" y "lvalue" de la variable. Adem�s del identificador "var", tenemos la palabra "int" que nos indica el TIPO (type) de la variable. El tipo nos indica: 1-CUANTAS CELDAS DE MEMORIA (bytes) se asocian a ese nombre de variable. 2-DE QUE MODO SERAN INTERPRETADOS  los datos que se encuentren en tal localidad de memoria, 1-Un byte es la menor unidad de informaci�n que pueden direccional la mayor�a de las computadoras. En la mayor�a de las arquitecturas el tipo char ocupa un solo byte, por lo tanto es la unidad m�nima. Un bool admite solo dos valores diferentes, pero es almacenado como un byte. El tipo integer ocupa generalmente 2 bytes, un long 4, double 8, y asi con el resto de los tipos. 2-El otro punto es la relaci�n entre LO QUE HAY en una celda de memoria y COMO ES INTERPRETADO. Lo que hay en un celda cuya extensi�n es un byte es simplemente un conjunto de ocho estados posibles (8 bits) que a nivel hardware admiten dos estados diferenciales, estados que pueden ser interpretados como 'verdadero / falso', 0/1, o cualquier otro par de valores. Una celda de memoria del sector de datos, podr�a contener algo como lo siguiente:

    Que es esto? Depende en gran parte del TIPO (type) que hayamos asociado a esa celda (y suponiendo que exista tal asociaci�n). Ese valor interpretado como un Hexadecimal es 0x61, en decimal es 97, y si fue asociada al tipo char representara la letra 'a', cuyo ASCII es igual a 97. En ninguna localidad de memoria hay algo como la letra 'a', lo que encontramos son valores binarios que en caso de estar asociados a char y en caso de que lo saquemos en pantalla como char hara que veamos encendidos ciertos pixeles de pantalla, en los cuales reconoceremos una representaci�n de la letra 'a'. La representaci�n binaria de datos ocupa demasiado espacio, por ese motivo es preferible utilizar el sistema Hexadecimal, ademas de ser muy f�cil de traducir a binario es mas econ�mico que este o el decimal. Observar los bytes de un sector de memoria de un programa facilita la comprensi�n sobre el modo en que cada tipo (type) se asocia a direcciones de memoria. Supongamos un programa que declara, define e inicializa las siguientes variables:

    Algunos Conceptos Importantes

    BIT: Acr�nimo del ingl�s "Binary Digit", o "D�gito Binario" es decir, cada uno de los 0 (ceros) o 1 (unos) que utiliza el sistema de numeraci�n en base 2. Esta es la forma en la que ordenador procesa toda la informaci�n.

    Byte: Tambi�n llamado "octeto", es el conjunto de 8 bit (ceros y unos) que se necesitan para codificar un car�cter, al ser un valor muy peque�o, se utilizan sus m�ltiplos: kilobyte, megabyte, gigabyte…

    Palabra: Unidad direccionable formadas por bits. Desde 8 bits (1 byte) hasta 64 bits (8 bytes).

    ASCII: Siglas de "American Standard Code for Informati�n Interchange", o c�digo est�ndar americano para el intercambio de informaci�n. El conjunto b�sico de caracteres ASCII comprende s�lo las letras (del alfabeto ingl�s) y carece de acentos, de letras no inglesas (como la �) y de formatos (negrita, cursiva…), pero existen conjuntos de caracteres m�s amplios.

    Unicode: Han Ampliado a 16 bits, que se usan. Se puede representar todos los s�mbolos de los lenguajes de programaci�n.

    BCD: (Binary Code Decimal) se utilizan s�lo 4 bits para representar las letras, los d�gitos y otros s�mbolos. Estos grupos de 4 bits se llaman nibble.

    Ejemplo:

    0=0000

    2=0010

    9=1001

    Apuntadores

    Una de las cosas m�s dif�ciles que encuentran los principiantes en C es entender el concepto de

    apuntadores. Me he encontrado a menudo que la principal raz�n por la que los principiantes tienen problemas con los apuntadores es que tienen una muy pobre o m�nima concepci�n de las variables, (del modo en que C hace uso de ellas). As� que comencemos con una discusi�n sobre las variables de C en general. Una variable en un programa es algo con un nombre, que contiene un valor que puede variar. El modo en que el compilador y el enlazador (linker) manejan esto es que asignan un bloque espec�fico de la memoria dentro de la computadora para guardar el valor de una variable. El tama�o de este bloque depende del rango en que a

    esta variable le es permitido variar. Por ejemplo, en PC�s de 32 bits, el tama�o de una variable de tipo entero (int) es de 4 bytes, en una m�quina antigua de 16 bits los enteros tienen un tama�o de 2 bytes. En C el tama�o de un tipo de variable como una de tipo entero no tiene porqu� ser el mismo en todos los tipos de m�quinas. Es m�s en C disponemos de diferentes tipos de variables enteras, est�n los enteros largos (long int) y los enteros cortos (short int) sobre los que puedes averiguar en cualquier texto b�sico sobre C. El presente documento asume que se est� usando un sistema de 32 bits con enteros de 4 bytes.

    Recordemos, el tama�o de los tipo de datos usados en el lenguaje C:

    TABLA CON LOS TIPOS DE DATOS PREDEFINIDOS EN C

    >ENTEROS: numeros completos y sus negativos

    Palabra reservada:

    Ejemplo

    Tama�o (byte)

    Rango de valores

    Int

    -850

    2

    -32767 a 32767

    VARIANTES DE ENTEROS

    short int

    -10

    1

    -128 a 127

    unsigned int

    45689

    2

    0 a 65535

    long int

    588458

    4

    -2147483648 a 2147483647

    unsigned long

    20000

    4

    0 a 4294967295

    >REALES: n�meros con decimales o punto flotante

    Palabra reservada:

    Ejemplo

    Tama�o (byte)

    Rango de valores

    Float

    85

    4

    3.4×10-38 a 3.4×1038

    VARIANTES DE LOS REALES

    Double

    0.0058

    8

    1.7×10-308 a 1.7×10308

    long double

    1.00E-07

    10

    3.4×10-4932 a 1.1×104932

    >CAR�CTER: letras, digitos, s�mbolos, signos de puntuaci�n.

    Palabra reservada:

    Ejemplo

    Tama�o (byte)

    Rango de valores

    Char

    'O'

    1

    0 ……255

    Como ya se ha dicho, cuando hacemos una declaratoria, como int a;

    Le estamos indicando a la computadora la cantidad de almacenamiento (tama�o, que para el caso es de 2 bytes), adem�s indica c�mo se va a interpretar los datos guardados en ese lugar de la memoria (en nuestro caso, enteros).

    Y es a qui donde radica la importancia, de lo punteros, por que como ya se dijo, los Punteros o Apuntadores, son variables que contienen la direcci�n de otra variable.

    En forma gr�fica, la podemos representarlo, como se muestra en la figura de arriba. P, es apuntador hacia el entero identificado como "a", en el cual, se reservan dos bytes, para guardar el entero. 3B, es la direcci�n de la variable. (NOTA: "3B", es s�lo por decir un dato, para que el lector tenga la idea de una direcci�n de memoria).

    Cabe mencionar que, un puntero, es una variable q termina con la direcci�n con la que comienza la variable a la que apunta:

    Los usos principales, que tienen, los punteros, son los siguientes:

    ->Nos ayuda, para que una funci�n devuelva m�s de un valor. Por ejemplo, una funci�n que devuelva un vector de enteros, en dicha funci�n mandamos la direcci�n del primer elemento a la funci�n principal, y a partir de ella, imprimimos todos los valores contenidos en el vector.

    ->Mejor uso de la memoria din�mica. Esto es lo que m�s nos tiene cuenta, el lector debe tener presente que, el uso de punteros, ayuda a ahorrar memoria y por consiguiente, hace m�s efectivo el uso y administraci�n de la misma.

    La forma de declarar un apuntador, es la siguiente:

    Int *p;

    Int->indica que, es un puntero hacia un entero.

    *->indica al compilador que esa variable, es un puntero

    p-> Es el identificador del puntero.

    Otros ejemplos:

    float *q; /*apuntador hacia un flotante*/

    char *z; /*puntero que contiene la direcci�n de una variable que guarda un car�cter */

    Para referirnos a un valor a trav�s de un apuntador, lo hacemos mediante un proceso llamado indirecci�n.

    Por ejemplo, para mandar a impresi�n el valor entero, hacia el cual a punta "p", ser�a as�:

    printf("%d", *p);

    Los punteros, pueden ser inicializados a 0, NULL � alguna direcci�n v�lida:

    float *p1;

    p1=0;

    p1=NULL;

    Ahora bien, para guardar la direcci�n de alguna variable, en un puntero, debemos conocer un nuevo amigo:

    &->Operador de Direcci�n.

    P1=&a;

    Con esa simple declaraci�n, estamos indic�ndole al compilador que p1, contendr� le direcci�n de memoria de a.

    Veamos algunos ejemplos:

    Ejemplo 7.1

    Dise�e un programa que muestre el uso de operadores b�sicos en la declaraci�n de punteros empleando el direccionamiento y el operador indirecci�n.

    #include <stdio.h>

    #include <conio.h>

    main()

    {

    int a;

    /*Declaraci�n de un puntero a un entero */

    int *p;

    clrscr();

    printf("Ingrese un valor ENTERO para la variable:n");

    scanf("%d", &a);

    while(a<0)

    {

    printf("ERROR, el valor debe ser mayor que cero:n");

    scanf("%d", &a);

    }

    clrscr();

    /*Limpiamos la pantalla */

    printf("a=%dn", a); /*Imprimo el valor de a*/

    printf("La direcci�n de a es %pn", &a);

    printf("*p=%pn", p); /*Imprimo la direcci�n que guarda p*/

    /*imprimo el valor guardado en la direccion a la que apunta p*/

    printf("a=%dn", *p);

    printf("El tama�o de *p es %dn", sizeof(p));

    getch();

    return 0;

    }

    Explicaci�n:

    Como ya se ha explicado, la forma de declaraci�n de un puntero consiste en colocar al tipo de dato al cual apunta, el operador asterisco (*), seguido del identificador del mismo. Debo hacer notar que, para mandar a impresi�n una direcci�n de memoria, debo hacerlo usando la expresi�n "%p", para lo cual es indistinto si mando directamente la direcci�n de memoria, haciendo uso del operador & (ejemplo: &a), o si le mando, a impresi�n el contenido de la variable apuntador. (ejemplo: "*p=%pn", p).

    La funci�n sizeof como su nombre lo indica, se utiliza para determinar el tama�o (en bytes) de alguna variable en espec�fico.

    Ejemplo 7.2

    Dise�e un programa, que sume dos variables de tipo entero, por medio de apuntadores.

    #include <stdio.h>

    #include <conio.h>

    main()

    {

    int a, b, c;

    int *p1, *p2, *p3; /*declaracion de los punteros */

    printf("Ingrese el valor de a:n");

    scanf("%d", &a);

    printf("Ahora el valor de b:n");

    scanf("%d", &b);

    c=a+b;

    printf("a+b=%dn", c);

    /*asiganamos las direcciones a los punteros correspondientes/

    p1=&a;

    p2=&b;

    /*ahora desreferenciamos el valor de los punteros para sumarlos */

    printf("*p1 + *p2=%dn", *p1+*p2);

    p3=&c;

    printf(" Direcci�n de a es %pn Direccion de b es %pn Y la de c es %pnn", p1, p2, p3);

    getch();

    return 0;

    }

    con esta instrucci�n: *p1+*p2, estamos invocando el valor guardado en la direcci�n a la que apunta p1 y p2, por tanto se obtiene el mismo resultado, que si sum�ramos directamente las dos variable (a+b).

    Ejemplo 7.3

    Programa que, asigna valores a punteros y juega un poco con las direcciones y las desrefenciaciones.

    #include <stdio.h>

    #include <conio.h>

    main()

    {

    int a, b,c, *p1, *p2;

    void* p; /*puntero que apunta a void*/

    p1=&a;/*guarda la direcci�n de a*/

    *p1=1; /* a donde apunta p1, guarda el valor de uno */

    p2=&b;

    *p2=2;

    p1=p2; /*En p1, guarda la direccion a la que apunta p2*/

    *p1=0;

    p2=&c;

    *p2=3;/*a donde apunta p2, le asigno 3*/

    printf("a =%dn b =%dn c =%dnn", a,b,c);

    p=&p1; /*p contiene la direccion de p1*/

    p=p2;

    *p1=1;/*es como asignarle a C, el valor de 1*/

    /*con los cambios realizados imprimimos el nuevo

    valor de las variables */

    printf("a =%dn b =%dn c =%dnn", a,b,c);

    getch();

    return 0;

    }

    La salida que presenta este c�digo, es la siguiente:

    Punteros y Arreglos

    Si mat es un arreglo unidimensional, la direcci�n del primer elemento puede ser expresada tanto como &mat[0] o simplemente como mat.

    La direcci�n del elemento (i+1) se puede expresar como (mat+i), donde mat, representa la rireci�n del arreglo e "i", la posici�n espec�fica a la cual nos referimos.

    Si &mat[i] y (mat +i) representan la direcci�n del i-�simo elemento de mat, mat[i] y *(mat+i), representa el contenido de esa direcci�n.

    Por ejemplo, supongamos que tenemos el siguiente arreglo:

    Int mat[]={2, 16, -4, 29, 234, 12, 0, 3}

    Tenemos un arreglo de 8 posiciones, de las cuales ya hemos aprendido que, para referirnos a cada uno de esos valores, lo podemos hacer usando sub�ndices. As�:

    Mat[0]=2;

    Mat[1]=16;

    pero podemos acceder a ellos de un modo alternativo, usando punteros de la siguiente manera:

    int *ptr;

    ptr=&mat[0];

    y de esa manera, podemos imprimir los valores de nuestro arreglo, ya sea usando la notaci�n de arreglos o desreferenciando nuestro puntero.

    Ejemplo 7.4

    Utilice un puntero para imprimir los valores de un puntero de 6 posiciones.

    #include <stdio.h>

    #include <conio.h>

    main()

    {

    int mat[]={2,3,5,9,10,4};/*declaracion del arreglo*/

    int i;

    int *ptr;

    ptr=&mat[0];/*al puntero ptr, le asignamos la direccion del primer

    elemento del arreglo*/

    clrscr();

    printf("tttElementos del arreglo:nnn");

    for(i=0; i<6; i++)

    {

    printf("mat[%d]=%dn", i, mat[i]); /*notacion de arreglos*/

    printf("ptr+%d=%dn", i, *(ptr+i));/*notacion de punteros*/

    }

    getch();

    return 0;

    }

    ahora, supongamos que, lo que tenemos es un arreglo multidimensional, entonces, podemos acceder a el mediante la siguiente sintaxis:

    multi[renglon][columna]

    *(*(multi + renglon) + columna)

    Para entender mejor lo que sucede, reemplacemos *(multi + renglon) con una X, tal que la expresi�n nos quede como *(X + columna) Ahora vemos que esta X es como un apuntador ya que la expresi�n se encuentra desreferenciada y sabemos que col es un entero. Aqu� la aritm�tica a utilizar es de un tipo especial llamada "aritm�tica de punteros". Eso

    significa que, ya que hablamos de un arreglo de enteros, la direcci�n a ser apuntada por (el valor de) X + columna + 1 debe ser mayor que la direcci�n de X + columna por una cantidad que es igual a sizeof(int) (el

    tama�o del tipo de dato entero). Ya que sabemos la estructura de memoria para arreglos bidimensionales, podemos determinar que en la

    expresi�n multi + renglon como se hizo arriba, multi + renglon + 1 hace que esto se incremente por un valor igual al necesario para "apuntar a" el siguiente rengl�n, lo cual ser�a entonces COLUMNAS * sizeof (int).

    Esto nos dice que si la expresi�n *(*(multi + renglones) + columnas) va a ser evaluada correctamente en tiempo de ejecuci�n, el compilador debe generar c�digo que tome en consideraci�n el valor de COLUMNAS, es decir, la segunda dimensi�n. Debido a la equivalencia entre las dos formas de expresi�n, esto se hace cierto ya sea que usemos la sintaxis de punteros o la de arreglos multi[renglon][columna].

    As� que para evaluar cualquiera de estas dos expresiones, se deben conocer 5 valores:

    1. La direcci�n del primer elemento del arreglo, la cual es conocida por la expresi�n multi, es decir, el

    nombre del arreglo.

    2. El tama�o y el tipo de los elementos que conforman el arreglo, en este caso sizeof(int).

    3. La segunda dimensi�n del arreglo.

    4. El valor espec�fico del �ndice para la primera dimensi�n, renglon en este caso.

    5. El valor espec�fico del �ndice para la segunda dimensi�n, columna en este caso.

    Una vez que conocemos esto, consideremos el problema de dise�ar una funci�n que manipula los elementos de un arreglo previamente declarado. Por ejemplo, uno que establecer�a un valor de 1 todos los elementos del

    arreglo multi.

    void set_value(int m_arreglo[][COLUMNAS])

    {

    int renglon, columna;

    for (renglon = 0; renglon < RENGLONES; renglon++)

    {

    for (columna = 0; columna < COLUMNAS; columna++)

    {

    m_arreglo[renglon][columna] = 1;

    }

    }

    }

    Y para llamar a esta funci�n usar�amos entonces:

    set_value(multi);

    Dentro de esta funci�n, hemos usado los valores establecidos por #define en RENGLONES y COLUMNAS, los

    cuales establecen los l�mites para los ciclos. Pero dentro de lo que le concierne al compilador, estas son

    simples constantes, es decir, no hay nada que les relacione directamente con el tama�o del arreglo dentro de la

    funci�n. renglon y columna son variables locales, por supuesto. La definici�n formal del par�metro le permite

    al compilador determinar las caracter�sticas del valor del puntero que ser� pasado en tiempo de ejecuci�n.

    Realmente no necesitamos la primera dimensi�n y, como veremos m�s adelante, habr� ocasiones en las que

    preferiremos no declararla en la definici�n de los par�metros de una funci�n, no quiere decir que eso se vuelva un h�bito o algo con lo que haya que ser consistente. Pero la segunda dimensi�n debe ser usada como se ha mostrado en la expresi�n del par�metro. La raz�n por la que la necesitamos es por la forma de la evaluaci�n de m_arreglo[renglon][columna]. Mientras que el par�metro define el tipo de datos (int en este caso) y las variables autom�ticas para rengl�n y

    columna son definidas en los ciclos for, s�lo un valor puede ser pasado usando un �nico par�metro. En este caso, es el valor de multi, como lo pasamos en la llamada a la funci�n, es decir, la direcci�n de su primer

    elemento, m�s bien referido como un apuntador al arreglo. As� que la �nica manera que tenemos de informar al compilador de la segunda dimensi�n es incluirlo expl�citamente en la definici�n del par�metro de la funci�n. De hecho y por lo general todas las dimensiones de orden mayor que uno, necesitan de arreglos multidimensionales. Esto significa que si hablamos de arreglos de 3 dimensiones, la segunda y tercera dimensiones deben estar especificadas en la definici�n del par�metro.

    Ejemplo 7.5

    /*Programa que lee un arreglo y una matriz usando

    aritm�tica de punteros */

    #include <stdio.h>

    #include <conio.h>

    #define M 3

    #define N 3

    main()

    {

    int x[3][3], y[3];

    int f=0,c=0;

    clrscr();

    for(f=0; f< M; f++)

    {

    for(c=0; c<N; c++)

    {

    printf("*(x+ %d)+%d)=", f,c);

    scanf("%d", *(x+f)+c);

    }

    printf("Elemento %d del vector:n", f);

    scanf("%d", &y[f]);

    }

    printf("IMPRESIONES:n");

    printf("*** MATRIZ ***n");

    for(f=0; f<M; f++)

    for(c=0; c<N; c++)

    printf("%d", *(*(x+f)+c));

    printf("n*** VECTOR ***n");

    for(f=0; f<M; f++)

    printf("%d", *(y+f));

    getch();

    return 0;

    }

    Ejemplo 7.6

    Uso de funciones

    #include <stdio.h>

    #include <conio.h>

    void generacion(int a[3][3], int b[3][3]);

    main()

    {

    int a[3][3]={1,2,3,4,5,6,7,8,9}; /*generamos dos matrices de igual tama�o*/

    int b[3][3]={1,2,3,4,5,6,7,8,9};

    int f,c;

    printf("LAS MATRICES GENERADAS SON:nn");

    printf("Matriz a:n");

    for(f=0; f<3; f++)

    {

    for(c=0; c<3; c++)

    printf("%d", a[f][c]);

    printf("n");

    }

    printf("Matriz b:n");

    for(f=0; f<3; f++)

    {

    for(c=0; c<3; c++)

    printf("%d", b[f][c]);

    printf("n");

    }

    generacion(a,b); /*la funci�n recibe como par�metros, las dos matrices*/

    getch();

    return 0;

    }

    void generacion (int a[3][3], int b[3][3])

    {

    int d[3][3],f,c;

    for(f=0; f<3; f++)

    for(c=0; c<3; c++)

    d[f][c]=a[f][c]*b[f][c];

     

    for(f=0; f<3; f++)

    for(c=0; c<3; c++)

    printf("Valor de la Fila %d y columna %d = %d n", f,c,*(*(d+f)+c));

    }

    Ejemplo 7.7

    Programa que dado un vector de enteros, aumenta en una unidad el valor de los miembros del arreglo y env�a la direcci�n del primer elemento como retorno a la funci�n principal. (Note, como hacer para devolver m�s de un valor)

    #include <stdio.h>

    #include <conio.h>

    /*funcion que devuelve una direcion de memoria*/

    int *valores (int a[]);

    main()

    {

    int a[]={2, 5, 9, 7, 10};

    int *p, i;

    clrscr();

    printf("Los valores del puntero, aumentado en uno, son:n");

    p=valores(a);

    for(i=0; i<5; i++)

    printf("* %d *", *(p+i));

    getch();

    return 0;

    }

    int *valores (int a[])

    {

    int i, *p;

    for(i=0; i<5; i++)

    a[i]=a[i]+1;

    p=&a[0];

    return (p);

    }

    Cuestionario

    1. �Cu�l es la diferencia entre memoria principal y memoria cach�?___________________________________________________________________________________________________________________________________________________________________________________________________________________
    2. 8 bytes es equivalente a:________ bits
    3. �Qu�s es un registro?:_____________________________________________________________________________________________________________________________
    4. �Para que nos sirven los puneteros?:___________________________________________________________________________________________________________________________
    5. �cu�l es la diferencia entre desreferenciaci�n e indirecci�n?__________________________________________________________________________________________________________________________

    Ejercicios:

    1. Dise�e un programa en C, que lea un arreglo de 20, lea los valores, luego los imprime y muestre la suma de sus elementos, usando notaci�n de punteros
    2. Dise�e un programa, que imprima, las direcciones de memoria de los elementos de una arreglo de 5X5, elementos
    3. Se desea conocer el n�mero de elementos de una cadena de caracteres, ingresada por el usuario. (NOTA: el final de una cadena es representada por un elemento nulo ��. Utilice aritm�tica de punteros para determinar este valor)
    4. Escriba un programa en C, que le permita al usuario, ingresar una cadena de caracteres (no mayor a 100 elementos) y luego, la imprima, en forma inversa, usando para ello, notaci�n de punteros.
    5. Un programa en C, contiene la siguiente declaraci�n:

    Char *mat[5]={"Yo", "Tu", "El", "Nosotros", "Ellos"};

    a)�Cu�l es el significado de mat?_________________________________

    b)�Qu� significa (mat+2)?_________________________________________

    c)�Qu� significa *(mat+2)?_________________________________________

     

    Cap�tulo VIII: Recursi�n

    La recursi�n, o recursividad, es una propiedad que tienen algunas funciones de llamarse a s� mismas. Lo cual es una alternativa, ante la iteraci�n.

    El uso de funciones recursivas, ayudan a que �l programador ahorre (en la mayor�a de los casos) muchas l�neas de c�digo.

    Todos los ejemplos que se han presentado hasta ahora, est�n bajo la l�nea de programaci�n estructurada, por tanto son soluciones correctas. Sin embargo, como programadores, en muchas ocasiones nos podemos enfrentar a la necesidad de utilizar funciones que se llamen a s� mismas. En muchos libros, cursos o seminarios de programaci�n, se hace mucho �nfasis a �ste t�pico, y por supuesto, no dejar�amos pasar la oportunidad de hablar de ello y darle el lugar que se merece.

    En forma gr�fica, una funci�n recursiva, ser�a m�s o menos as�:

    Funcion1(…)

    {

    funcion1(…);

    }

    en �ste cap�tulo, trataremos de darle mayor prioridad a los ejemplos, es por ello, que, iniciaremos con el ejemplo m�s emblem�tico de una funci�n recursiva, como es el caso del factorial.

    Ejemplo 8.1

    Realice una funci�n recursiva que muestre el factorial de un n�mero ingresado por el usuario.

    Un n�mero factorial, se define como:

    n!=nx(n-1)X(n-2)x….x1

    lo cual puede representarse as�:

    0!=1

    1!=1

    2!=2X1

    3!=3x2x1

    y la forma recursiva para ello ser�a:

    2!=2×1!

    3!=3×2!

    n!=nx(n-1)!

    #include <stdio.h>

    #include <conio.h>

    int factorial (int n); /*declaraci�n de la funci�n*/

    main()

    {

    int n, r;

    printf("Ingrese el Valor del factorial que desea Calcular:n");

    scanf("%d", &n);

    while(n<0)

    {

    printf("Ingrese el Valor del factorial que desea Calcular:n");

    scanf("%d", &n);

    }

    r=factorial(n); /*llamado de la funci�n*/

    printf("El valor de %d!= %dn", n, r);

    getch();

    return 0;

    }

    int factorial (int n)

    {

    int r;

    if(n==0)/*CASO BASE: al resultar cierta se sale de la recursi�n*/

    r=1;

    else

    r=n*factorial(n-1); /*llamado recursivo de la funci�n */

    return r;

    }

    Explicaci�n:

    El programa, lo que hace, es mandarle el par�metro a la funci�n factorial (supongamos que n=2). Entonces pregunta si n=0, al evaluar, resulta falsa, por tanto nos vamos por el lado del else, y asigna a r el valor de 3, por la llamada recursiva de n-1, (es decir 2. r=2*factorial(1)). Y vuelve a evaluar la condici�n, resulta falsa y vuelve a llamar la funci�n (r=1*factorial(0)). Por tanto, al evaluar la condici�n esta vez, resultar� cierta, y le asigna el valor de 1, el cual lo retorna a la llamada anterio (r=1*1), este resultado, lo devuelve a la llamada que se hizo antes de �sta (r=2*1). Y este resultado, se devuelve a la llamada anterior. �sta es la que se hizo en la funci�n original, y luego se imprime ese valor

    Antes de continuar, debemos hacer hincapi� en algo muy importante, como lo es: la forma en la cual, dejar�amos de ejecutar las llamadas iterativas, ya que debe existir alguna condici�n que, al resultar cierta, ya no haga m�s llamadas recursivas y por el contrario, empiece a retornar los valores de la funci�n. A esto se le llama Caso Base.

    Un Caso Base, son los casos del problema que se resuelven sin recursi�n, para nuestro primer ejemplo es 0, por que el factorial de cero, est� definido como 1 (uno).

    Si el problema es lo suficientemente complejo, debemos hacer uso de Los casos generales, que son definidos como la soluci�n de uno o m�s problemas o un conjunto de pasos adicionales. Sin embargo, debemos tener en cuenta que, los casos generales, deben avanzar hacia un caso base, de lo contrario tendremos una recursi�n infinita.

    Ejemplo 8.2

    Realice una funci�n recursiva que calcule la suma de dos enteros.

    Soluci�n:

    Si tenemos por ejemplo a+b, la suma ser�a "a", siempre y cuando "b" sea cero, de lo contrario, recursivamente, ser�a: 1+suma(a, b-1).

    #include <stdio.h>

    #include <conio.h>

    int suma (int a, int b);

    main()

    {

    int a, b, r;

    clrscr();

    printf("Ingrese el primer sumando:n");

    scanf("%d", &a);

    printf("Ingrese el segundo sumando:n");

    scanf("%d", &b);

    r=suma(a,b);

    printf("%d + %d = %dn", a, b,r);

    getch();

    return 0;

    }

    int suma (int a, int b)

    {

    int r;

    if(b==0)/*caso base*/

    r=a;

    else

    r=1+suma(a, b-1);/*llamada recursiva, disminuimos b, para que en un

    return r;} momento dado, sea igual a cero*/

    Ejemplo 8.3

    Dise�e un programa que multiplique, dos valores enteros, recursivamente.

    Sabemos muy bien que, a*b, es igual a sumar a+a+a+a+a+……b veces.

    Pero si b=1, entonces, el producto es igual a "a".

    #include <stdio.h>

    #include <conio.h>

    int multi (int a, int b);

    main()

    {

    int a, b, r;

    clrscr();

    printf("Ingrese el valor del multiplicando:n");

    scanf("%d", &a);

    printf("Ahora el multiplicador:n");

    scanf("%d", &b);

    r=multi(a, b);

    printf("%d * %d= %d", a, b, r);

    getch();

    return 0;

    }

    int multi (int a, int b)

    {

    int r;

    if(b==1)/*elemento neutro de la multiplicaci�n, que es nuestro caso base*/

    r=a;

    else

    r=a+multi(a, b-1);/*llamada recursiva de la funci�n, disminuimos b, para que llegue un momento en el que sea igual a 1, y le sumamos a, por que as� est� definida la multiplicaci�n, como la suma de "a", b veces*/

    return r;

    }

    Ejemplo 8.4

    Calcule, recursivamente la expresi�n: Xn

    #include <stdio.h>

    #include <conio.h>

    int exp (int base, int p);

    main()

    {

    int base, p, r;

    clrscr();

    printf("Ingrese la Base:n");

    scanf("%d", &base);

    printf("Exponente es:n");

    scanf("%d", &p);

    r=exp(base, p);

    printf("%d^%d=%dnn", base, p, r);

    getch();

    return 0;

    }

    int exp (int base, int p)

    {

    int r;

    if(p==0)

    r=1;

    else

    r=base*exp(base, p-1);

    return r;

    }

    Ejemplo 8.5

    Obtenga, recursivamente el M�ximo Com�n Divisor, mcd(m,n), de dos n�meros, sabiendo que:

    -es n si n<m y n divide a m

      • es mcd(n,m) si m<n
      • es mcd(n, residuo de m/n), en caso contrario

    #include <stdio.h>

    #include <conio.h>

    int mcd (int m, int n);

    main()

    {

    int r, m, n;

    clrscr();

    printf("PROGRAMA QUE CALCULA EL M.C.D.n");

    printf("Ingrese un N�mero:n");

    scanf("%d", &m);

    printf("El otro es:n");

    scanf("%d", &n);

    r=mcd(m,n);

    printf("el mcd de %d y %d es %dn", m,n, r);

    getch();

    return 0;

    }

    int mcd (int m, int n)

    {

    int r;

    if(n<=m && m%n==0)

    r=n;

    else

    if(m<n)

    r=mcd(m,n);

    else

    r=mcd(n, m%n);

    return r;

    }

    Ejemplo 8.6:

    Programa que calcula la secuencia de Fubonacci, recursivamente:

    #include <stdio.h>

    #include <conio.h>

    int fubonacci (int n);

    main()

    {

    int n, r;

    printf("PROGRAMA QUE IMPRIME LA SECUENCIA DE FOBONACCIn");

    printf("Ingrese el valor de n:n");

    scanf("%d", &n);

    printf("** %d **", r);

    getch();

    return 0;

    }

    int fobonacci (int n)

    {

    int r;

    if(n==0 || n==1)

    r=n;

    else

    r=fobonacci(n-1)+fobonacci(n-2);

    return r;

    }

    Ejemplo 8.7:

    /*Programa que calcula la suma de los primeros

    n numeros enteros, positivos */

    #include <stdio.h>

    #include <conio.h>

    int func (int n);

    main()

    {

    int n, r;

    clrscr();

    printf("A partir de cero… �Hasta que n�emro desea sumar?n");

    scanf("%d", &n);

    r=func(n);

    printf("La suma de los primeros %n numeros eneteros es: %dn", n, r);

    getch();

    return 0;

    }

    int func (int n)

    {

    int r;

    if(n==0)

    r=0;

    else

    r=n+func(n-1);

    return r;

    }

    Este programa, lo que hace es sumar desde cero, hasta el n, n�mero entero.

    Ejemplo 8.8:

    Programa que calcula, la suma de los primeros n elementos de un vector:

    #include <stdio.h>

    #include <conio.h>

    int sumav(int v[], int n);

    main()

    {

    int n, r, v[]={1,2,3,4,5,6,7,8,9};

    clrscr();

    printf("Ingrese el valor hasta el cual dsea sumar:n");

    scanf("%d", &n);

    while(n<0 || n>9)

    {

    printf("Error, numero no v lido vuelva a intertarlo:n");

    scanf("%d", &n);

    }

    r=sumav(v, n);

    printf("El valor de la suma es %dn", r);

    getch();

    return 0;

    }

    int sumav(int v[], int n)

    {

    int r;

    if(n==0)

    r=v[0];

    else

    r=v[n]+sumav(v, n-1);

    return r;

    }

    Ejemplo 8. 9:

    #include <stdio.h>

    #include <conio.h>

    /*programa que lee e imprime un arreglo de enteros

    y despues hace una busqueda lineal recursiva de ciertos

    valores digitados por el usuario */

    int busquedalineal(int *V, int n, int b);

    void main()

    {

    int arreglo[12];

    int pos, i, num, bus, buscar=1;

    pos=0;

    printf("Digite 12 valores para guardarlos en el arreglo:n");

    for(i=0; i<12; i++)

    {

    printf("Digite el elemento de la posici�n %d del vector:n", i+1);

    scanf("%d", &arreglo[i]);

    }

    printf("Los elementos del vector son:n");

    for(i=0; i<12; i++)

    {

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

    }

    printf("n");

    while(buscar)

    {

    printf("Digite un n�mero a buscar:");

    scanf("%d", &num);

    pos=busquedalineal(arreglo, 11, num);

    if(pos)

    printf("El numero %d se encuentra en el arreglon", num);

    else

    printf("El numero %d NO se encuentra en el arreglon", num);

    printf("�Desea Buscar otro numero? (S/N)n");

    bus=getch();

    if(bus=='s' || bus=='S')

    buscar=1;

    else

    buscar=0;

    }//fin del while

    getch();

    }//fin del main

    /*Funci�n REcursiva de Busqueda Lineal */

    int busquedalineal(int *V, int n, int b)

    {

    if(n==0)

    return (V[0]==b);

    else

    if(V[n]==b)

    return 1;

    else

    return busquedalineal (V, n-1, b);

    }

    Ejemplo 8.10:

    /* Pograma que dado un vector de enteros

    calcule:

    1. el elemento maximo del arreglo

    2. el elemento minimo del arreglo

    3. la suma de los elementos del arreglo

    4. el producto de los elementos del arreglo

    5. el promedio de los elementos del arreglo */

    #include <stdio.h>

    #include <conio.h>

    #define N 10

    int max (int a[], int n);

    int min (int a[], int n);

    int sum (int a[], int n);

    int multi (int a[], int n);

    int prom (int a[], int n);

    main()

    {

    int a[N], i=0, r, op, n=10, ban=1;

    clrscr();

    printf("ttBIENVENIDO (A)nn");

    printf("Ingrese los valores del vector:n");

    for(i=0; i<N; i++)

    {

    printf("Ingrese el elemento %d del arreglo:n", i);

    scanf("%d", &a[i]);

    }

    clrscr();

    printf("Los valores del arreglo son:n");

    for(i=0; i<N; i++)

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

    getch();

    while(ban==1)

    {

    clrscr();

    printf("*****************************************n");

    printf("** �Que desea hacer? **n");

    printf("** 1. El maximo elemento **n");

    printf("** 2. El minimo elemento **n");

    printf("** 3. La suma de sus elementos **n");

    printf("** 4. Su producto **n");

    printf("** 5. Su promedio **n");

    printf("** 6. Sair **n");

    printf("*****************************************n");

    scanf("%d", &op);

    switch(op)

    {

    case 1: clrscr();

    r= max(a, n);

    printf("el maximo elemto del vector es %dn", r);

    break;

    case 2: clrscr();

    r=min(a, n);

    printf("El elemnto minimo es %dn", r);

    break;

    case 3: clrscr();

    r= sum(a, n);

    printf("La suma de los elementos del vector es: %d n", r);

    break;

    case 4: clrscr();

    r=multi (a, n);

    printf("Al multiplicar todos los elementos del vector resulta %d n", r);

    break;

    case 5: clrscr();

    r=prom(a, n);

    printf("El promedio de los valores son: %d n", r);

    break;

    case 6: return 0;

    default:

    printf("ERROR, comando no valido!!n");

    }

    printf("�Desea Seguir? (1/2)n");

    scanf("%d", &ban);

    }

    getch();

    return 0;

    }

    int max (int a[], int n)

    {

    int r, aux;

    if(n==0)

    r=a[0];

    else

    {

    aux=max(a, n-1);

    if(a[n]>aux)

    r=a[n];

    else

    r=aux;

    }

    return r;

    }

    int min (int a[], int n)

    {

    int r, aux;

    if(n==0)

    r=a[0];

    else

    {

    aux=min(a, n-1);

    if(a[n]<aux)

    r=a[n];

    else

    r=aux;

    }

    return r;

    }

    int sum (int a[], int n)

    {

    int r;

    if(n==0)

    r=0;

    else

    r=a[n-1]+sum(a, n-1);

    return r;

    }

    int multi (int a[], int n)

    {

    int r;

    if(n==0)

    r=1;

    else

    r=a[n-1] * multi(a, n-1);

    return r;

    }

    int prom (int a[], int n)

    {

    int r;

    r=sum(a, n)/N;

    return r;

    }

    Hasta el momento, hemos visto algunos ejemplo del uso de la recursi�n, pero hay que tener en cuenta, algunos aspectos importantes, para decidir entre recursi�n o iteraci�n:

    1. la relaci�n, tiempo-espacio de la memoria, para una llamada recursiva es mayor que para una iterativa, por eso, se debe hacer uso de la recursi�n s�lo cuando, una soluci�n iterativa no sea posible.
    2. si son posibles las dos soluciones (iterativa y recursiva), entonces es mejor hacer uso de la soluci�n iterativa, puesto que una llamada recursiva consume mayor tiempo y espacio en memoria.
    3. hay por el contrario, muchos problemas que se resuelven exclusivamente con recursi�n, ya que son demasiado extensos y complejos, por tanto una soluci�n recursiva, presentar� una imagen m�s fiable, comprensible y elegante.

    Recursi�n Infinita

    Cuando habl�bamos de los ciclos (bucles), dec�amos que, dentro del ciclo debe existir algo que modifique la condici�n, para que, en un momento dado, se pueda salir del ciclo. Lo mismo pasa con la iteraci�n.

    Una Recursi�n Infita, implica que cada llamada recursiva produce otra, y �sta a su vez produce otra llamada, y �ta tambi�n produce otra llamada, y as� sucesivamente para SIEMPRE, o al menos hasta que se termine la memoria de la computadora, para evitar esto, hay que tener en cuenta, y muy bien definidos, los casos baes y los casos generales del problema.

    Algoritmos: Div�delos y Vencer�s

    Una e las t�cnicas de programaci�n m�s importante, es la denominada "Divide y vencer�s", que, como ya hemos explicado en cap�tulos anteriores, consiste en subdividir un programa grande y complejo, en peque�os programas m�s simples y sencillos.

    En recursi�n, normalmente este proceso da lugar a casos base, es decir, problemas cuya soluci�n es inmediata, sin recursi�n.

    Un algoritmo divide y vencer�s, puede ser llamado recursivamente, a partir de un subconjunto (m�s peque�o) de datos. La condici�n que deja de hacer las llamadas, es lo que hemos denominado como caso base.

    Torres de Hanoi

    Quiz� el siguiente ejemplo, sea uno de los m�s complejos de la recursi�n, por que consite en tres varillas, torres o postes, paralelos; del cual el primero, posee tres donas, discos… en orden de la m�s grande a la m�s peque�a, y se desean mover de la varilla 1 a la varilla 3, pero bajo ciertas condiciones:

    1. los movimientos deben ser de uno en uno (es decir, no se pueden mover dos o tres donas al mismo tiempo)
    2. en una varilla, no puede quedar una dona peque�a, bajo una grande.
    3. La siguiente imagen, representa, la forma m�s adecuada, para realizar los movimientos:

      Parece bastante complejo verdad?, en lo personal, me tom� un poco de tiempo, entender y buscar la forma de mover las donas (debo confesar que no soy muy bueno para ese tipo de jueguitos).

      El ejemplo anterior constaba, unicamente de tres discos (donas), pero puede generalizarse para n discos y tres varilas.

      El algoritmo ser�a el siguiente:

      If(n==1)

      -Mover e disco 1 de la varilla inicial(1) a la varilla final(3)

      Else

      -Mover n-1 discos desde varilla inicial(1) hasta varilla la varilla temporal(2), utilizando la varilla 3, como auxiliar.

      -mover el disco n desde varilla inicial a varilla final

      -mover n-1 discos desde la varilla auxiliar central a varilla final (3) utilizando como auxiliar a la varilla n�mero uno.

      Ejemplo 8.11:

      #include <stdio.h>

      #include <conio.h>

      void hanoi (int n, int inic, int temp, int fin);

      main()

      {

      int n; /*numero de discos a mover*/

      clrscr();

      printf("Numero de discos:n");

      scanf("%d", &n);

      /*mover n discos desde 1 a 3, usando a 2 como auxiliar*/

      hanoi(n, 1, 2, 3);

      getch();

      return 0;

      }

      void hanoi (int n, int inic, int temp, int final)

      {

      if(n>0)

      {

      /*mover n-1 discos de inic a temp*/

      hanoi(n-1, inic, final, temp);

      /*mover el que quede en inic a final*/

      printf("Del poste %d al %dn", inic, final);

      /*mover n-1 discos de temp a final*/

      /*el temporal es inic*/

      hanoi(n-1, temp, inic, final);

      }

      }

      Cuestionario

      1. �Qu� es recursi�n?:________________________________________________________________________________________________________________________________________

    4. �Qu� aspectos, son los que debo tomar en cuenta, cuando tengo que decidir entre iteraci�n o recursi�n?_________________________________________________________________________________________________________________________________________________________________________________________________________________
    5. �Cu�ndo se produce una recursi�n infinita?_______________________________________________________________________________________________________________________________
    6. �Para que sirven los casos base?�y los casos generales?______________________________________________________________________________________________________________________________________________________________________________________________
    7. �En que consiste la t�cnica "divide y vencer�s"?______________________________________________________________________________________________________________________________________________________________________________________________

    Descubre el error:

    El siguiente c�digo, presenta algunos errores de l�gica, de sintaxis y de ejecuci�n, �Puedes descubrirlos y corregirlos?

    /* Programa que calcula la suma de dos vectores */

    #include <stdio.h>

    int suma (int a[], int b[], int n);

    void main()

    {

    int a[5]={1,2,3,4,5};

    int b[5]={1,2,3,4,5};

    int n=5, r;

    clrscr();

    printf("el valor de la suma es:n");

    r=suma(a,b, n);

    printf("%d", r);

    getch();

    }

    int suma (int a[], int b[], int n)

    {

    if(n!=0)

    r=0;

    else

    {

    r=a[n-1]+b[n-1]+suma(a, b, n-1);

    }

    return r;

    }

    /*Funci�n que suma a+b, usando una suceci�n*/

    #include <stdio.h>

    #include <conio.h>

    int suma (int x, int b);

    main()

    {

    int a,b,x=0, sum;

    clrscr();

    printf("PROGRAMA QUE USA LA FUNCION SUCC PARA SUMAR A+Bn");

    printf("Ingrese el valor de a:");

    scanf("%d", &a);

    printf("b=");

    scanf("%d", b);

    sum=a+suma(x,b);

    printf("%d + %d = %dnn", a,b,sum);

    getch();

    return 0;

    }

    int suma (int a, int b)

    {

    int r;

    if(x==b)

    r=x;

    else

    {

    x++;

    r=suma(a,b);

    }

    }

    Ejercicios:

    1. Dise�e un programa que sume los primeros n m�ltiplos de tres, en forma recursiva
    2. Dise�e un funci�n (o programa completo) que, dado un vector, determine el producto de todos sus elementos en forma recursiva.
    3. el valor de e (una expresi�n matem�tica, puede representarse de la siguiente manera: e=1/1!+1/2!+…+1/n!. Dise�e una funci�n (o programa) que muestre el resultado recursivo de �sta sucesi�n.
    4. Dado un vector, escriba un programa que busque un elemento ingresado por el usuario, en el vector y con un mensaje en pantalla, indique si ese valor se encuentra o no.
    5. Escribir un programa, que comntenga una funci�n recursiva, para calcular la funci�n de Ackermann, definida de la siguiente forma:
    6. ->A(m,n)=n+1, si m=0

      ->A(m,n)=A(m-1, 1) si n=0

      ->A(m,n)=A(m-1, A(m, n-1)) si m>0 y n>0

    7. El combinatorio de dos n�meros, est� definido, por la expresi�n siguiente: C(m, n)=m!/(n!x(m-n)!), escribir un programa en C, que calcule el combinatorio, donde n! Es el factorial de n.
    8. Dados dos vectores de enteros e igual tama�o, calcular la suma de los productos de sus elementos: C=S (AiXBi).
    9. Escriba un programa que transforme cualquier n�emro base 10, a otra base b, que puede ser de 8 a 16, utilizar para ello recursividad.
    10. Dise�e un programa que muetre cual es el valor mayor contenido en un vector unidimensional.
    11. Se tiene un conjunto de pesos p1, p2, p3…pn. y se desea estudiar, como objetivo si existe una selecci�n de dichos objetos que totalicen exactamente un peso v, dado. Por ejemplo, si el objetivo es V=12y los pesos son: 4, 3, 6, 2, 1; se pueden elegir el primero, el tercero y el cuarto, ya que: 4+6+2=12. El algotimo para solucionar este problema tiene como tarea b�sica a�adir un nuevo peso , probar si con ese peso se logra el objetivo o se avanza en el sentido de alcanzar la soluci�n.

    Cap�tulo IX: Estructuras y Uniones

    En este cap�tulo, vamos a aprender a usar una de las caracter�sticas m�s singulares, peculiares y sobre todo, portentes de C, como lo es, el uso de estructuras, uniones y TIPOS DE DATOS creados por el programador, lo cual nos facilita muchas cosas ya que, no nos restringimos a los tipos de datos que nos ofrece el lenguaje C.

    Estructuras

    Los arrays, son un ejemplo de estructuras (de tipo homog�neo), sin embargo, �ste tipo de estructuras posee una gran limitante, puesto que, s�lo admite datos del mismo tipo (entero, flotante, car�cter); las estructuras que vamos a estudiar, estan compuestas por un grupo de variables, no necesariamente del mismo tipo, en las cuales, podemos almacenar diferente informaci�n (en cuanto a tipo), pero referente a un mismo t�pico.

    Por ejemplo, en nuestro carn�t de identidad, del colegio, de la universidad, aparecen muchos datops acerca de nosotros.

    Nombres, apellidos, edad, direcci�n, fecha de nacimiento, grado, secci�n, ciclo…

    Estos datos, son de diferente tipo, pero, en C, podemos almacenarlos utilizando un tipo de dato registro al que llamamos Estructura.

    Por tanto, una estructura es una calecci�n de una o m�s tipos de elementos denominados miembros, cada uno de los cuales puede ser de un tipo de dato diferente.

    La figura anterior, muestra, los datos (y el tipo) para cierto registro, pero supongamos que dichos datos, son para alumnos de alguna universidad, la cual cuenta con 500 alumnos… la cantidad de variables, ser�a impresionante, por ejemplo tendr�amos que declarar 500 variables para los nombres, 500 para los apellidos, 500 para las edades, y as� sucesivamente.

    Es ello que radica la importncia de las estructuras, por que nos ahora tiempo adem�s que las estructuras son una herramienta importante para la creaci�n de programas portentes y bases de datos.

    Declaraci�n de una Estructura

    Como toda variable, en C, debemos declarar una estructura para poder hacer uso de ella, y la forma de hacerlo es la siguiente:

    strcuct <nombre de la estructura>

    {

    <tipo de dato del miembro1> <nombre del miembro 1>;

    <tipo de dato del miembro 2> <nombre de miembro 2>:

    <tipo de dato del miembro n> <nombre del miembro n>;

    }

    Por ejemplo, para los datos anteriores, la forma de declararla ser�a la siguiente:

    struct datos

    {

    char nombre[30];

    char apellido[20];

    int edad;

    char direcci�n[100];

    char fecha_nac[8];

    };

    Definici�n de variables del tipo estructura

    Al igual que las funciones, las estructuras son declaradas y definidas; en la declaraci�n es que, le estamos indicando al compilador que las variables de tipo datos estar�n compuestas por los elemetos, tales como nombre, apellido etc; pero adem�s, debemos defininir variables que, ser�n de ese tipo. As� como en alguna parte de C, est�n declarados los tipos de datos char, int, float; as� tambi�n nosotros debemos definir las estructuras. Y cuando en el main, definimos las variables que ser�n del tipo int, del tipo float o del tipo char, de igual manera, debemos definir que variables ser�n del tipo de la estructura que hemos creado. Para ello existen dos procedimientos:

    1. listar las variables inmediantamente despu�s de cerrar la llave de la estructura
    2. Listar las variables que serp�n del tipo estructura creadas, inmeditamente despu�s del identificador de la estructura; en la zona de declaraciones del programa.

    Ejemplo:

    1. struct datos

    {

    char nombre[30];

    char apellido[20];

    int edad;

    char direcci�n[100];

    char fecha_nac[8];

    } alumno1, alumno2, alumno3;

    2. struct datos alumno1, alumno2, alumno3;

    Si por alg�n caso, los datos de las tres variables, fuesen los mismos, podemos asignar los valores de �sta forma:

    Alumno1=alumno2;

    Alumno3=alumno2;

    O tambi�n:

    Alumno1=alumno3=alumno2;

    Ya que, al contener los mismos miembros, es como si se tratacen de datos tipo int, float o char, por consiguiente podemos hacer las asignaciones anteriores.

    Una esructuram la podemos inicializar as�:

    struct datos

    {

    char nombre[30];

    char apellido[20];

    int edad;

    char direcci�n[100];

    char fecha_nac[8];

    }alumno1 = {"Manuel",

    "Ortez",

    20,

    "San Salvador, El Salvador",

    "27/04/86",

    };

    o tambi�n as�:

    struct datos alumno2={"Carolina",

    "Pelayo",

    20,

    "San Salvador, El Salvador",

    "14/05/86",

    };

    El tama�o de una estructura es determinado de forma muy simple, consiste en sumar, el tama�o de todos los miembros, para nuestro caso particular, el tama�o (en bytes) de la estructura datos ser�a:

    Ciertamente que, este proceso, lo podemos simplificar utilizando la sentencia:

    Sizeof(datos);

    Cuyo resultado ser�: 160.

    Acceso a una estructura.

    Para acceder a una estructura, o bi�n a la informaci�n guardada en ella, podemos hacer uso de dos nuevos amigos:

    1. El operador punto (.)
    2. El operador puntero �flecha- (->)

    Por ejemplo:

    Alumno1.nombre="Manuel";

    Strcpy(alumno1.apellido, "Ortez");

    Para utilizar el operador flecha, debemos hacer uso de punteros (y cre�ste que ya nos hab�amos olvidado de los punteros verdad?).

    Por ejemplo, si tenemos un puntero a una estructura:

    .struct datos *ptr;

    ptr=&alumno1;

    ptr->edad=20;

    Ejemplo 9.1

    Dise�e un programa que guarde los datos de dos cd.

    #include <stdio.h>

    #include <conio.h>

    /*declracion de la estructura*/

    struct datos_cd

    {

    char titulo[20];

    float precio;

    char fecha[8];

    };

    main()

    {

    /*definicion de las variables estructuras*/

    struct datos_cd cd1, cd2;

    struct datos_cd *ptr;

    int tam;

    ptr=&cd2; /*asignacion de la direccion de cd2 al puntero*/

    clrscr();

    /*leemos los datos, usando el operador punto*/

    printf("Introduzca el t�tulo del primer cd:n");

    scanf("%s", &cd1.titulo);

    printf("Intriduzca el precio del cd1:n");

    scanf("%f", &cd1.precio);

    printf("Ahora, la fecha de edicion (dd/mm/aa/):n");

    scanf("%s", &cd1.fecha);

    printf("Introduzca el t�tulo del segundo cd:n");

    scanf("%s", &cd2.titulo);

    printf("Intriduzca el precio del cd2:n");

    scanf("%f", &cd2.precio);

    printf("Ahora, la fecha de edicion (dd/mm/aa/):n");

    scanf("%s", &cd2.fecha);

    clrscr();

    printf("tttAhora vamos a imprimir los valores guardados:nn");

    printf("********************Datos del CD1*************************n");

    printf("Titulo %s n", cd1.titulo);

    printf("Precio %.2fn", cd1.precio);

    printf("Fecha %sn", cd1.fecha);

    printf("**********************************************************nn");

    printf("********************Datos del CD2*************************n");

    printf("Titulo %sn", ptr->titulo);

    printf("Precio %.2fn", ptr->precio);

    printf("Fecha %sn", ptr->fecha);

    printf("**********************************************************nn");

    printf("El tama�o de la estructura es %d bytesnn", sizeof(datos_cd));

    getch();

    return 0;

    }

    Estructuras Anidadas

    Al igual que los ciclos, las decisiones, las expreciones, etc, las estructuras tambi�n pueden estar dentro de otras, a esto es que se le llama estructuras anidadas.

    Supongamos que tenemos dos estructuras siguientes:

    stuct empleado

    {

    char nom[30];

    char puesto[10];

    int edad;

    float sueldo;

    char municipio[20];

    char ciudad[10];

    char direcci�n[50];

    };

    struct cliente

    {

    char nom[30];

    char fecha_deuda[8];

    float saldo;

    char municipio[20];

    char ciudad[10];

    char direcci�n[50];

    };

    observamos que, en ambas estructuras, hay fatos que se repiten, los cuales los podr�amos ubicar en otra structura, as�:

    struct direc

    {

    char municipio[20];

    char ciudad[10];

    char direcci�n[50];

    };

    por tanto, las estructuras de empleado y cliente, ser�a de la siguiente forma:

    stuct empleado

    {

    char nom[30];

    char puesto[10];

    int edad;

    float sueldo;

    struct direc direc_empleado;

    };

    struct cliente

    {

    char nom[30];

    char fecha_deuda[8];

    float saldo;

    struct direc direc_cliente;

    };

    en C, podemos definir una estructura dentro de otra estructura, claro siempre y cuando la declaraci�n de �sta, haya sido previo.

     

 P�gina anterior Volver al principio del trabajoP�gina siguiente