Lezione XV                               frames     noframes

 

Array e stringhe

 

Un  array e’ una struttura di dati  costituita da  un insieme di variabili dello stesso tipo di dato (intero, carattere, reale ecc. ), a cui e’ possibile accedere tramite un nome, e referenziare uno specifico elemento attraverso un indice.

Nel linguaggio C gli elementi di un array sono allocati in MC in celle adiacenti, in cui viene associato l’indirizzo piu’ basso, al primo elemento dell’ array e l’indirizzo piu’ alto all’ultimo elemento dell’array.

Gli array possono essere ad una dimensione (vettori ) , a due dimensioni (matrici) , ad n dimensioni (array multidimensionali).

Array ad una dimensione

Se scriviamo la seguente definizione  int  A[5];

avremo dichiarato un array o vettore A formato da 5 elementi il cui tipo e’ un intero.

In MC viene allocata una sequenza di locazioni intere ( 5 locazioni intere , una dopo l’altra ) come in figura.

 

 

 

 

 

 

 

Ogni locazione sara’ tale da poter contenere un valore  dell’array.

Ciascuna locazione e’ una variabile uguale ad una variabile intera, soltanto che queste 5 variabili hanno un NOME COMUNE ( A ) e sono distinte da un indice che varia da 1 fino a 5.

Nel linguaggio C il primo elemento di un array ha indice 0, quindi  l’indice varia da 0 fino a 4

Per cui avremo A[0], A[1], A[2], A[3], A[4] .

 

0

1

2

3

4

A[0]

A[1]

A[2]

A[3]

A[4]

 

Con le variabili con indice si puo' fare tutto quello che si fa con le variabili normali .

 

Esempio

A[3] = 7 ;  /* assegniamo il valore intero 7 alla quarta componente dell’ array A */

if (A[3]==7)  printf (“\n%d”, A[0]) ;

/* se la quarta componente dell’ array A e’ uguale a 7 allora stampa la prima componente dell’array A */

L’ indice dell’ array puo’ essere gestito scrivendo espressioni aritmetiche :

A[j] coincide con la componente (j+1)-esima ; se j e’ uguale a 2 e’ la terza componente.

A[J+2]  coincide con A[4] ossia con la quinta componente.

Il vincolo da mantenere e’ che l’indice usato sia tale da non andare fuori campo.

A[-1] NON ESISTE !

A[5]  NON ESISTE !

Precisazioni :

*   Gli array non possono essere assegnati ad array:

          Avendo int x[5],y[5]; ….  x=y ; /*e’ scorretta*/

*   L’ indirizzamento di un elemento dell'array puo’ avvenire come detto mediante una espressione    int v=2, a=1; …per cui x[v+4-a];  /*equivale all’accesso ad x[5] */

*   Non c’e’ nessun controllo sull’effettiva esistenza della componente indirizzata: … x[12] = 9; /* la tredicesima componente dell’array non esiste ma l’istruzione passa la compilazione: verra’ posto nella tredicesima locazione intera a partire dall’inizio dell’array…*/

 

esercizio

 

Nel caso volessimo  ordinare almeno un solo elemento di  un array A costituito da 20 elementi di tipo intero potremmo usare il seguente codice:

#include<stdio.h>

#define  N  20

int main( )

{

int  A[N] ;        /* dichiarazione dell’ array A di N =20 elementi */

int  AUX;   /* dichiarazione della variabile intera AUX di ausilio (appoggio) */

int  j;   /* variabile indice per scandire l’ array */

for (j=0; j<=N-2; j++)

 if (A[J] > A[J+1] ) /* confronto fra il j-esimo ed (j+1) - esimo elemento */

   {

    AUX= A[j] ;

    A[j] = A[j+1] ;

   A[j+1]= AUX;

   }

for (j=0; j<=N-1; j++)

printf (“\n%d”,A[j]);

return  0;

}

Notiamo che l’ indice dell’ array ( elemento fra le parentesi quadre ) deve essere sempre un intero od una espressione intera.

 

 

esercizio

 

 

