• Non ci sono risultati.

int info;

struct nodo *left;

struct nodo *right;

} node;

viene de¯nito un alias tt lunghezza per il tipo intero, e un alias node per

il tipo struct nodo. Gli alias aumentano la legibilitµa e la portabilitµa di

un programma. Inoltre, permettono nel caso delle strutture e dei tipi

enu-merati, di non ripetere la parola chiave struct o enum ogni qualvolta si

voglia dichiarare una nuova variabile di quel tipo. Dopo una de¯nizione con

typedef, la dichiarazione

struct nodo* anodeptr;

µe equivalente alla dichiarazione

node* anodeptr;

6.3 Ulteriori aggregazioni

µ

E possibile in C de¯nire aggregazioni piµu complesse a partire dalle regole e

dai costrutti visti ¯nora. In particolare, vediamo in questo paragrafo come

realizzare array multidimensionali e array di puntatori.

6.3.1 Array multidimensionali

Ricordando che l'i-esimo elemento di un array monodimensionale µe

individ-uato speci¯cando il nome dell'array e l'indice i di o®set, possiamo estendere

questo concetto al caso di piµu dimensioni nel seguente modo (per il

mo-mento, supponiamo di voler de¯nire array bidimensionali, ma il discorso µe

generalizzabile a qualunque dimensione):

dichiara come automatic una matrice di 10 righe e 5 colonne. Gli indici di

riga variano da 0 a 9, mentre gli indici di colonna variano da 0 a 4. Il generico

elemento a

ij

della matrice µe indirizzato con l'espressione

int a[i][j];

Un array multidimensionale viene memorizzato per righe. Essendo i nomi di

array puntatori al primo elemento dell'array, nella matrice a de¯nita sopra

a rappresenta il puntatore alla prima riga (si noti che una riga µe un array

monodimensionale), e a+i il puntatore alla (i+1)-esima riga.

Conseguente-mente, il tipo di a µe int**. Forniamo a titolo di esempio un frammento di

codice che realizza una funzione che stampa tutti gli elementi di una matrice

bidimensionale.

void printmatrix(int** a, int m, int n)

{

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

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

printf("\n elemento %d-%d: %d", i, j, a[i][j]);

)

Si puµo notare come, per passare un array (di qualunque numero di

dimen-sioni) come parametro, occorre passare il nome dell'array e le sue dimensioni.

6.3.2 Array di puntatori

Abbiamo visto inx6.3.1 che una matrice di interi µe un array (righe) di array

(colonne) di interi. Poich¶e in C un array µe anche un puntatore, il discorso µe

generalizzabile ad array di puntatori qualunque. In questo modo possiamo

collezionare dati complessi, ad esempio di un tipo de¯nito dall'utente. A

titolo di esempio, vediamo come realizzare un array di puntatori a strutture,

in modo da organizzare in modo omogeneo oggetti di tipo struttura.

int i, n=10;

/* definisci il tipo persona */

struct persona {

char nome[20];

char cognome[20];

char sesso;

};

/* definisci un array di puntatori a persona */

struct persona **persone;

/* alloca memoria per l'array di puntatori */

persone = (struct persona**)malloc(n*sizeof(struct persona*));

/* inserisci informazioni nell'array di puntatori */

while(i<n)

{

/* alloca memoria per una nuova struttura */

struct persona *p = (struct persona*)malloc(sizeof(struct persona));

/* poni il puntatore alla nuova struttura nell'array */

persone[i] = p;

i++;

}

A seguito di questa dichiarazione, si puµo recuperare, ad esempio, il nome

della i-esima persona con

char *p = persone[i]->nome;

Ovviamente, anche le variabili di tipo struttura possono essere collezionate in

un array, ma questo comporta la scrittura sequenziale in memoria delle

infor-mazioni nelle strutture, cosa non richiesta negli array di puntatori. Inoltre,

con i puntatori la manipolazione dell'array risulta piµu e±ciente.

Capitolo 7

Input e Output

Il linguaggio non dispone di istruzioni di ingresso/uscita. Il compito di

in-terfacciare un programma con l'esterno µe delegato a un insieme di funzioni

che fanno parte della libreria standard di input/output (stdio). Come ogni

