Enrere Mòdul 7
Fonaments de programació. Llenguatge C/C++---
Pràctica    Resum teòric Exercicis
Pràctica d'ampliació

 
Miniagenda telefònica

En aquesta pràctica repassarem alguns conceptes d'estructures. També repassarem nombrosos aspectes tractats en mòduls anteriors. 

 

Desenvolupament de la pràctica

En aquesta pràctica construirem una petita agenda telefònica. 

Abans de començar, hem de remarcar que aquesta agenda no serà pràctica fins que arriben a la part d'arxius. ¿De què serveix una agenda en la qual no podem guardar les dades en el disc?. Malgrat aquest handicap, veurem en el pròxim mòdul que emmagatzemar les dades en un arxiu suposarà només petites modificacions a aquest programa. 

Creeu un nou espai de treball per aquest setè mòdul amb el nom: m7. Aneu afegint un projecte nou del tipus Win32 Console Application per cada nova pràctica o exercici amb els noms que cada vegada es proposi.

Definiu un projecte nou anomenat m7p01 i afegiu-li un arxiu de font C/C++ anomenat m7p01.cpp. Escriviu el següent codi:

I //m7p01.cpp: Miniagenda telefònica

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include
<conio.h>



#define MAX 100

struct {
    char nom[30];
    char cognom1[30];
    char cognom2[30];
    char tel[9];
} entrada[MAX];

int punter=0;

void nova_entrada(void);
void esborra_entrada(void);
void mostrar(void);
void cerca_nom(void);
void cerca_cognom1(void);
void cerca_tel(void);

II void main(){
    char sel;

    do{
        system("cls");
        printf("Miniagenda telefònica\n\n\n");
        printf("1. Introduir nova entrada\n");
        printf("2. Esborrar entrada\n");
        printf("3. Cerca per nom\n");
        printf("4. Cerca per cognom1\n");
        printf("5. Cerca per telèfon\n");
        printf("6. Mostra totes les entrades\n");
        printf("7. Sortir\n\n\n");
        printf("\n\n Premi una opció (ESC per sortir)\n");

        do{
            sel=getchar();
        }while((sel<'1' || sel>'7')&&sel!=27);

        switch(sel){
            case '1':
                nova_entrada();
                break;

            case '2':
                esborra_entrada();
                break;

            case '3':
                cerca_nom();
                break;

            case '4':
                cerca_cognom1();
                break;

            case '5':
                cerca_tel();
                break;

            case '6':
                mostrar();
                break;

            case '7':
            case 27:
                return;
         };
    }while(1);
}



III void nova_entrada(void){

    if (punter==MAX) {
        printf("no és possible afegir més entrades");
        getch();
        return;
    }


    printf("entrada %d.\n",punter);
    printf("\nnom? ");gets(entrada[punter].nom);
    printf("cognom1? ");gets(entrada[punter].cognom1);
    printf("cognom2? ");gets(entrada[punter].cognom2);
    printf("telèfon? ");gets(entrada[punter].tel);
    printf("\nEntrada %d emmagatzemada", punter);
    punter++;
    return;
}



 

 

IV void esborra_entrada(void){
    int sel,ct;

    printf("número d'entrada per esborrar?\n");
    getchar();
    scanf("%d",&sel);

    if (sel<0||sel>=punter){
        printf("número d'entrada erroni");
        getch();
        return;
    }

    for (ct=sel+1;ct<punter;ct++){
        entrada[ct-1]=entrada[ct];
    }
    punter--;
}


 

V void mostrar(void){
    int ct;

    for (ct=0;ct<punter;ct++){
        printf("%d: %s %s %s %s\n", ct, entrada[ct].nom,
        entrada[ct].cognom1, entrada[ct].cognom2,
        entrada[ct].tel);
    }
    getch();
    return;
}


VI void cerca_nom(void){
    int ntrobats=0,ct;
    char ncer[30];

    printf("nom a buscar?\n");
    scanf(" %s", ncer);
    
    for(ct=0;ct<punter;ct++){
      if( !strncmp( ncer, entrada[ct].nom, strlen(ncer))){
         printf("%d: %s %s %s %s\n", ct, entrada[ct].nom,
            entrada[ct].cognom1, entrada[ct].cognom2,
            entrada[ct].tel);
         ntrobats++;
      }
    }
    printf("s'han trobat %d entrades",ntrobats);
    getch();
    return;
}


 

VII void cerca_cognom1(void){
    int ntrobats=0,ct;
    char ncer[30];

    printf("cognom1 a buscar?\n");
    scanf(" %s", ncer);

    for(ct=0;ct<punter;ct++){
      if( !strncmp( ncer, entrada[ct].cognom1, strlen(ncer))){
         printf("%d: %s %s %s %s\n", ct, entrada[ct].nom,
            entrada[ct].cognom1, entrada[ct].cognom2,
            entrada[ct].tel);
         ntrobats++;
      }
    }
    printf("s'han trobat %d entrades",ntrobats);
    getch();
    return;
}

 

 