Definizione del tipo array

Una variabile di tipo array  si dichiara attraverso la seguente sintassi:

< var_array>::= <tipo><identificatore_var><array> ;

<array>::= < costruttore_array> | <costruttore_array> <array>

<costruttore array>::=  [ <espressione_costante_intera>]

 

esempio

float  negozi [20]; 

/* abbiamo dichiarato un array chiamato negozi formato da 20 elementi il cui tipo e’ float. */

l’ indice dell'array puo’ spaziare da 0 fino a 19

Comportamento del codice

Con gli array molte delle operazioni diventano piu’ compatte se eseguite sulle variabili indicizzate piuttosto che su variabili isolate.

Ad esempio dovendo leggere dei dati sui negozi basta scrivere :

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

scanf (“%f”,&negozi[i]);

/*lettura della i-esima variabile*/

 

esercizio

 

Inizializzazione degli array monodimensionali

Il linguaggio C permette la loro inizializzazione in fase di dichiarazione.

L’ inizializzazione di un array di dimensione unitaria ha il seguente formato:

tipo nome_array[dimensione]={lista _di_valori};

La lista_di_valori e’ una lista di costanti separate da delle virgole. Il tipo di queste costanti deve essere compatibile con il tipo degli elementi dell’ array.

La prima costante che si incontra nella sequenza  lista_di_valori viene assegnata al primo elemento, la seconda costante al secondo elemento e cosi di seguito.

L'inizializzazione di un array in fase di esecuzione si puo' fare utilizzando ad esempio un ciclo di iterazione for.

Esempio

Inizializzazione di un array, di nome potenze_di_due, di interi formato da n=11 elementi:

int  potenze_di_due[11]={1,2,4,8,16,32,64,128,256,512,1024};

Con questa inizializzazione abbiamo assegnato all’ elemento dell’array di indice  i-esimo  il valore di 2 elevato ad i.

Per cui si avra’ che l’ elemento di indice 0 cioe’ potenze_di_due[0]  sara’ inizializzato ad 1 e cosi di seguito fino a potenze_di_due[10] che assumera’ il valore 1024.

 

Array a due dimensioni

 

Un array a due dimensioni  (detto anche matrice) consiste in pratica in un array di array ad una dimensione.

La forma generale per dichiarare un array bidimensionale e’ la seguente:

tipo  nome_array[dimensione1][dimensione2]

volendo dichiarare una matrice di nome matrix, di 6 righe per 4 colonne, i cui elementi siano di tipo intero, scriveremo:

int  matrix[6][4];

La matrice come noto e’ una struttura bidimensionale , quindi ci serve un indice i per scandire le righe ed un indice j per scandire le colonne.

Supponendo di voler inizializzare a 0 tutti gli elementi della matrice matrix, possiamo scrivere:

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

for  (j=0; j<4;   j++)

matrix[i][j]= 0 ;

/*queste istruzioni azzerano una dopo l’ altra la : riga 0 , riga 1, … ,riga 5*/

Notiamo quindi che nel linguaggio C vettori e matrici si dichiarano usando il costruttore di tipo [ ] , chiamato costruttore array.

Se volessimo  accedere all’ elemento avente coordinate 3, 4 dell’ array di nome matrix per assegnare il valore intero 55, scriveremmo:

matrix[3][4]=55;

 

/* tenere presente che tale elemento corrisponde alla 4 riga e 5 colonna della matrice corrispondente */

 

L’ array  di nome matrix  e’ cosi’ logicamente rappresentato :

 

 

          Secondo indice j

Primo

indice

i

0 , 0

0 , 1

0 , 2

0 , 3

1 , 0

1 , 1

1 , 2

1 , 3

2 , 0

2 ,1

2 , 2

2 , 3

3 , 0

3 , 1

3 ,2

3 , 3

4 , 0

4 , 1

4 ,2

4 , 3

5 , 0

5 , 1

5 ,2

5 , 3

       

Il primo indice e’ l’ indice di riga, il secondo indice e’ l’ indice di colonna.