altra libreria, per poter usare le sue funzioni dobbiamo includere nel nostro

¯le sorgente il ¯le tt stdio.h (e linkare il suo codice oggetto). Tra le funzioni

piµu comunemente usate in questa libreria, abbiamo:

getchar legge un carattere dallo standard input (tastiera)

putchar scrive un carattere sullo standard output (terminale)

scanf legge una sequenza di caratteri dallo standard input

printf scrive una sequenza di caratteri sullo standard output

fscanf legge una sequenza di caratteri da un ¯le

fprintf scrive una sequenza di caratteri su un ¯le

La funzione getchar non ha parametri, e restituisce il carattere letto, mentre

la funzione putchar prende come parametro il carattere da stampare. Nella

lettura tramite getchar, il carattere speciale CTRL+D µe considerato come

la costante EOF (¯ne del ¯le) per lo standard input. A titolo di esempio,

riportiamo un frammento di codice che legge caratteri da tastiera e li stampa

sul terminale, ¯no a che non si digita CTRL+D.

main()

{

int c;

while ((c=getchar())!= EOF)

putchar(c);

}

La sintassi delle funzioni scanf e printf µe la seguente:

scanf(string, arg1, arg2, ..., argn);

printf(string, arg1, arg2, ..., argn);

La stringa string µe composta da caratteri che vengono stampati su standard

output, e da speci¯catori di formato. Gli speci¯catori di formato istruiscono

il compilatore su come interpretare la sequenza in input, nel caso di scanf, e

in output nel caso di printf. Il numero degli speci¯catori di formato presenti

deve essere pari a quello degli argomenti, e la corrispondenza tra essi avviene

nell'ordine in cui compaiono. Ad esempio, in

scanf("\n la codifica ASCII del carattere %c e' ", c);

printf("%d", c);

il carattere c viene considerato in lettura come carattere, e in scrittura come

un intero (la sua rappresentazione in codice ASCII). Gli speci¯catori di

for-mato iniziano con il carattere % e sono seguiti da un carattere di conversione.

I possibili caratteri di conversione sono i seguenti:

'd' intero decimale

'o' intero ottale

'x' intero esadecimale

'h' intero short

'c' carattere

's' stringa

'f' reale °oat

Per la funzione scanf µe inoltre de¯nito il carattere di conversione 'lf' per

i reali double, mentre per la printf µe de¯nito in aggiunta a quelli elencati

in tabella il carattere di conversione 'e' che permette di stampare un reale,

°oat o double, in notazione esponenziale.

Le funzioni fprintf e fscanf sono del tutto equivalenti alle printf e scanf,

ma hanno un ulteriore parametro che speci¯ca il puntatore a un oggetto di

tipo FILE, dove scrivere o leggere i dati. Quando si vuole che l'input venga

letto da un ¯le e non da tastiera, oppure che l'output venga reindirizzato su

¯le anzich¶e su terminale, bisogna usare queste due funzioni. L'uso di fprintf

e fscanf presuppone che il ¯le sia stato aperto (istruzione fopen) e che il

tipo di operazione (lettura o scrittura) sia compatibile con la modalitµa con

la quale il ¯le µe stato aperto. La struttura FILE e la funzione fopen sono

dichiarate nella stdio.h.

La fopen speci¯ca con un parametro di tipo stringa se il ¯le µe aperto in

lettura ("r"), in scrittura ("w"), in scrittura in coda ("a", non cancella il

contenuto del ¯le ma appende il nuovo output). Una operazione di apertura

di un ¯le µe sempre seguita da un'operazione di chiusura (fclose). Vediamo

a titolo di esempio un programma che stampa su ¯le l'e®etto di un ciclo for.

#include <stdio.h>

main(int argc, char*argv[])

{

FILE *fp;

if (argc != 2) /* programma (argv[0]), file output (argv[1]) */

{

printf("errore! numero di parametri non valido");

exit;

}

if ((fp=fopen(argv[1]), "w")!= NULL)

{

printf("errore! impossibile aprire il file");

exit;

}

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

fprintf(fp, "\nindice i alla %d-esima iterazione", i);

}

Documenti correlati