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
ijdella 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);
}
Nel documento
Il linguaggio C dispense del corso di Laboratorio di Algoritmi e Strutture Dati A.A. 2001/2002
(pagine 31-36)