L’ occupazione di memoria di un array bidimensionale si puo’ ricavare dalla seguente formula,(essendo sizeof(tipo) un operatore unario che agisce durante la compilazione e che restituisce il numero di byte che occupa il tipo di dato fra le parentesi tonde) :

Byte totali occupati =

= (dimensione di i)  X (dimensione j )X ( sizeof(tipo))

Ad esempio la nostra matrice di tipo intero matrix avra’:

Byte totali occupati = 6 X 4 X 2 = 48 byte

In quanto la dimensione del tipo intero in C e’ di 2 byte.

Inizializzazione degli array  bi-dimensionali

Il linguaggio C permette la loro inizializzazione in fase di dichiarazione.

L’ inizializzazione di un array di dimensione N=2 ha il seguente formato:

tipo nome_array[dimensione1][dimensione2]={lista _di_valori};  

La lista_di_valori e’ una lista di costanti separate da  virgole. Il tipo di queste costanti deve essere compatibile con il tipo degli elementi dell’ array.

La prima costante che si incontra nella sequenza  lista_di_valori viene assegnata al primo elemento, la seconda costante al secondo elemento e cosi di seguito.

Esempio 

 

Essendo la memoria una riga di byte tutti i suoi elementi stanno in "fila indiana" pertanto l'inizializzazione di un array multidimensionale constestuale alla sua dichiarazione puo' essere effettuata come mostrato di seguito:

L'inizializzazione di una matrice  di nome matrix[3][3] con le prime nove potenze di due:

int  matrix[3][3]={{1,2,4},{8,16,32},{64,128,256}};

Con questa inizializzazione abbiamo assegnato all’ elemento dell’array di indice  i-esimo  il valore di 2 elevato ad i.

Per cui si avra’ che l’ elemento di indici 0, 0, cioe’ matrix[0][0]  sara’ inizializzato ad 1 e matrix[0][1] assumera' il valore 2 ....matrix[0][2] assumera' il valore 4....cosi di seguito fino a matrix[2][2] che verra' inizializzato al valore 256.

Notiamo che gli elementi sono elencati nello stesso ordine con  cui si trovano nella memoria; dal  punto di vista logico, pero' ogni gruppo di elementi nelle coppie di parentesi  graffe interne rappresenta una riga della matrice.

 

Per avere una idea piu’ pratica di quanto detto consideriamo il seguente programma che carica in un array a due dimensioni i numeri da 1 fino a 20 e li stampa una riga per volta:

#include<stdio.h>

int main()

{

 int  matrix[5][4]  /* dichiarazione della matrice di nome matrix*/

 int  i , j ;             /* dichiarazione indice di riga ed indice di colonna*/

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

  for (j=0; j<4; j++)

  matrix[i][j]=(i*4)+j+1;

 /*stampa*/

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

 {

  for (j=0; j<4; j++)

   printf (“%3d ”,matrix[i][j]) ;

  /*%3d significa che riserva 3 cifre per il numero intero*/

   printf (“\n”) ; /* newline va a capo, ad ogni riga della matrice*/

 

return 0;

}

all’elemento matrix[0][0] viene assegnato il valore 1, a matrix[0][1] viene assegnato il valore 2 e cosi di seguito fino a 20. In pratica avremo la struttura seguente per l’array matrix:

 

 

0

1

2

3

0

1

2

3

4

1

5

6

7

8

2

9

10

11

12

3

13

14

15

16

4

17

18

19

20

 

Problema

Una societa’ possiede 15 negozi; dati i guadagni mensili di ciascun negozio (15 input )

1)    stampare la media  (Media)

2)    stampare quali negozi hanno guadagni inferiori ad 1/3 della media

3)    stampare la media dei guadagni tolto il migliore ed il peggiore (Media2)

4)    stampare quali negozi sono sotto alla Media2

algoritmo

1)    leggere i dati e memorizzarli nel vettore cioe’ nelle componenti dell’ array Negozi[i]

2)    calcolare la media (Media)

3)    scandire i negozi e stampare quelli il cui guadagno e’ inferiore ad 1/3 della Media