VIII void cerca_tel(void){
    int ntrobats=0,ct;
    char ncer[30];

    printf("telèfon a buscar?\n");
    scanf(" %s", ncer);

    for(ct=0;ct<punter;ct++){
      if( !strncmp( ncer, entrada[ct].tel,strlen(ncer))){
         printf("%d: %s %s %s %s\n", ct, entrada[ct].nom,
            entrada[ct].cognom1, entrada[ct].cognom2,
            entrada[ct].tel);
         ntrobats++;
      }
    }
    printf("s'han trobat %d entrades",ntrobats);
    getch();
    return;
}

 

 

Explicació del programa

El primer que es pot observar és que aquest és el programa més llarg que hem fet fins ara. No obstant això, encara que el programa sigui llarg, serà fàcil d'entendre ja que hi ha moltes parts similars.

Per facilitar la comprensió del programa, s'ha dividit aquest en vuit parts numerades amb nombres romans a la part esquerra del codi. La primera part correspon a les declaracions inicials i variables globals. La segona part és la funció main() que conté el menú principal. La resta de parts correspon a les diferents funcions definides.

PART I

En aquest programa es fa servir 4 arxius de capçalera: 

  • stdio.h, per a les funcions estàndards d'entrada i sortida (scanf(), printf(), gets(),...).

  • stdlib.h, per esborrar la pantalla amb system("cls").

  • string.h, per a les funcions de manipulació de cadenes de caràcters (strlen(), strncmp())

  • conio.h, per a la funció getch(). Recordeu que aquesta funció es fa servir per llegir directament del teclat un caràcter, sense esperar la tecla INTRO. En aquest programa només s'utilitza quan es vol aturar el programa per tal que l'usuari visualitzi algun missatge fins que premi una tecla.

A més de la declaració de les sis funcions que fa servir el programa, en aquesta primera part s'ha definit una variable entera global: punter i una estructura global anònima amb un vector d'aquesta estructura anomenat entrada. Aquest vector tindrà una capacitat de 100 entrades. (és segur que en aquesta versió del programa en la què no es pot guardar les dades al disc, 100 entrades és més que suficient).

Els camps de l'estructura són, tots quatre, cadenes de caràcters. Com que amb els telèfons no es fan càlculs numèrics, és convenient emmagatzemar-los en variables de cadena. 

La variable punter ens indicarà en qualsevol funció del programa el nombre de dades que hi ha a la memòria.

PART II

La funció main(), en aquest cas, només s'encarrega de fer la gestió del menú. En aquest menú només hi ha les opcions 1 al 6 a més de les opcions 7 i la tecla d'escapada, que serveix per sortir del programa.

PART III

L'opció 1 fa que s'executi la funció nova_entrada(). Aquesta funció comprova que el nombre d'entrades no sigui superior a la mida del vector d'entrades i després demana la introducció dels quatre camps. Per omplir un dels camps d'una variable estructura es fa servir l'operador punt. Per exemple, per omplir el camp nom s'ha fet:

printf("\nnom? ");gets(entrada[punter].nom);

PART IV

L'opció 2 fa que s'executi la funció esborra_entrada(). Aquesta funció demana un número d'entrada, comprova que sigui un número d'una entrada existent i l'esborra. De fet, el que fa és desplaçar totes les entrades de número més gran que el número introduït a l'entrada anterior. La última entrada no es modifica, encara hi és, però la variable punter és decrementada en una unitat i, per tant, una crida a l'opció 1 de nova entrada la sobreescriurà.

És important destacar que l'assignació d'una variable d'estructura a una altra es pot fer globalment i no camp a camp, és a dir:

 
    entrada[ct-1]=entrada[ct];

PART V

L'opció 3 fa que s'executi la funció mostrar(). Aquesta funció imprimeix a la pantalla totes les entrades emmagatzemades. Això es fa camp per camp amb la següent sentència:

 
printf("%d: %s %s %s %s\n", ct, entrada[ct].nom,
        entrada[ct].cognom1, entrada[ct].cognom2,
        entrada[ct].tel);

PARTS VI, VII, i VIII

Les opcions 4, 5 i 6 serveixen per cercar per un camp. Una vegada introduït el text a cercar, aquest s'emmagatzema en la variable de cadena ncer. La comprovació es fa per totes les entrades i és la següent:

 
  if( !strncmp( ncer, entrada[ct].tel,strlen(ncer)))

La funció strncmp() va ser mencionada al mòdul anterior, serveix per comparar un nombre determinat de caràcters de dos cadenes. En aquest cas, aquesta funció tornarà el valor true en el cas que la variable entrada[ct] comenci pel contingut de la variable ncer, ja que el nombre de caràcters a comparar coincideix amb la longitud de la cadena ncer (strlen(ncer)).

Aquest tipus de cerca es diu cerca seqüencial pel fet que es comença amb el primer registre i es miren seqüencialment tots els registres fins trobar el que volem. Quan els registres estan desordenats, aquest és el mètode més fàcil i tant eficient com qualsevol altre. En el cas que els registres estiguin ordenats hi ha altres mètodes d'ordenació que són molt més eficients que una cerca seqüencial, per exemple, al mòdul 8 es veurà com fer una cerca binària o dicotòmica.