4)    trovare e stampare i/il negozi/o con il max guadagno (Max)

5)    trovare e stampare i/il negozi/o con il min guadagno (Min )

6)    calcolare la Media2 dei guadagni (detta media ridotta)

7)    scandire l’ array con stampa di indice e valore quando il valore e’ inferiore a Media2

Soluzione

Limitandoci per ora ai primi 3 punti dell’ algoritmo avremo:

1)    Leggere ogni elemento del vettore

(“scandire” il vettore componente per componente e assegnare alle componenti i dati letti)

questo si traduce in

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

{

 printf (“\n negozio %2d : “,i+1);

 scanf(“\n%f”,&Negozi[i]);

}

    2)

*  Inizializzazione di Somma_Guadagni a zero per calcolare la media Media

*  Sommare (ACCUMULARE) in Somma_Guadagni tutti i valori dell’ array

*  Media= Somma_Guadagni/15;

3)    Stampa dei negozi pecore nere

Per ogni negozio : se guadagno <Media/3

Stampa indice del negozio e guadagno ossia stampa di i e di Negozi[i]

   

Adesso definiamo due costanti ( valori cristallizzati nel codice ) con uno dei due metodi consenti dal linguaggio C

#define   DIM  15

#define   RAGIONE  1/3

Poi possiamo definire alternativamente

1)    float Negozi[DIM];

2)    typedef  float VETTORE_NEGOZI[DIM]; e poi

VETTORE_NEGOZI Negozi;

/* con questa seconda definizione abbiamo usato la parola chiave typedef per definire un array di nome VETTORE_NEGOZI formato da 20 componenti di tipo reale.

E poi abbiamo introdotto un nome Negozi che e’ di tipo VETTORE_NEGOZI.

Questa e’ la definizione dei tipi da parte dell’ utente. Infatti abbiamo anche per un tipo_array la seguente sintassi:

<tipo_array>::= typedef  <tipo><identificatore><array>

Il programma completo guadagni.c puo’ essere trovato al seguente link a cui vi rimando per una analisi completa e dettagliata : GUADAGNI DEI NEGOZI.

Versione del programma guadagni dei negozi usando le funzioni

Problema sulle matrici

Calcolare la matrice C prodotto di due matrici di interi quadrate A (n x n) e B(n x n) :  C=A*B ?

Esempio di matrice A ,con elementi generici aij , di dimensioni 3 x 3

 

A( 3 righe e 3 colonne )

          A

a11

a12

a13

a21

a22

a23

a31

a32

a33

 

 

 

 

 

 

 

Come certamente sara’ noto, il prodotto di due matrici quadrate (numero di righe uguale al numero di colonne) entrambe di dimensioni n x n ci fornira’ una matrice una nuova matrice C anche essa di dimensioni n x n, dove il generico elemento della matrice C detto Cij sara’ dato da: 

 

 

          n

Cij = ĺ  aik * bkj

        k=1

con       1 <= (i,j) <= n

               

 

Ricordiamo che  per eseguire il prodotto di due matrici generiche A (m x n ) e B(r x c)  e’ necessario che il numero di colonne della matrice A coincida con il numero di righe della matrice B ; nel nostro caso dobbiamo avere n = r ed otterremo una matrice prodotto  C (m x c) avente un numero di righe pari alle righe di A ed un numero di colonne pari alle colonne di B.

Detto cio’ avremo che per le matrici quadrate il prodotto e’ sempre eseguibile dal punto di vista algebrico e la formula per calcolarla sara’ la sopradetta.

 Adesso scriviamo il programma per calcolare C sapendo che la dimensione n delle due matrici quadrate A e B e’ pari a 10.

#include<stdio.h>

#define  n  10   /* numero di righe e colonne delle matrici */

int main ( )

{

 int  A[n][n] ;

 int  B[n][n];

 int  C[n][n];

 int  i, j, k; /* indici per scandire le matrici */

/* lettura matrice A */

printf (“\nInserite gli N= %d elementi della matrice A\n”, n*n);

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

for (j=0 ; j<n ; j++)

scanf (“\n%d”,&A[i][j]);

/* lettura matrice B */

printf (“\nInserite gli N= %d elementi della matrice B\n”, n*n);

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

for (j=0 ; j<n ; j++)

scanf (“%d”,&B[i][j]);

 /* Calcolo della matrice prodotto C */

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

  for (j=0 ; j<n ; j++)

  {

   C[i][j]=0; /* inizializzazione della matrice prodotto*/

   for (k= 0; k<n ; k++)

   C[i][j] =  C[i][j] + A[i][k] * B[k][j];

  }

 /* Stampa della matrice prodotto */

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

 for (j=0 ; j<n ; j++)

  printf (“%6d ”, C[i][j]);

   printf (“\n”); /* Per andare a capo ad ogni riga */

   }

return 0;}

 

Puntatori e array 
aritmetica dei puntatori

Nella parte destra di una espressione vi possono tranquillamente essere dei puntatori ed e’ possibile assegnare il valore di questi puntatori ad altri puntatori.

Nel linguaggio C si possono usare soltanto due  operatori aritmetici sui puntatori: l’addizione + e la sottrazione - .

 

Esempio

 

Se punt1 e’ un puntatore ad interi, l’espressione *punt1 , si puo’ utilizzare come qualsiasi variabile intera.

 

int  k,  i=1, *punt1;

punt1 = &k;     /*inizializzazione di punt1:ora in punt1 vi e’ l’indirizzo di k*/

punt1 =  i;       /* la locazione associata a k ora contiene 1*/

*punt1 = 25;  /* la locazione associata a k ora contiene 25*/

i = *punt1;    /*alla variabile i viene assegnato 25*/

 

Esempio

 

Consideriamo la seguente dichiarazione di un puntatore ad un intero:

int  *punt1;

 

Come noto il tipo intero occupa solitamente 2 byte in  memoria (2 celle); per cui nel caso il valore corrente del puntatore punt1 e’ 1500 , dopo la seguente espressione

punt1 = punt1 +1 ; il contenuto di punt1 diventa 1502, per poter puntare all’ intero successivo e non alla cella successiva. Idem per il decremento;

punt1 = punt1 +1; equivale a punt1++;

punt1--; avremmo come contenuto di  punt1  1498.

 

Nota: La maggior parte dei computer oggigiorno utilizzano interi di 2 o 4 byte. Alcune macchine piu’ recenti utilizzano interi di 8 byte.

 

Se fosse stata char *punt1; (tutto semplice poiche’ il carattere occupa  1 byte (1cella) ).

 

 

Indirizzo di memoria

Contenuto della memoria

variabili

1498

.

punt1--

1499

.

.

1500

.

punt1

1501

.

.

1502

.

punt1++

1503

.

.

1504

.

.

1505

.

.

.

.

 

 

 

Uniche operazioni consentite con i puntatori sono : incremento ,decremento e somma e sottrazione di interi.

 

Importante ricordare sempre: che gli array nel linguaggio C sono gestiti con un meccanismo di allocazione statica della memoria mentre con  i puntatori si possono utilizzare meccanismi di allocazione dinamica. Quindi array e puntatori sono dei concetti compenetrati in C; il loro uso e’ in sostanza intercambiabile.

Il nome di un array e’ il puntatore all’indirizzo di memoria della prima componente del vettore .

Nota: per vettore si intende un array monodimensionale

Se il vettore e’ cosi’ dichiarato: int  vector[20]; 

avremo che vector punta alla locazione corrispondente all’intero vector[0].

L’aritmetica dei puntatori e’ diversa da quella delle altre variabili; se un puntatore a <tipo> riceve un incremento di 1, il valore dell’indirizzo che esso contiene non viene necessariamente aumentato di 1;  verra’ aumentato di una quantita’ di byte uguale alla dimensione del tipo di locazione puntata;

 

 

Tra puntatori ed array esiste come detto una relazione molto stretta derivante dal fatto che gli elementi di un array vengono allocati in memoria in “celle” consecutive. Per capire tale tipo di relazione consideriamo le seguenti istruzioni:

 

char  strin[81] , *punt1;

punt1 = strin;

 

Per definizione al puntatore punt1 si assegna l’indirizzo del primo elemento dell’array strin (il valore di una variabile strin di tipo array e’ l’indirizzo del suo elemento zero cioe’ &strin[0]). Per cui le due espressioni strin ed &strin[0] sono equivalenti; in altre paraole l'espressione strin==&strin[0] e' vera.

Questo avviene poiche’ , come detto, nel linguaggio C, il nome dell’array senza gli indici, e’ considerato l’indirizzo iniziale dell’array; quindi il nome dell’array e’ un puntatore all’array.

Nel caso volessimo accedere alla componente 30 dell’array strin, possiamo usare una delle due equivalenti espressioni:

strin[29] oppure *(punt1+29) poiche' qualunque variabile di ti po puntatore puo' essere dotata di indici come se fosse stata dichiarata come array.

In generale alla componente i-esima dell’array strin si accede con

strin[i-1] oppure con *(punt1 + i - 1);

inoltre  *(punt1 +i)  equivale a punt1[i] .

Assegnamento con l'aritmetica dei puntatori: *(punt1+3)=100; assegnamento tramite indice: punt1=strin; punt1[3]=100;

 

esempio

 

 

 

 

 

 

 

 

Osservazione

Dato che l’array e’ una sequenza di un certo numero di consecutive locazioni di un certo tipo stabilito, conoscendo il puntatore alla prima e’ possibile raggiungerle tutte.

int  vector[20];  /*array di 20 interi*/

int  *pa, i;   /*pa puntatore a interi*/   …..

pa = &vector[0];

/* pa ora contiene l’indirizzo della prima locazione dell’array, che e’ un intero*/

pa = vector; /* corretta poiche’ vector e’ praticamente un puntatore ad un intero*/

tabella di equivalenze: sia vector un array di interi e pa un puntatore a interi

pa=vector;

equivale a

pa=&vector[0];

vector

equivale a

&vector[0]

*(vector+i)

equivale a

vector[i]

*vector

equivale a

vector[0]

(vector+i)

equivale a

&vector[i]

In sostanza , la scansione di un array puo’ essere fatta anche con l’uso di un puntatore.

Nelle matrici si puo' accedere agli elementi, anche, sia tramite indice che tramite puntatore . esempio: int matrix[5][4]; int *punt1; punt1=matrix; avremo che all'elemento matrix[0][3] si puo' accedere con puntatore con *(punt1+3); in generale a matrix[i][j] corrisponde *(punt1+(i*dimensione_riga)+j).

Scansione di un array con puntatori

La scansione di un array puo' essere effettuata attraverso i puntatori vista la stretta relazione esistente fra i due:

vediamo un esempio 

#include<stdio.h>

int main()
{

static int nome_array[]={4,6,2,44,55,77};
int *punt_array;
char car;
punt_array=nome_array;
/*punt_array punta al primo elemento dell'array nome_array*/

while(*punt_array)
{ printf("%X -> %d\n", punt_array,*punt_array);
++punt_array; /*incrementa punt_array*/
}
scanf("%c",&car);
return 0;}
/* nel ciclo while avremo che finche' l'intero puntato da punt_array non e' 0
viene stampato punt_array e l'intero puntato. Lo specificatore di formato %X visualizza in esadecimale */

Test intermedio

 

Passaggio di array a funzioni

Il linguaggio C passa un array ad una funzione, sempre per indirizzo, in quanto  in fase di compilazione il nome dell'array e' automaticamente associato all'indirizzo del primo dell'elemento.

int  nome_funzione( int  nome_array[5]);  e' un prototipo di funzione con parametro un array di 5 interi.

Nella chiamata della funzione scriveremo nome_funzione(nome_array); 

che equivale a nome_funzione(&nome_array[0]);

per cui possiamo sostituire la definizione di un argomento di tipo array con la dichiarazione di un parametro puntatore al tipo_elementi dell'array; ossia il prototipo su scritto diventerebbe:

int  nome_funzione( int  *vettore);

vettore e' un puntatore ad interi.

esercizio

 

Stringhe

 

Nel linguaggio C gli  array ad una dimensione e’ possibile utilizzarli come stringhe di caratteri, ossia array i cui elementi sono di tipo carattere; e' da ricordare bene che l’ultimo carattere dell’array e’ il carattere NULL ( indicato come ‘\0’ e rappresentato in memoria con uno 0 ).

"notare che questa di utilizzare gli array come stringhe non e' un buon metodo di programmazione in C"

Se volessimo dichiarare un array di nome strin che contiene una stringa lunga 5 caratteri si deve usare la seguente dichiarazione:

char  strin[6];

ossia dobbiamo tenere conto del fatto che bisogna inserire una cella per il carattere, di terminazione della stringa, NULL. Quindi sempre la lunghezza dell’array per le stringhe deve essere superiore di 1 alla lunghezza della stringa piu’ grossa che si intende rappresentare.

Il linguaggio C  non possiede un tipo di dato stringa , ma consente la dichiarazione di costanti stringa, come una sequenza di caratteri racchiusa fra virgolette.

Un esempio di costante stringa puo’ essere :

 “io sono una stringa”.

La lunghezza di questa costante stringa e’ di 19 caratteri contando gli spazi bianchi che sono dei caratteri pure essi; quindi per essere contenuta dentro un array di nome strin necessita che l’array abbia una lunghezza pari a 20 ; 19 caratteri +1 per contenere il carattere di terminazione NULL.

/* Dichiarazione dell'array strin necessario a contenere tale stringa */

char strin [20]; 

Il metodo corretto per dichiarare e manipolare le stringhe  nei programmi e'  attraverso l'uso dei puntatori, come possiamo vedere nel seguente programma che visualizza la stringa "fondamenti di informatica"  ed attende con la funzione scanf(), l'inserimento di un carattere.

 

#include<stdio.h>
int main()
{

char *strin="fondamenti di informatica";
char car;
printf("%s\n",strin);
scanf("%c",&car);
return 0;}

strin e' un puntatore alla stringa "fondamenti di informatica" 

*strin contiene solo il primo carattere della stringa ossia il carattere 'f' ;

per accedere ai caratteri successivi al primo e' necessario eseguire una scansione incrementando il puntatore di una opportuna quantita'. 

Il programma seguente visualizza la stringa "fondamenti di" in senso verticale:

 

#include<stdio.h>
void scansione();
int main()
{

char *strin="fondamenti di"
char car;
printf("%s\n",strin);
scansione();
scanf("%c",&car);
return 0;}

void scansione()
{
int i=0;
while(*(strin+i) !=NULL)
{
printf("%c\n", *(strin +i));
i++;
}
}

APRIAMO UNA PARENTESI PER DISCUTERE DELLA FUNZIONE SCANF()

La funzione scanf() puo' leggere dati dalla tastiera convertendo i numeri nella loro rappresentazione interna, piu' opportuna. Essa e' contenuta in stdio.h ed ha il seguente prototipo:

int scanf(char *stringa_di_controllo, elenco_argomenti); 

essa restituisce il numero  di oggetti a cui vene assegnato un valore ed EOF nel caso di errore.

La stringa_di_controllo determina la modalita' con cui vengono lette le variabili a cui punta l'elenco_ argomenti; la stringa di controllo o di formato  e' sempre presente.

L'elenco degli argomenti o parametri, enumera le variabili dove devono essere memorizzati i dati in lettura.

 

avendo int i,j; scanf("%d",&i); avremo che la scanf() legge un intero e lo assegna alla variabile i.

 

 

Tabella dei specificatori di formato di scanf() e printf()

 

 

codice formato
%c singolo carattere
%d decimale intero
%i decimale intero
%e formato scientifico
%E formato scientifico
%f floating point
%o ottale
%s stringa di caratteri
%x esadecimale
%X esadecimale
%p puntatore (a void)
%u intero senza segno

 

La funzione scanf() puo' essere usata per leggere una stringa dal flusso di input mediante lo specificatore di formato %s. Pero' la scanf() legge fino a quando incontra un carattere di spazio, teminando la stringa con un carattere nullo, a differenza della funzione gets() che tratta gli spazi come dei caratteri.

Tutte le variabili utilizzate da scanf() devono essere passate per indirizzo; e' chiaro che avendo dichiarato char strin[21]; per memorizzarvi dati letti da input in strin basta scrivere 

scanf("%s", strin); non viene richiesto l'operatore & , poiche' strin rappresenta un puntatore come sappiamo.

Fine parentesi

 

Per manipolare le stringhe esistono delle funzioni di libreria contenute in "string.h" tipo :

strcpy (s1,s2); la quale copia la stringa s2 nella stringa s1.

il prototipo della strcpy() e':

char *strcpy(char *s1, char *s2);

la stringa puntata da s2 deve terminare con un carattere nullo. La funzione strcpy restituisce un puntatore ad s1.

 

strlen (s1); che e' una funzione  restituisce la lunghezza della stringa puntata da s1;

il prototipo della strlen() e':

size_t strlen(char *s1);

la stringa s1 deve essere terminata da un carattere nullo che pero' non viene conteggiato.

strcmp (s1,s2) compara, secondo l'ordinamento alfabetico le due stringhe terminate da un carattere nullo;

il prototipo della funzione e:

int strcmp(const char *s1, const char *s2);

 

restituisce 0 se s1 ed s2 sono uguali, un valore > 0 se s1>s2 , un valore < 0 se s1<s2

N.B. la funzione strcmp() vale falso se le due stringhe sono uguali, pertanto bisogna usare l’operatore !  (NOT) per invertire la condizione e verificare l’ eguaglianza fra stringhe;

esempio

Se volessimo verificare se la stringa s1 sia uguale alla stringa

“io sono una stringa”

scriveremmo : 

if  ( !strcmp (s1, “io sono una stringa”))  

printf (“\n%s”, le due stringhe sono uguali );

L’ assegnazione  all’ array strin, della stringa  “io sono una stringa” , puo’ essere fatta attraverso la seguente istruzione:

strcpy (strin, “io sono una stringa”);

Queste funzioni sono contenute nel file di libreria del compilatore string.h . Per cui se volessimo fare uso dentro il nostro programma di tali funzioni dovremmo includere come file di intestazione la string.h , attraverso la seguente direttiva per il preprocessore:

#include<string.h>

esercizio_strcpy   esercizio_ strlen   esercizio_ strcmp

esercizio sulle stringhe

Array di stringhe

 

Nella programmazione si possono dichiarare gli array di stringhe attraverso una matrice di caratteri , in cui con il primo indice i spazia lungo  il numero max di stringhe ed  il secondo indice j  spazia sulla “ lunghezza massima ammissibile +1” di una  stringa.

Questo lo si fa’ con la seguente dichiarazione:

char  stringhe_caratteri[20][81];

Abbiamo dichiarato una matrice od array bidimensionale di stringhe avente nome stringhe_caratteri, precisamente 20 stringhe ognuna formata da un massimo di 80 caratteri. Da notare, che indipendentemente dalla lunghezza delle stringhe di caratteri che inseriremo nell’array, di nome stringhe_caratteri, la memoria allocata (riservata) per tale tipo di dato e’ sempre la stessa.

Ossia con questo tipo di dichiarazione si ha la allocazione statica della memoria. Infatti si puo’ calcolare a priori la memoria che bisogna riservare per tale dato.

La memoria occupata dall’array stringhe_caratteri e’ pari a:

Numero totale di byte = 20 x 81 x sizeof(char) ;

Siccome nel linguaggio C un carattere occupa 1 byte avremo che sizeof(char)=1 byte

per cui : Numero totale di byte=20 x 81 x 1 = 1620 byte .

 

 

 

 

esercizio 

 

 Test 15