Sei sulla pagina 1di 119

Indice

Voci
C/Copertina 1
C/Linguaggio 2
C/Linguaggio/Panoramica 2
C/Linguaggio/Struttura del linguaggio 4
C/Variabili, operatori e costanti 5
C/Variabili, operatori e costanti/Variabili 5
C/Variabili, operatori e costanti/Operatori aritmetici 11
C/Variabili, operatori e costanti/Operatori sui bit 12
C/Variabili, operatori e costanti/Costanti 15
C/Blocchi e funzioni 17
C/Blocchi e funzioni/Blocchi if e switch 17
C/Blocchi e funzioni/Operatori logici 22
C/Blocchi e funzioni/Cicli 23
C/Blocchi e funzioni/Funzioni 25
C/Blocchi e funzioni/main 27
C/Blocchi e funzioni/Librerie 29
C/Blocchi e funzioni/Ricorsività 29
C/Vettori e puntatori 31
C/Vettori e puntatori/Vettori 31
C/Vettori e puntatori/Puntatori 35
C/Vettori e puntatori/Interscambiabilità tra puntatori e vettori 41
C/Variabili, operatori e costanti/Stringhe 43
C/Enumerazioni, strutture e unioni 44
C/Enumerazioni, strutture e unioni/Enumerazioni 44
C/Enumerazioni, strutture e unioni/Strutture 46
C/Enumerazioni, strutture e unioni/Unioni 49
C/Conversioni di tipo 50
C/Lettura e scrittura su file 52
C/Compilatore e precompilatore 55
C/Compilatore e precompilatore/Compilatore 55
C/Compilatore e precompilatore/Direttive 56
C/Compilatore e precompilatore/Header 61
C/Visibilità 62
C/Gestione della memoria 65
C/Le applicazioni CGI 67
C/Appendice/Librerie standard 74
C/Appendice/Librerie standard/assert.h 75
C/Appendice/Librerie standard/ctype.h 77
C/Appendice/Librerie standard/errno.h 78
C/Appendice/Librerie standard/float.h 79
C/Appendice/Librerie standard/limits.h 80
C/Appendice/Librerie standard/locale.h 81
C/Appendice/Librerie standard/math.h 84
C/Appendice/Librerie standard/setjmp.h 85
C/Appendice/Librerie standard/signal.h 87
C/Appendice/Librerie standard/stdarg.h 91
C/Appendice/Librerie standard/stddef.h 92
C/Appendice/Librerie standard/stdio.h 92
C/Appendice/Librerie standard/stdlib.h 101
C/Appendice/Librerie standard/string.h 102
C/Appendice/Librerie standard/time.h 103
C/Appendice/Approfondimenti 109
C/Appendice/Strumenti 109
C/Indice 110

Note
Fonti e autori delle voci 114
Fonti, licenze e autori delle immagini 116

Licenze della voce


Licenza 117
C/Copertina 1

C/Copertina
Benvenuto nel wikibook:
iL C
Autore: La Comunità

Vai ai contenuti >>


Fase di sviluppo: C
C/Linguaggio 2

C/Linguaggio
Il linguaggio C è un linguaggio di programmazione che segue il paradigma della programmazione imperativa. Il
linguaggio ha rivestito, e riveste tutt'ora, un ruolo importante nella tecnologia informatica:
• È il linguaggio di sistema dei sistemi operativi Posix (Unix e derivati, come BSD, Linux, Mac OS X).
• È il linguaggio con cui sono sviluppati la maggior parte dei sistemi operativi e dei device driver (moduli di
gestione di periferiche).
• Sebbene in declino per lo sviluppo di software applicativo, esistono tuttora moltissime applicazioni sviluppate in
C e la cui manutenzione costa meno di una completa riscrittura in un altro linguaggio.
• Da esso sono derivati vari moderni linguaggi di programmazione, principalmente C++, Objective C, Java e C#.
Tali linguaggi condividono in misura variabile il lessico, la sintassi e la logica del C. Molti considerano pertanto
l'apprendimento del C propedeutico all'apprendimento tali linguaggi.
Può invece astenersi dall'imparare il linguaggio C chi non è interessato al software di sistema e vuole utilizzare un
linguaggio non derivato dal linguaggio C.

C/Linguaggio/Panoramica
Un linguaggio di programmazione
Il primo computer fu programmato con il linguaggio macchina, ossia un linguaggio formulato tramite una sequenza
di bit. Questo modo di programmare è detto di bassissimo livello, la vita si alleggerì un po' con l'avvento
dell'assembler: una parola per identificare un'operazione, una condizione o un ciclo, anche se il codice era molto
laborioso visto che si aveva a che fare direttamente con i registri del processore (che variavano da modello a
modello). Questo era ottenuto grazie a un traduttore, che traduceva il codice scritto in linguaggio macchina.
Successivamente nacquero i linguaggi di alto livello, chiamati così perché riducono molto il codice donandogli
compattezza e una maggiore comprensibilità. Alcuni esempi di questi linguaggi sono il Pascal per le applicazioni
didattiche, il Fortran per applicazioni scientifiche, il BASIC, il Cobol per citarne alcuni, e naturalmente il C.

Le particolarità del C

È un linguaggio a medio livello


Quello che differenzia il C dagli altri linguaggi di programmazione è proprio il fatto che è considerato Il linguaggio
di più basso livello tra quelli di alto livello, proprio in questa affermazione va ricercato il fatto che il C è ampiamente
utilizzato per programmare driver, sistemi operativi o comunque applicazioni che richiedono un controllo diretto
sulle periferiche.

Strutturato
Ogni programma viene visto come un comando (complesso, o strutturato) ottenuto componendo altri comandi
mediante 3 strutture di controllo: concatenazione, alternativa e ripetizione.
C/Linguaggio/Panoramica 3

Con tipizzazione debole


Nel senso che un dato di un particolare tipo può essere visto dal programma, tramite particolari tecniche, come di un
altro tipo o addirittura convertito in un altro tipo. Inoltre alcune conversioni da un tipo all'altro sono implicite, altre
possono essere forzate.

È case-sensitive
Il C è case-sensitive: pertanto ab, AB, Ab e aB saranno quattro variabili diverse.

Storia del C
Fu ideato nei Bell Laboratories della AT&T nel 1972 da Dennis Ritchie come evoluzione del linguaggio B di Ken
Thompson usato per la scrittura dei primi sistemi operativi UNIX. Lo stesso Thompson nel 1970 si era a sua volta
ispirato al linguaggio BCPL di Martin Richards, anch'esso pensato per scrivere sistemi operativi e software di
sistema. La definizione formale si ha nel 1978 a cura di B. W. Kernighan e D. M. Ritchie. Nel 1983 iniziò il lavoro
di definizione di uno standard da parte dell'American National Standards Institute, che rilasciò nel 1990 lo Standard
ANSI C (ISO C89).

Varianti e i linguaggi derivati


Lo standard venne anche adottato dall'International Organisation for Standardisation (ISO) nel 1999 con il nome di C
Standard ANSI/ISO. Nel 1995 fu adottato l'Emendamento 1 al C Standard che, fra le altre cose, ha aggiunto nuove
funzioni alla libreria standard del linguaggio. Usando come documento base il C89 con l'Emendamento 1, e
unendovi l'uso delle classi di Simula, Bjarne Stroustrup iniziò a sviluppare il C++.
Il risultato finale del continuo sviluppo del C fu lo standard promulgato nel 1999, noto come ISO C99.
Il linguaggio Objective C deve la sua popolarità alla Apple che lo ha scelto come base per il suo sistema operativo
Mac OS X.
È un linguaggio orientato agli oggetti e a differenza del C++ mantiene la piena compatibilità con il C.

Un semplice programma
Partiamo introducendo il classico programma Hello World!.

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
printf("Hello World!");
system("PAUSE"); //permette di fermare il programma
}

Ecco l'analisi del codice:


• #include <stdio.h> consente di poter usare la funzione printf che consente di scrivere sullo schermo;
• int main(void){: è la funzione principale del programma (vedere La funzione main);
• printf("Hello World!");: stampa la stringa Hello World!;
Esamineremo in dettaglio tutte queste istruzioni nei prossimi capitoli.
C/Linguaggio/Struttura del linguaggio 4

C/Linguaggio/Struttura del linguaggio


Il C è un linguaggio che svolge le operazioni in modo sequenziale, questo sta ad indicare che verrà fatta
un'esecuzione dall'alto verso il basso.
Da questa nozione si deduce che bisognerà stare molto attenti sull'ordine delle istruzioni che poi il compilatore dovrà
eseguire.
Inoltre un sorgente C è diviso in funzioni; la struttura generale di un programma è la seguente:

Dichiarazioni globali

tipo funzione_principale (argomenti)


{
funzione (argomenti);
}
tipo funzione (argomenti)
{
codice;
}

e passando alla sintassi C, la struttura base è la seguente:

int main (void)


{
qui va il codice
}
C/Variabili, operatori e costanti 5

C/Variabili, operatori e costanti


Questa sezione tratta delle variabili, degli operatori e delle costanti. Ecco le sotto-sezioni:
• Variabili -
• Operatori aritmetici -
• Operatori sui bit
• Costanti -

C/Variabili, operatori e costanti/Variabili


Una variabile in C (e in programmazione in generale) è uno spazio di memoria che contiene un valore. Una
variabile ha un nome che viene usato nella programmazione per riferirsi a un certo particolare spazio di memoria.

Nozioni minime
I tipi di dati elementari gestiti dal linguaggio C dipendono dall'architettura dell'elaboratore sottostante. In questo
senso è difficile definire la dimensione delle variabili numeriche; si possono dare solo delle definizioni relative.
Solitamente, il riferimento è dato dal tipo numerico intero (int) la cui dimensione in bit corrisponde a quella della
parola, ovvero dalla capacità dell'unità aritmetico-logica del microprocessore.
I documenti che descrivono lo standard del linguaggio C, definiscono la «dimensione» di una variabile come rango.

Bit, byte e caratteri


Il riferimento più importante in assoluto è il byte, che per il linguaggio C è almeno di 8 bit, ma potrebbe essere più
grande. Dal punto di vista del linguaggio C, il byte è l'elemento più piccolo che si possa indirizzare nella memoria
centrale, questo anche quando la memoria fosse organizzata effettivamente a parole di dimensione maggiore del
byte. Per esempio, in un elaboratore che suddivide la memoria in blocchi da 36 bit, si potrebbero avere byte da 9, 12,
18 bit o addirittura 36 bit.
Una volta definito il byte, si considera che il linguaggio C rappresenti ogni variabile scalare come una sequenza
continua di byte; pertanto, tutte le variabili scalari sono rappresentate come multipli di byte; di conseguenza anche le
variabili strutturate lo sono, con la differenza che in tal caso potrebbero inserirsi dei «buchi» (in byte), dovuti alla
necessità di allineare i dati in qualche modo.
Il tipo char (carattere), indifferentemente se si considera o meno il segno, rappresenta tradizionalmente una variabile
numerica che occupa esattamente un byte.

Dichiarazione
Per essere usata una variabile va prima dichiarata. La dichiarazione è la seguente:

tipo nome_variabile;

che dichiara una variabile il cui nome è nome_variabile e il cui tipo è tipo. Per esempio se vogliamo dichiarare una
variabile di tipo intero possiamo farlo nel seguente modo:

int numero;

dove int è un tipo di variabile usato per rappresentare i numeri interi.


C/Variabili, operatori e costanti/Variabili 6

È possibile dichiarare più variabili dello stesso tipo su una sola riga, per esempio:

int a, b;

dichiara due variabili di tipo int.


Le variabili possono essere inizializzate durante la dichiarazione:

int c = 1;

se non si inizializza una variabile si dice che il suo valore è indeterminato, nel senso che non si può dire che cosa
contenga. Tipicamente una variabile non inizializzata contiene garbage, spazzatura, cioè contiene quello che è
presente nel suo indirizzo al momento dell'allocazione. Alcuni compilatori azzerano le variabili appena dichiarate.
Poiché il comportamento del compilatore in genere non è noto, e poiché il nostro programma dovrà essere
compatibile col maggior numero di compilatori il programma dovrà inizializzare le varibili prima dell'uso. Se si
dichiarano più variabili sulla stessa riga possono essere o meno inizializzate:

int d, e = 2;

che crea due variabili, ma solo la seconda è inizializzata

int f = 1, g = 2;

tutte e due sono inizializzate.


esiste anche la possibilità di effettura una dichiarazione concatenata:

int a=b=3;

entrambe le variabili vengono inizializzate al valore 3

Nomi di variabili
Il nome delle variabili viene definito dal programmatore. La scelta di un particolare nome non ha influenza
sull'esecuzione del programma. Esistono le seguenti regole:
• due variabili non possono avere lo stesso nome
• si possono usare sia le lettere che i numeri, ma il primo carattere deve essere una lettera
• anche se non è errore è meglio non usare l'underscore _ come iniziale per evitare conflitti con le librerie che
spesso usano variabili che iniziano con l'underscore
• il C è case sensitive, quindi le maiuscole sono distinte dalle minuscole
• il numero di caratteri significativi è di almeno 31 (63 per l'ultimo standard) caratteri per i nomi interni, e 6 (31 per
l'ultimo standard) caratteri per quelli esterni
• le parole riservate come if, int, ... non possono essere usate per i nomi delle variabili
Il numero di caratteri significativi indica quanti caratteri iniziali, nel nome, possono essere uguali, prima che
intervenga un conflitto di similitudine

int abcdefghijklmnopqrstuvwxyzABCDEaaa;
int abcdefghijklmnopqrstuvwxyzABCDEbbb; /* Possibile conflitto con
la precedente! */

Nel caso sopra vengono definite due variabili, i cui primi 31 caratteri del nome sono uguali: questo può impedire al
compilatore di distinguerle fra loro, generando un errore.
Da notare che se per i nomi interni (usati all'interno di un singolo modulo/file) i caratteri significativi sono ben 31,
per quelli esterni (definiti in un modulo, ma usati da un altro modulo di programma) il limite è di soli 6 caratteri.
C/Variabili, operatori e costanti/Variabili 7

In realtà questo limite così basso è stato spesso esteso dalla quasi totalità dei compilatori. Il motivo più forte per
attenersi a questa regola, così rigida, è quello della compatibilità del proprio codice verso vecchi sistemi o
compilatori.

Assegnazione
Con l'operatore di assegnazione = si assegna ad una variabile un particolare valore:

int a;
a = 1;

la variabile a è dichiarata di tipo intero e successivamente gli viene assegnato il valore 1. Ad una variabile può essere
assegnato il valore di un'altra variabile:

int b,c = 0;
b = c;

entrambe le variabili valgono 0.


L'assegnamento può essere fatto anche per più variabili contemporaneamente:

int x,y;
x = y = 2;

Ad entrambe le variabili viene assegnato il valore 2.

I tipi di variabili
Ad ogni variabile in C deve essere associato un tipo, quindi quando si dichiara una variabile bisogna specificare
sempre di che tipo è. In C le variabili possono essere di 5 tipi base:
• int: sono i numeri interi (16 bit).
• float: sono i numeri a virgola mobile (in inglese "floating-point") a precisione singola (32 bit).
• double: sono i numeri a virgola mobile a precisione doppia (64 bit).
• char: sono le variabili che contengono un carattere (8 bit).
• void: il non-tipo: non si possono creare variabili void, ma questo tipo è usato per dichiarare puntatori generici
(che possono puntare a qualunque tipo di dati) e per definire funzioni che non restituiscono alcun valore.
Le dimensioni reali del tipo int, e che decidono quindi la gamma di numeri rappresentabili, dipendono strettamente
dall'implementazione (compilatore e tipo di processore). Le specifiche di linguaggio suggeriscono che abbia una
dimensione naturale suggerita dall'ambiente di esecuzione; questo si traduce solitamente in almeno 16 bit (gamma
numerica da -32768 a +32767), anche nella maggioranza dei processori/compilatori a 8 bit, ma con processori a 32
bit è piuttosto comune che il compilatore usi int a 32 bit (gamma da -2147483648 a +2147483647).
In casi particolari anche le dimensioni del tipo char sono state aumentate dal minimo degli 8 bit, come per alcuni
DSP. In quel caso poteva essere giocoforza, date alcune ALU a soli 16 o 32 bit, costringere anche il tipo char a 16
o 32 bit.
Per la certezza delle dimensioni rimane in ogni caso fondamentale la documentazione del compilatore, oltre che il
file header limits.h fornito; in questo file sono presenti delle definizioni con #define, come ad esempio
INT_MIN e INT_MAX, ai limiti di gamma numerica rappresentabile. Uno scopo analogo è dato al file header
float.h, per i numeri a rappresentazione in virgola mobile.
C/Variabili, operatori e costanti/Variabili 8

Esempi d'uso
#include <stdio.h>
int main(void)
{
int numeroIntero = 45;
float numeroInVirgolaMobile = 54.78;
double numeroInVirgolaMobileDoppio = 34.567;
char carattere = 'a';

printf("La variabile intera è uguale a %d", numeroIntero);

numeroIntero = 54;
numeroInVirgolaMobile = 78.9;
numeroInVirgolaMobileDoppio = 179.678;
carattere = 'b';
return 0;
}

Opzioni
È possibile utilizzare delle opzioni quando si dichiarano le variabili per ottenere speciali comportamenti.

const
Il comando const dichiara delle costanti, ovvero dei nomi assegnati in modo fisso a dei valori. In pratica sono
come delle macro, solo che non accettano funzioni come valore.
Si può usare anche la direttiva #define per ottenere lo stesso risultato, ma in generale l'uso di const è
preferibile.
Inoltre le variabili const sono anche usate come parametri delle funzioni per assicurarsi che all'interno della
funzione il valore di una variabile non possa subire variazioni.

const int dieci = 10;


printf("Il numero è %d", dieci);

Si possono anche dichiarare puntatori costanti. La cosa interessante è come varia il comportamento del puntatore a
seconda che il modificatore const venga prima o dopo l'*. Nella fattispecie se viene prima, il puntatore non potrà
modificare l'area di memoria ma potrà spostarsi liberamente su di essa, se viene dopo potrà modificare l'area di
memoria ma non potrà spostarsi, comportandosi come un array (dichiarato con le []).

volatile
La parola chiave volatile indica al compilatore che il valore della corrispondente variabile potrebbe essere
modificato da eventi esterni al normale flusso sequenziale di esecuzione delle istruzioni.
In condizioni normali un compilatore può ottimizzare una sequenza consecutiva di accessi in lettura ad una variabile
privi di scritture intermedie producendo un codice eseguibile che ne prelevi il valore dalla memoria un'unica volta,
collocandolo in uno dei registri del processore, e successivamente riutilizzare tale registro nella certezza che questo
continui a contenere il valore corretto.
L'opzione volatile elimina tale certezza, costringendo di fatto il compilatore a produrre codice che rilegge il
valore della variabile dalla memoria per ogni accesso in lettura, non essendovi garanzia che questo sia, nel frattempo,
C/Variabili, operatori e costanti/Variabili 9

rimasto inalterato.

volatile int laMiaVariabile=14;

static
È una variabile che ha permanenza statica in memoria. Se si dichiara una variabile di tipo static all'interno di una
funzione, essa manterrà il suo valore anche quando l'esecuzione della funzione sarà terminata. Se la stessa funzione
fosse invocata un'altra volta, la variabile statica avrà ancora il valore presente alla precedente uscita della funzione.
Lo specificatore static assume un secondo significato se la variabile è definita al livello base del file (fuori da
qualsiasi funzione).
In quel caso, oltre a indicare che la sua esistenza in memoria è valida per tutta l'esecuzione del programma, indica
anche che la sua visibilità è ristretta all'interno del modulo (file). Nessun altro modulo di programma potrà farvi
riferimento con lo specificatore extern.
Questo permette di avere variabili con nomi uguali, specificate in moduli diversi, senza che possano avere conflitti
fra loro: ciascuna sarà visibile solo nel proprio modulo.

auto
La classe di memorizzazione auto indica che una variabile può essere assegnata in memoria in modo automatico
dal compilatore. Questo tipo di memorizzazione ha senso solo all'interno di una funzione.
Il suo uso è decaduto rapidamente nel tempo, in quanto è il tipo di memorizzazione predefinito all'interno delle
funzioni, per cui non necessita di essere specificato.

extern
La classe di memorizzazione extern indica che una variabile è definita esternamente al modulo.
La definizione extern ha lo scopo d'informare il compilatore che ogni riferimento alla variabile dovrà essere
predisposto e infine risolto solo nella fase finale di linking.

extern int n; /* Uso interno al modulo, definizione esterna */

register
Lo specificatore register indica che l'uso di una variabile è critico per il tempo di esecuzione del programma: il
suo accesso dovrà quindi essere più veloce possibile, con la memorizzazione diretta in un registro della CPU o
tramite altri metodi in grado di velocizzarne l'accesso. Questo comando potrebbe essere ignorato dal compilatore se
non vi sono registri liberi nella CPU (nel contesto di esecuzione in cui la variabile è visibile), oppure se i registri
liberi non sono di dimensioni sufficienti a contenere il tipo di dato.

register int i = 1;
for (i=1; i<=10; i++)
printf("Il numero è %d \n", i);

Va osservato che nei compilatori moderni, dove l'uso delle risorse di CPU è generalmente reso al meglio, l'uso di
questo specificatore è spesso ridondante.
C/Variabili, operatori e costanti/Variabili 10

long
Lo specificatore long può essere impiegato sia come modificatore di alcuni tipi base (aumentando lo spazio
assegnato alla variabile), che direttamente come tipo di dati, applicandosi in modo predefinito al tipo int.
Ad esempio, con

long int a;
long b;

le due variabili hanno le stesse dimensioni, come spazio di memorizzazione (fino al doppio delle dimensioni del tipo
int, potendo rappresentare quindi una gamma di valori più ampia).
La relazione fra variabili int e long è in realtà dipendente dall'implementazione del compilatore: su sistemi a
8/16 bit si possono avere casi in cui int ha una rappresentazione a 16 bit e long a 32 bit, mentre su sistemi a 32
bit non è infrequente trovare sia int che long a 32 bit.
Alcune implementazioni permettono la doppia specificazione long, aumentando ancora la dimensione di alcuni tipi
di dato, per esempio:

long long foo; /* Variabile a 64 bit */

short
Lo specificatore short può essere impiegato sia come modificatore di alcuni tipi base (riducendo lo spazio
assegnato alla variabile), che direttamente come tipo di dati, applicandosi in modo predefinito al tipo int.
Ad esempio, nel caso

short int a;
short b;

le due variabili hanno le stesse dimensioni, in modo analogo alle considerazioni per lo specificatore long.

signed
Lo specificatore signed indica che una data variabile va trattata con segno positivo o negativo, nei calcoli
aritmetici. Può essere impiegato sia come modificatore di alcuni tipi base, che direttamente come tipo di dati,
applicandosi in modo predefinito al tipo int.
Ad esempio, nel caso

int a;
signed int b;
signed c;

le tre variabili hanno dimensioni e funzionalità equivalenti (il tipo int viene già trattato con segno aritmetico).
C/Variabili, operatori e costanti/Variabili 11

unsigned
Lo specificatore unsigned indica che una data variabile va trattata sempre con segno positivo, nei calcoli
aritmetici. Può essere impiegato sia come modificatore di alcuni tipi base, che direttamente come tipo di dati,
applicandosi in modo predefinito al tipo int.
Ad esempio, nel caso

unsigned int a;
unsigned b;

le due variabili hanno dimensioni e funzionalità equivalenti.


Uno degli scopi più comuni del trattare variabili senza segno (ovvero sempre come valori positivi o zero) è quello di
ampliare la gamma di valori nel campo positivo, per le quantità numeriche che non necessitano di rappresentazione
negativa. Una variabile int a 32 bit può spaziare numericamente da -2147483648 a +2147483647, mentre se
dichiarata unsigned int può andare da 0 a 4294967295.

C/Variabili, operatori e costanti/Operatori


aritmetici
Gli operatori aritmetici del linguaggio C sono i seguenti:
• + (addizione);
• - (sottrazione);
• * (moltiplicazione);
• / (divisione);
• \ (divisione tra numeri interi);
• % (modulo: restituisce il resto di una divisione intera);
• + (+ unario);
• - (- unario);
• ++ (incrementa la variabile numerica di 1);
• -- (decrementa la variabile numerica di 1);
• = (assegna il valore destro alla variabile sinistra);
Ogni operatore restituisce il risultato dell'operazione. Perciò si potranno eseguire assegnamenti multipli:

int a,b;
a=b=14+34; //a e b sono uguali entrambi a 48

Esempi d'uso
int c,d;
c=d=98;
c++;
d--;
printf("c è uguale a %d; d è uguale a %d.", c, d);

Questo programma restituirà: c è uguale a 99; d è uguale a 97.


C/Variabili, operatori e costanti/Operatori aritmetici 12

Gli operatori di incremento e decremento


L'operatore di incremento ++ e quello di decremento -- hanno un significato diverso se usati a sinistra o a destra
della variabile che si vuole modificare.
Infatti, se posti a sinistra della variabile, questi operatori prima la modificano e poi restituiscono il valore
all'espressione. Invece, se posti a destra, prima restituiscono il valore della variabile all'espressione e poi la
modificano.
Ad esempio:

int a=5, b, c;
b=++a; //b sarà uguale a 6, come a.
c=a++ //c sarà uguale a 6, a sarà invece uguale a 7.

C/Variabili, operatori e costanti/Operatori sui bit


Il C è fornito di molti operatori sui bit. Queste operazioni possono essere eseguite solo sui tipi int e char, incluse
le varianti ottenute con i modificatori long, short, signed e unsigned.
Ecco gli operatori:
• | OR;
• & AND;
• ^ XOR;
• << Scorri a sinistra;
• >> Scorri a destra;
• ~ Complemento a uno.

AND, OR e XOR
Gli operatori OR e AND hanno lo stesso funzionamento degli operatori logici, la differenza è che questi operano sui
bit.
È stato aggiunto l'operatore XOR (OR esclusivo), che restituisce vero se solo 1 operando è vero. Funziona così:

a b c=a^b

0 0 0

0 1 1

1 0 1

1 1 0
C/Variabili, operatori e costanti/Operatori sui bit 13

Esempi

OR
int a=147;
int b=97;
int c=a|b; //OR
printf("c è uguale a %d", c);

Il risultato sarà: c è uguale a 243, perché:

128 64 32 16 8 4 2 1

1 0 0 1 0 0 1 1

0 1 1 0 0 0 0 1

1 1 1 1 0 0 1 1

AND
int a=147;
int b=97;
int c=a&b; //AND
printf("c è uguale a %d", c);

Il risultato sarà: c è uguale a 1, perché:

128 64 32 16 8 4 2 1

1 0 0 1 0 0 1 1

0 1 1 0 0 0 0 1

0 0 0 0 0 0 0 1

XOR
int a=147;
int b=97;
int c=a^b; //XOR
printf("c è uguale a %d", c);

Il risultato sarà: c è uguale a 242, perché:


C/Variabili, operatori e costanti/Operatori sui bit 14

128 64 32 16 8 4 2 1

1 0 0 1 0 0 1 1

0 1 1 0 0 0 0 1

1 1 1 1 0 0 1 0

Scorrimento a destra, a sinistra e complemento a uno


Gli operatori di scorrimento a destra e a sinistra fanno scorrere i bit della variabile specificata come primo operando
verso destra o verso sinistra per il numero di volte specificato dal secondo operando. I bit fuoriusciti non rientreranno
dall'altra estremità, ma andranno persi.
Il complemento a uno, invece, ha un funzionamento molto semplice. Esso inverte i bit, ovvero trasforma gli 1 in 0 e
gli 0 in 1.

Esempi

Scorrimento a sinistra
int a=42;
int c=a<<1; //Scorrimento a sinistra
printf("c è uguale a %d", c);

Il risultato sarà: c è uguale a 84, perché:

128 64 32 16 8 4 2 1

0 0 1 0 1 0 1 0

0 1 0 1 0 1 0 0

Scorrimento a destra
int a=42;
int c=a>>1; //Scorrimento a destra
printf("c è uguale a %d", c);

Il risultato sarà: c è uguale a 21, perché:

128 64 32 16 8 4 2 1

0 0 1 0 1 0 1 0

0 0 0 1 0 1 0 1

Complemento a uno
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
unsigned char i=10;
C/Variabili, operatori e costanti/Operatori sui bit 15

unsigned char j =~i;


printf("%u", j);
}

Il risultato sarà 245, perché:

128 64 32 16 8 4 2 1

0 1 0 1 0 0 0 0

1 0 1 0 1 1 1 1

C/Variabili, operatori e costanti/Costanti


Esistono due tipi di costanti: le costanti manifeste (o letterali) e le costanti simboliche.

Costanti manifeste
Le costanti manifeste si dichiarano nel seguente modo:

#define COSTANTE [sequenza_di_caratteri]

Ciò che si ottiene è la sostituzione nel sorgente del nome indicato con la sequenza di caratteri che lo segue. Si osservi
l'esempio seguente:

#define SALVE Ciao, Come stai?

In questo caso viene dichiarata la costante SALVE in modo tale che tutte le occorrenze di questo nome, successive
alla sua dichiarazione, vengano sostituite con Ciao! Come stai?. È molto importante comprendere questo particolare:
tutto ciò che appare dopo il nome della macro, a parte lo spazio che lo separa, viene utilizzato nella sostituzione.
Ecco un esempio:

#include <stdio.h>
#define SALVE "Ciao! come stai?\n"
int main (void)
{
printf (SALVE);
return 0;
}

In questo caso, SALVE può essere utilizzata in un contesto in cui ci si attende una stringa letterale, perché include
gli apici doppi che sono necessari per questo scopo. Nell'esempio si vede l'uso della macro-variabile come
argomento della funzione printf() e l'effetto del programma è quello di mostrare il messaggio seguente:

Ciao! come stai?

È bene precisare che la sostituzione delle macro-variabili non avviene se i loro nomi appaiono tra apici doppi, ovvero
all'interno di stringhe letterali. Si osservi l'esempio seguente:

#include <stdio.h>
#define SALVE Ciao! come stai?
int main (void)
C/Variabili, operatori e costanti/Costanti 16

{
printf ("SALVE\n");
return 0;
}

In questo caso, la funzione printf() emette effettivamente la parola SALVE e non avviene alcuna espansione di
macro:

SALVE

Una volta compreso il meccanismo basilare della direttiva #define si può osservare che questa può essere utilizzata
in modo più complesso, facendo anche riferimento ad altre macro già definite:
1. define UNO 1
2. define DUE UNO+UNO
3. define TRE DUE+UNO
In presenza di una situazione come questa, utilizzando la macro TRE, si ottiene prima la sostituzione con
DUE+UNO, quindi con UNO+UNO+1, infine con 1+1+1 (dopo, tocca al compilatore).
Tradizionalmente i nomi delle costanti manifeste vengono definiti utilizzando solo lettere maiuscole, in modo da
poterli distinguere facilmente nel sorgente.
Come è possibile vedere meglio in seguito, è sensato anche dichiarare una macro senza alcuna corrispondenza. Nella
definizione di una macro-variabile può apparire l'operatore ##, con lo scopo di attaccare ciò che si trova alle sue
estremità. Si osservi l'esempio seguente:

#include <stdio.h>
#define UNIONE 123 ## 456
int main (void)
{
printf ("%d\n", UNIONE);
return 0;
}

Costanti simboliche
In pratica si tratta di variabili a cui si applica il modificatore const. Ovviamente, è obbligatorio inizializzala
contestualmente alla sua dichiarazione. L'esempio seguente dichiara la costante simbolica e, il numero di nepero:

const float e = 2.71828183;

Le costanti simboliche di questo tipo, sono delle variabili per le quali il compilatore non concede che avvengano
delle modifiche; pertanto, il programma eseguibile che si ottiene potrebbe essere organizzato in modo tale da
caricare questi dati in segmenti di memoria a cui viene lasciato poi il solo permesso di lettura.
C/Blocchi e funzioni 17

C/Blocchi e funzioni
Questa sezione tratta dei blocchi e delle funzioni. Ecco le sotto-sezioni:
• Blocchi if e switch -
• Operatori logici -
• Cicli (while, do-while e for) -
• Funzioni -
• La funzione main -
• Librerie -
• Ricorsività -

C/Blocchi e funzioni/Blocchi if e switch


I blocchi if e switch sono detti di selezione perché "selezionano" le opzioni a seconda di una condizione.

Verità e falsità in C
Prima di poter usare blocchi if e switch è bene sapere che cosa intende il C per vero o falso.
• Un'espressione in C è ritenuta falsa se il suo valore è uguale allo zero binario, vera se il suo valore è diverso dallo
zero binario.
• Gli operatori logici (==, !=, <, <=, >, >=) rendono uno se l'espressione è logicamente vera, e zero se
l'espressione è logicamente falsa.
• Più in generale, qualsiasi istruzione C (assegnazione di variabile/operazione logica/operazione matematica)
restituisce sempre un risultato e può quindi essere valutata in termini di vero/falso. Ad esempio, l'istruzione di
assegnazione a=3; è vera (il valore restituito è 3) mentre b=0; è falsa (il valore restituito è 0).

Blocco if
Il blocco if esegue un'azione se la condizione specificata tra parentesi è vera.

Blocco if semplice
Questa istruzione esegue semplicemente un'istruzione se l'espressione specificata è vera. L'espressione va sempre
racchiusa tra parentesi.

Sintassi
if (espressione)
istruzione;

oppure, se si desiderano eseguire più istruzioni, le si racchiude in un blocco.

if (espressione)
{
istruzione1;
istruzione2;
istruzioneN;
}
C/Blocchi e funzioni/Blocchi if e switch 18

Esempi d'uso
if ((c+b/7)==45)
{
myvar=14;
printf("c+b/7 è uguale a 45");
}

Blocco if-else
Se la condizione è vera, si eseguono le istruzioni specificate dall'if; se è falsa quelle specificate dal blocco else.

Sintassi
if (condizione)
{
/* istruzione da eseguire se la condizione è Vera */
}
else
{
/* istruzione da eseguire se la condizione è Falsa */
}

Esempi d'uso
if ((c+b/7)==45)
{
myvar=14;
printf("c+b/7 è uguale a 45");
}
else
{
myvar=7;
printf("c+b/7 non è uguale a 45");
}

Blocco if-else if-else


Se la condizione specificata nell'if è vera, si eseguono le istruzioni specificate dall'if, altrimenti se è vera una delle
condizioni specificate in un else if si eseguono le istruzioni specificate dall'else if. Se nessuna delle espressioni
è vera, si eseguono le istruzioni specificate dall'else.

Sintassi
if (condizione1)
{
/* istruzione da eseguire se condizione1 è Vera */
}
else if (condizione2)
{
/* istruzione da eseguire se condizione2 è Vera */
}
else if (condizioneN)
C/Blocchi e funzioni/Blocchi if e switch 19

{
/* istruzione da eseguire se condizioneN è Vera */
}
else
{
/* istruzione da eseguire se nessuna condizione è Vera */
}

Esempi d'uso
int myvar;
if ((c+b/7)==45)
{
myvar=14;
printf("c+b/7 è uguale a 45");
}
else if ((c+b/7)==47)
{
myvar=15;
printf("c+b/7 è uguale a 47");
}
else
{
myvar=0;
printf("I numeri inseriti non sono validi.");
}

L'operatore ?
Il blocco if-else può essere sostituito dall'operatore ?. Per esempio:

((c+b/7) == 45) ? myvar=14 : myvar=7;

Questo è equivalente al secondo esempio (eccetto per i printf). Un altro possibile uso è un'assegnazione
condizionale:

i = j<10 ? 5 : 6;

che equivale a:

if (j<10) i = 5;
else i = 6
C/Blocchi e funzioni/Blocchi if e switch 20

Blocco switch-case
Il blocco switch-case permette di selezionare un'istruzione da eseguire in base ad una variabile.

Blocco switch-case semplice


Questo blocco seleziona semplicemente un'istruzione tra le proposte in base ad una variabile. L'istruzione break
interrompe l'elaborazione del blocco switch, in modo da uscire subito dal blocco se una condizione è vera.
L'istruzione break è opzionale, ma se non viene usata, vengono eseguite le istruzioni dei casi seguenti.

Sintassi
switch (variabile)
{
case valore1:
// istruzione1
break;

case valore2:
// istruzione2
break;

case valoreN:
// istruzioneN
break;
}

Esempi d'uso
switch (myVar)
{
case 1:
printf("myVar è uguale a 1");
break;

case 2:
printf("myVar è uguale a 2");
break;
}

switch (myVar)
{
case 1:
case 2:
printf("myVar è uguale a 1 o a 2");
break;
case 3:
printf("myVar è uguale a 3");
// Cade nel caso 4
case 4:
printf("myVar è uguale a 3 o 4");
C/Blocchi e funzioni/Blocchi if e switch 21

break;
}

Dopo l'ultimo caso, l'istruzione "break" è inutile, ma è meglio inserirla ugualmente per evitare errori nel caso si
aggiungessero altri casi.
Quasi sempre, prima di una parola-chiave "case", c'è:
• Un altro "case".
• Un'istruzione "break" o "return" o "continue" o "goto", che fa uscire dal blocco "switch".
Nei casi, come quello dell'esempio precedente, in cui non fosse così, è bene segnalare la situazione con un
commento.
In questo esempio le istruzioni vengono eseguite se myVar è uguale a 1 o a 2.

Istruzione switch-case-default
Come la precedente, solo che se non si avvera nessuna delle condizioni, viene eseguita l'istruzione specificata dal
caso default.

Sintassi
switch (variabile)
{
case valore1:
// istruzione1
break;

case valore2:
// istruzione2
break;

case valoreN:
// istruzioneN;
break;

default:
// istruzione altrimenti
}

Esempi d'uso
switch (myVar)
{
case 1:
printf("myVar è uguale a 1");
break;

case 2:
printf("myVar è uguale a 2");
break;

default:
C/Blocchi e funzioni/Blocchi if e switch 22

printf("Il valore inserito non è valido.");


}

Si potrebbe inserire l'istruzione "break" anche nel caso "default", ma è completamente inutile.

C/Blocchi e funzioni/Operatori logici


Per una definizione di verità e falsità in C, vedere Verità e falsità in C. Gli operatori logici nel linguaggio C sono:

NOT !
Questo operatore inverte il risultato di un'espressione: ovvero se essa restituisce true restituirà false o viceversa.
Negli esempi si ipotizzi incluso il file stdbool.h che definisce le macro true e false.

risultato = !true; // Restituirà false o 0

AND &&
Questo operatore restituisce true se entrambe le espressioni sono vere, false altrimenti.

risultato = true && false; // Restituirà false o 0

OR ||
Questo operatore restituisce true se almeno una delle due espressioni è vera, false altrimenti.

risultato = true || false; // Restituirà true o 1

Il linguaggio C non possiede l'operatore XOR.

==
Questo operatore ritorna vero se le due espressioni sono uguali.

!=
Questo operatore ritorna vero se le due espressioni sono diverse.

Valutazione "short-circuit"
Una caratteristica notevole degli operatori logici && e || è che essi valutano solo gli operandi effettivamente
necessari a determinare il risultato.

risultato = f(a) && g(++b); // se f(a) == false , g(++b) non sarà


valutata

Nell'esaminare questa espressione, verrà prima di tutto valutata f(a). Se l'espressione f(a) risulta in un valore di
falsità, g non sarà neppure richiamata (né sarà incrementata b).
C/Blocchi e funzioni/Cicli 23

C/Blocchi e funzioni/Cicli
Il C consente l'uso di cicli iterativi e condizionali che sono il ciclo while, il do-while e il for.

Ciclo while
Il ciclo while ripete delle istruzioni finché l'espressione specificata è vera. Vedi anche verità e falsità in C.
La condizione viene controllata all'inizio del ciclo, e questo vuol dire che se l'espressione è subito falsa le istruzioni
nel ciclo non verranno eseguite nemmeno una volta.

Sintassi
while(espressione)
istruzione;

while(espressione)
{
istruzione1;
istruzione2;
istruzioneN;
}

Esempi d'uso
int i=10;
while(i != 0) /*Finché 'i' è diverso da 0. Si potrebbe scrivere anche
semplicemente while(i) */
{
printf("Il numero è: %d \n", i);
i--;
}

Ciclo do-while
Il ciclo do-while, come il ciclo while, ripete le istruzioni finché la condizione immessa risulta vera.
Le istruzioni nel blocco, però, vengono eseguite almeno una volta perché la condizione viene controllata alla fine.

Sintassi
do
{
istruzione1;
istruzione2;
istruzioneN;
}while(espressione);
C/Blocchi e funzioni/Cicli 24

Esempi d'uso
int i=10;
do
{
printf("Il numero è %d \n", i);
i--;
}while(i>0);

Ciclo for
Il ciclo for ripete un blocco di istruzioni finché la condizione specificata è vera.
La condizione viene verificata all'inizio.
È possibile omettere qualsiasi istruzione del ciclo for.

Sintassi
for(istruzione_di_inizializzazione_delle_variabili; espressione;
istruzione_di_modifica_delle_variabili)
istruzione;

for(istruzione_di_inizializzazione_delle_variabili; espressione;
istruzione_di_modifica_delle_variabili)
{
istruzione1;
istruzione2;
istruzioneN;
}

L'istruzione di inizializzazione permette di impostare un valore iniziale alle variabili che verranno utilizzate nel
ciclo; l'istruzione di modifica delle variabili permette di incrementare (o decrementare) le variabili utilizzate nel
ciclo.

Esempi d'uso
int i;
for(i=1; i<=10; i++)
{
printf("Il numero è %d \n", i);
}

for( ; ;)
{
printf("Ciclo infinito \n"); /*Non usare nelle applicazioni così
com'è, le farebbe bloccare.*/
}
C/Blocchi e funzioni/Cicli 25

Le istruzioni break e continue


Se si inserisce un'istruzione break all'interno di un ciclo, esso terminerà. L'applicazione riprenderà dalla prima
istruzione fuori dal ciclo.
Esempio (un altro esempio d'uso di break è stato fatto in C/Blocchi e funzioni/Blocchi if e switch#Blocco
switch-case)

while(1){//Ripete all'infinito
if((random()%10) == 4)
break;
}

Questo programma terminerà soltanto quando il generatore di numeri casuali estrarrà 4.


Se si inserisce l'istruzione continue in un ciclo, esso salterà all'iterazione successiva.
Esempio:

int i=0;
for(i=0; i<=10; i++){
if(i==4)//Il 4 va saltato
continue;
printf("Il numero è %d", i);
}

C/Blocchi e funzioni/Funzioni
Il linguaggio C permette di scomporre il codice in sottoparti da poter riutilizzare in seguito. Questa funzionalità è
resa disponibile dalle funzioni.

Prototipi
Per poter fare uso di una funzione è necessario definirne il prototipo, ossia il nome, il tipo di valore restituito e il tipo
degli argomenti. La sintassi di un prototipo è la seguente:

tipo_restituito nome_funzione(elenco_tipi_argomenti);

dove tipo_restituito è uno dei tipi base o di quelli definiti dal programmatore e lo stesso vale per l'elenco dei tipi
degli argomenti. L'elenco dei tipi degli argomenti può essere sostituito dalla parola void per indicare che la funzione
non ha argomenti oppure essere costituita da almeno un tipo e in ultima posizione ... ad indicare che seguono un
numero variabile di argomenti, per chiarire:

tipo_restituito nome_funzione(tipo1, tipo2, ...);

Il prototipo della funzione va messo prima della funzione main, successivamente la funzione può essere chiamata in
qualsiasi punto del file sorgente; la definizione vera e propria della funzione può essere posta ovunque nel sorgente
al di fuori di altre funzioni e comunque dopo i prototipi, ma come pratica diffusa si mettono dopo la funzione main.
C/Blocchi e funzioni/Funzioni 26

Definizione di una funzione


Le funzioni in C si definiscono in questo modo:

tipo funzione(tipo1 par1, ..., tipoN parN)


{
istruzione1;
...
istruzioneN;

return valore_restituito;
}

Se il tipo di valore da restituire è void, indica che non viene restituito nessun valore.
In questo caso l'istruzione return è facoltativa. Può servire per un'uscita prematura, cioè per terminare la funzione
prima di giungere alla fine.
Se la funzione è stata dichiarata per restituire void, essa non restituirà alcun valore: l'istruzione return delle
funzioni che rendono un void non ammette parametri.
Lo standard afferma che una funzione che non accetta argomenti nella dichiarazione tra le parentesi tonde deve avere
la parola void, poi per richiamare tale funzione se ne scrive il nome seguito da una coppia di parentesi tonde aperta e
chiusa.
La definizione può fungere anche da prototipo se posta prima della funzione main e prima del prototipo di una
funzione che ne fa uso. Esempio:

int somma (int a, int b); // prototipo e definizione


{
return a + b;
}
void messaggio (void)
{
printf("Il risultato è:\n");
}
int main (void)
{
int a, b, c;
a = b = 2;
c = somma (a, b);
messaggio();
printf ("%d",c);
}

Il risultato sarà:

Il risultato è
4

main è la funzione principale che viene chiamata all'avvio del programma.


C/Blocchi e funzioni/main 27

C/Blocchi e funzioni/main
La funzione main è la funzione che viene eseguita all'inizio del programma. Essa può avere degli argomenti e deve
restituire un valore.
Nessun'altra funzione nel programma deve avere il nome main.

Restituzione di un valore
Per convenzione si è soliti far ritornare alla funzione main il valore 0 se l'esecuzione si è svolta in modo corretto ed
un valore diverso da zero per rappresentare un codice di errore in caso di esecuzione fallita. Lo standard (C99)
prevede la possibilita di omettere il valore di ritorno in tal caso verrà restitutito 0 in maniera implicita.
Per esempio:

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

int main(void)
{
int a=rand();
if (a>10)
return 1; //Errore!
return 0;
}

Questo semplice programma restituirà 1 se il numero casuale a è maggiore di 10, 0 se è minore o uguale a 10. È
possibile anche usare la funzione exit(int valore) per restituire un valore:

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

int main(void)
{
int a=rand();
if (a>10)
exit(1); //Errore!
exit(0);
}

Passaggio di parametri
Lo standard definisce 2 dichiarazioni per la funzione main:

int main (void)

int main (int argc, char *argv[])

la prima usata quando non interessa la riga di comando, la seconda altrimenti. Per quanto riguarda la seconda
dichiarazione, il primo parametro indica il numero di argomenti passati al programma (incluso il nome del
programma), il secondo contiene tutti gli argomenti sotto forma di stringhe (il nome del programma corrisponde ad
argv[0]).
C/Blocchi e funzioni/main 28

Talvolta viene anche incluso un terzo argomento (non previsto dallo standard) envp con la stessa sintassi di argv,
questo array contiene le variabili di ambiente.
Comunque si possono usare altri nomi per i 2 argomenti di main().
Il passaggio di parametri ad un programma avviene tramite la sua invocazione da linea di comando. Infatti è
sufficiente far seguire al nome del programma i valori da passare, separati da uno spazio. Gli argomenti vengono
trattati come stringhe, in particolare, alla prima posizione del vettore argv sarà presente una stringa che contiene il
nome del programma eseguito. Nel caso in cui sia necessario passare al programma parametri di altri tipi base sarà
necessario effettuare una conversione.

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

int main(int argc, char *argv[])


{
int i;
printf("A questo programma sono stati passati %d argomenti.\n",
argc-1);
for (i=1; i<argc; i++)
printf("Il %d° parametro è %s.\n", i, argv[i]);
system("pause");
return 0;
}

Se questo programma si chiama pippo, il comando pippo buongiorno ciao restituirà:

A questo programma sono stati passati 2 argomenti.


Il 1° parametro è buongiorno.
Il 2° parametro è ciao.
C/Blocchi e funzioni/Librerie 29

C/Blocchi e funzioni/Librerie
Librerie
Nel Linguaggio C, come nella maggior parte dei linguaggi ad alto livello, esistono le librerie, che sono una
collezione di funzioni precompilate che possono essere collegate al programma che si sta creando per sfruttarne le
funzionalità.

Dichiarazione d'uso
L'ambiente di sviluppo viene fornito con una serie di librerie standard, ma ogni utente può crearne una sua propria
collezione che utilizzerà nei suoi programmi. Per collegare una libreria ad un programma bisogna specificare
all'interno del codice il suo uso attraverso la clausola #include <nomelibreria.h>; quindi il file .h (da header,
intestazione), file di testo che contiene le informazioni relative alle funzioni e alle variabili contenute nel codice
oggetto, viene incluso assieme al sorgente nel file che viene fornito al compilatore.

Collegamento di una libreria


Durante la compilazione, il codice oggetto della libreria viene collegato al codice oggetto del programma utente, e le
funzioni di libreria rese così accessibili da questo.
Nel Linguaggio C, il tipo stringa non è definito, ma è stata creata una libreria che lo implementa; questa viene
collegata tramite lo header string.h. Altre librerie sono math.h, stdio.h. Le parentesi angolari servono per far ricercare
la libreria nella directory standard; per cercare in percorsi diversi bisogna fornire il percorso completo anteposto al
nome della libreria, senza parentesi angolari (per esempio "c:\linguaggio_C\mielib\mialibreria.h")

C/Blocchi e funzioni/Ricorsività
Visivamente una funzione è ricorsiva se al suo interno compare un richiamo a se stessa.
È possibile scrivere una procedura ricorsiva quando :
• Si sta risolvendo un problema con un metodo induttivo.
• Si sta calcolando una funzione tramite la sua definizione induttiva
Le funzioni ricorsive vengono divise in due parti :
• Base della ricorsione
• Passo della ricorsione

Base della ricorsione


Soluzione del problema per la dimensione più piccola per cui è definito corrisponde solitamente alla soluzione
immediata.
Esempio :
• Per il fattoriale il termine minimo è "0! = 1" quindi il primo controllo sarà se il numero è uguale a 0 restituisci il
valore 1.
C/Blocchi e funzioni/Ricorsività 30

Passo della ricorsione


Soluzione del problema per una dimensione Nesima espressa in termini di soluzione del problema di dimensione più
piccola.
Esempio :
• N! = N * (N - 1)!

Esempi
Fattoriale di un numero (versione non ricorsiva):

int fatt (int N) {


int F=1;
for (; N>=1; N--)
F *= N;
return F;
}

Fattoriale di un numero (versione ricorsiva):

int fattr (int N) {


if (N==0) return 1; /* BASE DELLA RICORSIONE*/
return N * fattr(N-1); /* PASSO DELLA RICORSIONE*/
}

// lo stesso codice in versione piu' ridotta e criptica


int fattr (int N) {
return N ? N * fattr(N-1) : 1;
}

Algoritmo che genera la successione di Fibonacci:

int fibor (int N) {


if (N == 1 || N == 2) return 1; /* BASE DELLA RICORSIONE*/
return fibor(N-1) + fibor(N-2); /* PASSO DELLA RICORSIONE*/
}

Problemi dovuti a errori


Eventuali problemi dovuti a errori di scrittura di un algoritmo potrebbero causare uno Stack Overflow, cioè un
utilizzo eccessivo della zona di memoria assegnata al programma per essere eseguito. Questo potrebbe provocare una
chiusura prematura dell'applicazione.
C/Vettori e puntatori 31

C/Vettori e puntatori
Questa sezione tratta dei vettori e dei puntatori. Ecco le sotto-sezioni:
• Vettori -
• Puntatori -
• Interscambiabilità tra puntatori e vettori -

C/Vettori e puntatori/Vettori
In C, come in altri linguaggi di programmazione, i vettori, anche chiamati array sono elenchi di variabili ad una o
più dimensioni.

Sintassi
Attenzione: l'indicizzazione dei vettori parte da 0 per andare fino a n-1, dove n è il numero di elementi. Perciò:

int arr[3];
arr[0]=1;
arr[1]=2;
arr[2]=3;
arr[3]=4; //ERRORE! Fuori dai limiti dell'array.

Dichiarazione
Per creare un vettore (monodimensionale):

int arr[3];

Si può anche inizializzare alla dichiarazione:

int arr[3]={1,2,3};

che produrrebbe un vettore come il seguente:

arr[0] 1

arr[1] 2

arr[2] 3

Per creare vettori multidimensionali si scrive questo:

int arr[3][3];

Quest'istruzione produrrebbe un vettore come questo:


C/Vettori e puntatori/Vettori 32

0 1 2

0 arr[0][0] arr[0][1] arr[0][2]

1 arr[1][0] arr[1][1] arr[1][2]

2 arr[2][0] arr[2][1] arr[2][2]

in generale un vettore n-dimensionale si dichiara:

tipo Nome_vettore[i1]...[iN];

dove i1 ... iN sono le dimensioni di ciascuna dimensione, che equivale al vettore monodimensionale:

tipo Nome_vettore[i1×...×iN];

ossia al vettore di dimensioni i1×...×iN.

Inizializzazione vettore multidimensionale


Un vettore multidimensionale viene inizializzato con la seguente sintassi:

tipo V[i1]...[iN] = {
{...
{}n1,...,{}ni(N-1)
}21,...,{}2i1
}1;

dove con {}n si è indicato la coppia di graffe al cui interno ci sono gli iniziaizzatori relativi alla dimenione n. Per
comprendere meglio ecco un esempio:

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

Se gli ultimi inizializzatori sono saltati allora si sottintende 0; può essere usata una sintassi semplificata
considerandolo come un vettore monodimensionale:

tipo v[i1]...[iN] = {elenco di tutti gli i1×i2×...×iN inizializzatori};

nel caso di una matrice di interi:

int M[2][3] = {1,2,3,


4,5,6};
C/Vettori e puntatori/Vettori 33

saltando gli ultimi inizializzatori si sottintende ossia:

int M[3][3] = {0,0,1,


0,1,0,
1};

che definisce la matrice :

0 0 1
0 1 0
1 0 0

Sfruttando questa caratteristica è posiisbile inizializzare a 0 tutti gli elemeni di un vettore n-dimensionale con la
seguente sintassi:

tipo V[i1]...[iN] = {0};

Vettori dinamici
L'ultimo standard (C99) dà la possibilità di dichiarare vettori di lunghezza variabile ossia la dimensione del vettore è
una variabile. Ecco un esempio:

// C99, infatti questo commento è permesso in C99.

#include <stdio.h>

int main(void){
int lun;

scanf ("%d",&lun); // lettura di lun

int Array[lun]; // dichiarazione all'interno del codice. C99.


// return 0 implicito, caratteristica C99.
}

Assegnamento
È possibile assegnare un valore ad un elemento di un vettore. Si specifica l'indice tra parentesi quadre:

/* C89. Compatibile con vecchi compilatori. */


int main (void)
{
int arr[3];

arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
return 0;
}
C/Vettori e puntatori/Vettori 34

Accesso
Si accede ad un elemento di un vettore specificando il suo indice tra parentesi quadre:

/* C89. */
#include <stdio.h>

int main (void){


int i, j, arr[3] = {7,8,9}, M[2][3] = {1,2,3,
4,5,6};

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


printf("arr[%d] = %d ", i, arr[i]);
printf("\nM =\n");
for (i=0; i<2; i++){
for (j=0; j<3; j++)
printf("%d ",M[i][j]);
printf("\n");
}
return 0;
}

Il risultato dell'esecuzione del programma è:

arr[0] = 7 arr[1] = 8 arr[2] = 9


M =
1 2 3
4 5 6
C/Vettori e puntatori/Puntatori 35

C/Vettori e puntatori/Puntatori
I puntatori, in C, sono uno strumento molto potente e utile, ma sono anche frequente causa di errori insidiosi.
Un puntatore è una variabile che contiene l'indirizzo di memoria di un'altra variabile (il puntatore x punta alla
variabile y).

Sintassi

Dichiarazione
Un puntatore si dichiara nel seguente modo:

int *a;

Si noti l'asterisco prima del nome del puntatore.

int *a, b;

Il precedente codice non genera due puntatori, ma uno solo, a. La variabile b è semplicemente un intero, perché
l'operatore * vale soltanto per una variabile. Qualora si desideri dichiarare più variabili di tipo puntatore, l'asterisco
va ripetuto per ciascuna di esse:

int *a, *b; // sia a che b sono puntatori a variabile intera

È possibile creare array di puntatori:

int *a[5];

crea un array di cinque puntatori ad intero. Per maggiori informazioni vedere la sezione sugli array.

Assegnamento
Prima di utilizzare un puntatore, bisogna assegnarlo ad un indirizzo di memoria, ovvero fare in modo che esso punti
ad una variabile.

int main (void)


{
int *a, b;

b = 10;
a = &b;
}

Questo codice fa puntare il puntatore a alla variabile b. È importante notare che a non contiene il valore 10, ma
l'indirizzo di memoria della variabile b, ottenuto tramite l'operatore &.
È anche possibile fare in modo che un puntatore punti alla stessa variabile puntata da un altro puntatore:

int *a, *c, b;

b = 10;
a = &b;
c = a; // c punta a b
C/Vettori e puntatori/Puntatori 36

Accesso
Dopo aver dichiarato e assegnato un
puntatore ad una variabile, è possibile
usarlo.

int main (void)


{
int *a, *b, c = 10;
Il puntatore p punta alla variabile a.

b = a = &c;
}
printf("La variabile c, puntata da a, vale %d, mentre la"
"stessa variabile puntata da b vale %d.", *a, *b);

Questo codice stamperà due volte il numero 10.

Aritmetica dei puntatori


È possibile addizionare o sottrarre ad un puntatore un valore intero. Questo farà in modo che il puntatore punti alla
cella di memoria immediatamente successiva.
Se un intero occupa 2 byte, allora se si somma 1 al puntatore che contiene l'indirizzo 100, questo non conterrà 101,
ma 102. Questo appunto perché si assume che un intero occupi 2 byte.
Al contrario, se si sottrae 1, il puntatore conterrà il valore 98.
Inoltre, è possibile calcolare la differenza tra due puntatori per capire quante celle di memoria ci sono fra i due. È
possibile anche confrontarli per capire se puntano allo stesso indirizzo o meno.

if (p1 == p2) printf("p1 e p2 puntano allo stesso indirizzo di


memoria.");
else printf("p1 e p2 puntano a due differenti indirizzi di memoria.");

L'aritmetica dei puntatori permette anche l'interscambiabilità tra puntatori e vettori.

Funzioni e puntatori
Se ad una funzione viene passato un puntatore, essa sarà in grado di modificare il valore puntato dal puntatore. Per
esempio:

void doubleof(int*); //prototipo


int main (void)
{
int a=2;
doubleof(&a);
printf("%d", a);
return 0;
}

void doubleof(int *x)


{
*x = (* x) * 2;
C/Vettori e puntatori/Puntatori 37

Questa piccola applicazione stamperà 4. Ecco come:


1. int a=2; Questa istruzione dichiara una variabile intera a e la inizializza con il valore 2.
2. doubleof(&a); Chiama la funzione doubleof, passandole l'indirizzo della variabile a.
3. Dentro doubleof viene eseguita l'istruzione * x = (* x) * 2;, che assegna alla variabile puntata da x (a) il valore
della stessa variabile puntata da x moltiplicato per 2.
4. printf("%d", a); Stampa il valore ottenuto.
Se invece il nostro programma fosse stato così, avrebbe restituito 2.

void doubleof (int) //prototipo


int main (void)
{
int a=2;
doubleof(a);
printf("%d", a);
return 0;
}

void doubleof(int x)
{
x = x*2;
}

Passare un array ad una funzione

Primo metodo
Il nome di un array, generalmente, decade in un puntatore al suo primo elemento. Questo avviene sempre, tranne
quando si usa il nome dell'array come argomento di sizeof, quando si usa l'operatore & ("indirizzo di"), o durante
un'inizializzazione con una stringa letterale. Tale puntatore può essere passato ad una funzione. In questo modo si
può passare un intero array alla funzione.
Per esempio:

void doubleof(int *ilmioarray);

int main(void)
{
int i, arr[5]={1,2,3,4,5};

doubleof(arr);
for (i=0; i<5; i++)
printf("arr[%d] = %d.\n", i, arr[i]);
return 0;
}

void doubleof(int *ilmioarray)


{
int j;
C/Vettori e puntatori/Puntatori 38

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


ilmioarray[j] = ilmioarray[j]*2;
}

restituisce:

arr[0] = 2.
arr[1] = 4.
arr[2] = 6.
arr[3] = 8.
arr[4] = 10.

Secondo metodo
In questo metodo cambia solo la sintassi della funzione, ma il meccanismo è identico. Il vantaggio di questo
meccanismo è che rende palese il fatto che ad una funzione verrà passato un array, ma se alla funzione fosse passato
un puntatore non produrrebbe errori in fase di compilazione, ma si potrebbe comportare in modo insolito.

void doubleof(int ilmioarray[]);

int main(void)
{
int i, arr[5]={1,2,3,4,5};

doubleof(arr);
for (i=0; i<5; i++)
printf("arr[%d] = %d.\n", i, arr[i]);
return 0;
}

void doubleof(int ilmioarray[])


{
int j;

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


ilmioarray[j]=ilmioarray[j]*2;
}

Passaggio di matrici a funzione


Ricapitolando una matrice in C altro non è che un vettore di vettore quindi viene naturale la dichiarazione:

tipo Matrice[righe][colonne];

con questa sintassi si dichiara un vettore che contiene un numero pari a colonne di vettori che contengono un numero
pari a righe di elementi di tipo tipo. Colonne e righe possono essere delle costanti intere o delle variabili intere (in tal
caso si hanno array dinamici caratteristica del C99).
Per poter passare una matrice a una funzione è necessario un puntatore alla matrice che definisca il numero di
colonne della matrice quindi il prototipo di una funzione che fa questo è :

tipo_restituito funzione (elenco tipi altri argomenti, tipo [][colonne]);


C/Vettori e puntatori/Puntatori 39

se il numero di colonne è variabile si usa questa sintassi

tipo_restituito funzione (elenco tipi altri argomenti, tipo [][*]);

Una volta definita la funzione per passarle la matrice basta passarle il mome della variabile mastrice che decade in
un puntatore alla matrice stessa.
Stesse considerazioni valgono per vettori n-dimensionali ossia del tipo:

tipo v[i1][i2]...[iN];

dove gli indici da i1 a iN possono essere costanti o variabili (standard C99), per passare a una funzione il vettore è
necessario definire un puntatore a v dove vengano definite le ultime n-1 dimensioni del vettore ossia il prototipo
della funzione è:

tipo_restituito fn (elenco tipi altri argomenti, tipo [][i2]...[iN]);

se le ultime n-1 dimensioni sono variabili:

tipo_restituito fn (elenco tipi altri argomenti, tipo [][*]...[*]);

l'asterisco va solo dove la dimensione è variabile.


Tornando al caso delle matrici segue un esempio commentato sull'uso delle matrici dinamiche e passaggio a
funzione.

Esempio passaggio matrici


/* Il seguente codice per funzionare correttamente necessita di un
compilatore che supporti
lo standard C99. */

#include <stdio.h>
void leggiMtrx(int, int, int[][*]); // il [*] indica che il
numero di colonne è
void stampaMtrx(int, int, int[][*]); // una variabile.

int main(void){
int Ri,Co; // parametri necessari per
definire la matrice

printf("-- acquisizione numero righe e colonne --\n");


scanf ("%d%d", &Ri, &Co);
printf("-- Lettura elementi della matrice --\n");
int M[Ri][Co]; // Matrice dinamica, è
possibile dichiararla
leggiMtrx(Ri, Co, M); // dopo aver letto Ri(ghe)
e Co(lonne).
printf("-- Stampa della matrice --\n");
stampaMtrx(Ri, Co, M);
}

void stampaMtrx(int Ri,int Co, int M[][Co]){ // qui viene specicicato


che M è un puntatore
C/Vettori e puntatori/Puntatori 40

int i,j; // a una matrice di Co


colonne dove Co è una
// variabile.
for (i=0;i<Ri;i++){
for (j=0;j<Co;j++)
printf ("%d ",M[i][j]);
printf("\n"); // ritorno a capo per
distinguere le righe del-
} // la matrice.
}
void leggiMtrx(int Ri, int Co, int M[][Co]){ // lettura degli elementi
della matrice.
int i,j;

for (i=0;i<Ri;i++)
for (j=0;j<Co;j++){
printf ("M[%d][%d] = ",i,j);
scanf("%d",&M[i][j]);
}
}

Puntatori a funzioni
È possibile far puntare un puntatore ad una funzione, in modo che essa possa essere chiamata attraverso il puntatore.
Anche le funzioni, come le variabili, hanno un indirizzo di memoria.
Il seguente esempio definisce, assegna e utilizza un puntatore a funzione:

#include <stdio.h>

int doubleof(int x)
{
return x*2;
}

int main(void)
{
int (*funz)(int);
funz=doubleof;
printf("Il doppio di 2 è %d", funz(2));
return 0;
}

Si noti che il tipo del puntatore "funz" è uguale al tipo dalla funzione "doubleof". Il tipo di una funzione è costituito
dalla sequenza dei tipi dei parametri e dal tipo di ritorno. Quindi per assegnare un indirizzo di una funzione a un
puntatore a funzione è necessario che la funzione e il puntatore abbiano lo stesso numero di parametri, che i
parametri corrispondenti abbiano lo stesso tipo, e il tipo di ritorno sia lo stesso.
Da notare inoltre che in questo caso non è stato usato l'operatore di referenziazione & come negli esempi precedenti,
infatti, i nomi delle funzioni sono già dei puntatori.
C/Vettori e puntatori/Puntatori 41

Puntatori a puntatori
È possibile definire anche puntatori ad altri puntatori che potrebbero a loro volta puntare a puntatori ecc.

#include <stdio.h>
int main (void)
{
int *p, **pp, a = 5;

p = &a; // p punta a
pp = &p; // pp punta a p
printf ("**p = %d", **pp); // stampa il contenuto di a
}

il risultato è:

**p = 5

C/Vettori e puntatori/Interscambiabilità tra


puntatori e vettori
Vettori e puntatori sono strettamente legati fra loro.
Infatti, se si usa il nome di un array senza indice, si ottiene un puntatore al suo primo elemento.
Perciò:

a == &a[0]
*a == a[0]

Esempio:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
printf("%i", prova());
return 0;
}
int prova(void)
{
int a[3];
a[0]=78;
a[1]=93;
return (*a==a[0]);
}

La funzione prova() restituirà 1 (true).


Come scritto nella pagina sui vettori, è possibile passare un array ad una funzione passando il puntatore al suo primo
elemento, per il motivo sopra citato.
C/Vettori e puntatori/Interscambiabilità tra puntatori e vettori 42

Usare l'aritmetica dei puntatori per accedere ad un array


L'aritmetica dei puntatori permette di accedere a qualsiasi elemento di un array addizionando un certo valore al
puntatore al primo elemento dell'array.

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

int main(void)
{
char a[7]={'a', 'b', 'c', 'd', 'e', 'f', 'g'};
printf("%c %c", a[3], *(a+3));
return 0;
}

Questo programma restituirà d d, perché a[3] e *(a+3) sono uguali.

Un esempio più complesso


Si può usare l'aritmetica dei puntatori anche per accedere ad array multidimensionali, perché gli elementi, per il
compilatore, sono ordinati uno dopo l'altro in tutti i tipi di array. In questo esempio si prenderà in considerazione un
array 5×3.

#include <stdio.h>
#include <stdlib.h>
#define LunR 5
#define LunC 3

int main(void)
{
char a[LunC][LunR]={'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o'};
printf("%c %c", a[1][2], *((char *)a+1*LunR+2));
return 0;
}

Questo programma restituirà h h. È stata operata una conversione cast sul puntatore a per assicurarsi che fosse un
puntatore ad un valore char. Si è poi addizionato a questo puntatore una riga (5 elementi) e 2 elementi, per ottenere
il carattere che sta sulla seconda riga alla terza colonna (ricordarsi che il primo elemento di un array è [0][0]).
C/Variabili, operatori e costanti/Stringhe 43

C/Variabili, operatori e costanti/Stringhe


Le stringhe in C sono dei vettori di caratteri e non come tipo unico. Le stringhe C sono sempre concluse dal carattere
nullo.

Dichiarazione di una stringa


È possibile anche assegnare una stringa ad un puntatore ad un char, ma è sconsigliato perché si scriverà in parti di
memoria non allocate e sovrascrivere dati necessari all'esecuzione del programma. Ciò può essere fatto in modo
sicuro con le funzioni di allocazione dinamica della memoria. Il puntatore, in questo caso, punterà al primo carattere
della stringa.
Una stringa non è altro che un array di elementi di tipo char. Questo fa pensare subito a un tipo di dichiarazione
immediato (ma alquanto scomodo):

char my_string[] = { 'H','e','l','l','o','\0' };

Il carattere \0 è necessario per terminare la stringa. La dichiarazione vista sopra non è comodissima, ragion per cui il
C consente di dichiarare le stringhe direttamente così:

char my_string[] = "Hello";

Ovviamente si può dichiarare le stringhe senza inizializzarle. In questo caso le si dichiara specificando il nome e la
dimensione:

//stringa di 20 caratteri di cui 1 per \0


char my_string[20];

Uso delle stringhe

Visualizzazione dei caratteri di una stringa


#include <stdio.h>
char frase[]="sono una stringa";
int main (void)
{
printf("%s", frase);
}

Lo specificatore %s indica una stringa alla funzione printf.

Lettura sicura dei caratteri di una stringa


#include <stdio.h>
int main (void)
{
char frase[10];
fgets(frase,10,stdin);
}

fgets(char *str, int lun, FILE *stream) legge i caratteri da un file che se si pone come stdin è lo schermo della
console e li memorizza come una stringa C finché (num-1) caratteri sono stati letti o un ritorno a capo o si raggiunge
la fine del file (EOF). Un carattere di nuova riga fa interrompere la lettura, ma è considerato un carattere valido e,
C/Variabili, operatori e costanti/Stringhe 44

pertanto, è incluso nella stringa str. Un carattere nullo viene automaticamente aggiunto in str dopo i caratteri letti per
segnalare la fine della stringa C.

Funzioni per le stringhe


string.h è l'header file della libreria standard del C che contiene definizioni di macro, costanti e dichiarazioni di
funzioni e tipi usati non solo nella manipolazione delle stringhe ma anche nella manipolazione della memoria.
Le funzioni dichiarate in string.h sono molto popolari ed essendo parte della libreria standard del C, il loro
funzionamento è garantito su ogni piattaforma che supporta il linguaggio C. Le funzioni in questione lavorano
solamente con caratteri ASCII o con un set di caratteri che lo estende in modo compatibile come l'ISO-8859-1; i set
di caratteri a più byte compatibili con l'ASCII, come l'UTF-8, possono funzionare solo a condizione che la lunghezza
della stringa sia interpretata come il numero di byte nella stessa piuttosto che il numero di caratteri Unicode. La
gestione di stringhe non compatibili con l'ASCII viene generalmente risolto con l'uso della libreria wchar.h.

C/Enumerazioni, strutture e unioni


Questa sezione tratta delle enumerazioni, delle strutture e delle unioni.
Ecco le sotto-sezioni:
• Enumerazioni
• Strutture -
• Unioni

C/Enumerazioni, strutture e
unioni/Enumerazioni
In C, un'enumerazione o variabile enumerativa è un tipo di dato che può assumere solo valori scelti dall'utente.
Questi valori vengono rappresentati con delle costanti, ognuna corrispondente ad un valore intero.

Sintassi
enum nome_del_tipo { lista_dei_valori } variabili;

Esempi d'uso
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])


{

enum umano { a_casa, al_lavoro, in_vacanza } mario_rossi;

char stato[20];

printf("Dov'è Mario Rossi? ");


C/Enumerazioni, strutture e unioni/Enumerazioni 45

scanf("%s", stato);
if (!strcmp(stato, "A_casa"))
{
mario_rossi=a_casa;
}
else if (!strcmp(stato, "Al_lavoro"))
{
mario_rossi=al_lavoro;
}
else if (!strcmp(stato, "In_vacanza"))
{
mario_rossi=in_vacanza;
}

printf("Status numerico di Mario Rossi: %d", mario_rossi);

return 0;
}

Questo programma tramuta lo stato di Mario Rossi immesso dall'utente in un numero: A_casa=0, Al_lavoro=1,
In_vacanza=2.
È anche possibile assegnare un numero a scelta ad ogni numerazione. Se i numeri successivi non sono specificati, il
compilatore procederà in ordine crescente.

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

int main(int argc, char *argv[])


{

enum umano { a_casa=25, al_lavoro=56, in_vacanza=57 } mario_rossi;

char stato[20];

printf("Dov'è Mario Rossi? ");


scanf("%s", stato);
if (!strcmp(stato, "A_casa"))
{
mario_rossi=a_casa;
}
else if (!strcmp(stato, "Al_lavoro"))
{
mario_rossi=al_lavoro;
}
else if (!strcmp(stato, "In_vacanza"))
{
mario_rossi=in_vacanza;
}
C/Enumerazioni, strutture e unioni/Enumerazioni 46

printf("Status numerico di Mario Rossi: %d", mario_rossi);

return 0;
}

In questo caso A_casa=25, Al_lavoro=56, In_Vacanza=57.

C/Enumerazioni, strutture e unioni/Strutture


Una struttura è un insieme di una o più variabili. Le variabili possono essere di qualunque tipo e vengono
raggrupate sotto un unico nome. Questo raggruppamento viene effettuato quando occorrono più variabili che hanno
qualcosa in comune, un esempio pratico può essere quello di uno studente universitario. Lo studente ha tre
caratteristiche principali (membri): Nome, Cognome, Matricola. Sarebbe utile raggrupparle insieme, le strutture
permettono appunto questo raggruppamento.
È possibile considerare la struttura come un vero e proprio tipo di dati creato dall'utente, un po' come le
enumerazioni.

Sintassi
La dichiarazione di una struttura è:

struct Studente {
char nome[25];
char cognome[25];
int matricola;
};

Notate il ; (punto e virgola) finale: è necessario.


Con questo codice, non ho ancora creato alcuna variabile di tipo Studente.

Esempi d'uso
Dopo una dichiarazione del genere, in qualunque momento può essere dichiarata una variabile in questo modo:

struct Studente var1;

Adesso var1 è una struttura con tre membri: nome e cognome, stringhe di 25 caratteri, e matricola, la matricola
dello studente.
Per modificare o leggere i membri si usa l'operatore . (punto) in questo modo:

scanf("%s", var1.nome);
scanf("%s", var1.cognome);
var1.matricola = 12345;
altra_matricola = var1.matricola; /* altra_matricola è uguale a 12345 */
C/Enumerazioni, strutture e unioni/Strutture 47

È possibile dichiarare variabili anche al momento della definizione della struttura:

struct Studente {
char nome[25],
char cognome[25];
int matricola;
} pippo;

In questo caso, ho definito una struttura Studente e ho anche dichiarato una variabile di tale tipo.

Puntatori a strutture
È possibile dichiarare anche puntatori a strutture.

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

int main(void)
{
struct Studente {
char nome[25];
char cognome[25];
int matricola;
} pippo, *punt;
punt = &pippo;
strcpy(punt->nome, "Massimiliano");
printf("Il nome dello studente è: %s", pippo.nome);
return 0;
}

L'output sarà:

Il nome dello studente è: Massimiliano

Il puntatore punt punta alla variabile pippo; per accedere dal puntatore ad un membro della struttura da esso
puntata si usa l'operatore -> (freccia).
Il programma avrebbe funzionato anche così:

strcpy((*punt).nome, "Massimiliano");

Ma l'operatore freccia è più comodo da usare.


La funzione strcpy, appartenente alla libreria C standard, permette di copiare la stringa rappresentata dal secondo
argomento nella stringa rappresentata dal primo argomento.
C/Enumerazioni, strutture e unioni/Strutture 48

Campi bit
In C, con i campi bit, è possibile accedere ai singoli bit dei dati. I campi bit sono utili in molte occasioni, tipo quando
si vogliono rappresentare una serie di variabili booleane.
Sintassi:

struct lamiastruttura
{
tipo membro1 : numerodibit;
tipo membro2 : numerodibit;
...
tipo membroN : numerodibit;
} ilmiocampobit;

Esempio:

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

int main(void)
{
struct animale {
unsigned mammifero : 1;
unsigned oviparo : 1;
} ornitorinco, balena;
ornitorinco.mammifero = 1;
ornitorinco.oviparo = 1;
balena.mammifero = 1;
balena.oviparo = 0;
return 0;
}

In questo caso, si sono usati solo 2 bit per memorizzare i valori mammifero e oviparo. Si nota come i campi bit siano
utili per dichiarare variabili booleane (vero/falso) in una struttura. Si possono usare, in una stessa struttura, membri
normali e campi bit.

Altri utilizzi
Una limitazione delle strutture è la seguente: una struttura non può contenere se stessa. Non è possibile insomma
avere una cosa del genere:

struct Elemento {
int Campo1;
struct Elemento Campo2;
};

L'ostacolo può essere però raggirato includendo nella struttura un puntatore allo stesso tipo struttura ed usando gli
operatori * o -> per accedere ai membri:

struct Elemento {
int Campo1;
struct Elemento *Campo2;
C/Enumerazioni, strutture e unioni/Strutture 49

};

Questa tecnica viene utilizzare per creare diverse strutture dati molto flessibili e utili.

Pagine correlate
• Linked list
• Alberi
• Grafi

C/Enumerazioni, strutture e unioni/Unioni


Un'unione è una struttura i cui membri condividono lo stesso spazio di memoria. I vantaggi delle unioni sono:
1. Si risparmia memoria;
2. Si possono interpretare dati in modi diversi.

Sintassi
union lamiaunione
{
tipo membro1;
tipo membro2;
...
tipo membroN;
} variabili;

L'istruzione union dichiare una struttura lamiaunione con dei membri e definisce delle variabili di tipo
lamiaunione. Le variabili sono opzionali. È importante ricordare che essendo la memoria in comune tra più
variabili, non possono essere presenti due dati contemporaneamente. L'esempio che segue infatti mostra una
semplice modalità per capire il tipo di dato attualmente presente nella union e quindi come interpretarlo.
La sintassi per specificare ulteriori variabili di tipo lamiaunione dopo la dichiarazione dell'unione è questa:

union lamiaunione lamiavariabile;

Esempi
Una unione può essere utilizzata per memorizzare dei dati di tipo differente all'interno della stessa struttura. Con una
struttura definita nel seguente modo potremmo ad esempio memorizzare una serie di risultati, interi o stringa,
ottenuti in un'elaborazione. Un esempio banale potrebbe essere:

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

int main(void)
{
#define N 3
struct miastruttura{
char tipo; // il tipo di valore
union miaunione{
C/Enumerazioni, strutture e unioni/Unioni 50

int intero;
char* stringa;
} valore;
} risultati[N];
int a[3] = {1,2,3};
int b[3] = {56, 98, 33};
char errstr[] = "Errore";
int i;
for (i = 0; i < N; i++)
if (a[i]) {
risultati[i].tipo = 'I';
risultati[i].valore.intero = b[i] / a[i];
} else {
risultati[i].tipo = 'C';
risultati[i].valore.stringa = errstr;
}
return 0;
}

che memorizza il quoziente di una divisione nel caso questa sia possibile, altrimenti memorizza la stringa "Errore".

C/Conversioni di tipo
Il C è un linguaggio debolmente tipizzato, questo significa che una variabile di un tipo può essere vista dal
programma come di altro tipo e convertita in un altro tipo. In C esistono due tipi di conversioni di tipo:
1. Conversioni automatiche o implicite
2. Cast: conversione esplicita

Conversioni implicite
Quando un operatore ha operandi di tipo diverso, questi vengono convertiti in un tipo comune. In generale, le uniche
conversioni automatiche sono quelle che trasformano un operando "più piccolo" in uno "più grande" in modo da non
avere perdite di informazione. Per quanto riguarda i tipi base la conversione automatica segue il seguente schema:
char → short → int → long → float → double
in particolare nelle assegnazioni se il valore a destra dell'operatore di assegnamento è di un tipo di quelli più a
sinistra nello schema e il valore a sinistra è di un tipo di quelli più a destra la conversione è automatica.
Un'altra conversione di tipo automatica è da void *, puntatore a tipo vuoto, verso puntatori a qualsiasi altro tipo e da
puntatori a qualsiasi tipo a void *.
Quindi assegnazioni di tale tipo sono lecite:

int main (void)


{
char c = 'A';
char *cPtr = &c;
// cPtr punta a c
void *vPtr = cPtr;
// vPtr punta a c
C/Conversioni di tipo 51

cPtr = vPtr;
// cPtr punta a c
}

Cast
Si tratta del metodo definito dal linguaggio per forzare una conversione a un tipo non automatica. La sintassi è la
seguente:

(tipo) valore

un esempio ne mostra l'utilità:

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

f = (float)3 / 2;
printf("%f\n", f);
}

se non si fosse fatto il cast il risultato di 3/2 avrebbe dato 1 invece di 1,5 in quanto è vero che la conversione al tipo
reale (float) è automatica, ma quella del risultato della divisione tra interi che dà 1. Basta, però che uno solo dei due
operandi sia un numero reale che l'intera operazione sia convertita a un numero reale.
In generale la conversione da puntatori a un oggetto a un tipo e viceversa devono essere espicite. Lo standard
afferma che non dovrebbe essere permesso fare un cast di un puntatore a funzione verso un oggetto che non sia un
puntatore a funzione e viceversa, e comunque se ciò fosse permesso dal compilatore dipenderebbe
dall'implementazione. Inoltre è permesso fare il cast di un intero a un puntatore e viceversa ma il risultato dipende
dall'implementazione (non è portabile).
L'unico cast garantito è quello della costante 0 a un puntatore che definisce un puntatore nullo ossia:

void *null = (void *) 0;

è lecito e inoltre il codice precedente può essere semplificato in

void *null = 0;

poiché in tal caso è prevista la conversione implicita.

Un esempio di conversione di puntatori a funzione


Il cast di un puntatore a funzione potrebbe sembrare un po' innaturale a un principiante quindi se ne mostra la sintassi
generale:

(tipo_restituito (*)(elenco tipi argomenti)) nome_puntatore;

gli elementi dell'elenco dei tipi degli argomenti devono essere separati dalla virgola e può contenere anche ... che
indica un numero variabil di argomenti. Segue un esempio:

#include <stdio.h>
int fn(char c){ // in tal caso la dichiarazione
return 0; // fa anche da prototipo.
}
C/Conversioni di tipo 52

int main(void){
void (*fnPtr)(int*) = /* un puntatore a una funzione che non
restituisce nulla
e ha per argomento un puntatore a
intero. */
(void (*)(int*))fn; /* viene inizializzato a fn dopo aver
fatto il cast
al tipo di fnPtr. */

if ( fn == (int(*)(char))fnPtr )
printf ("fnPtr = fn\n");
else // di fatto questo ramo non viene
percorso; lo standard
printf ("fnPrt diverso" // prevede che tornando al tipo
originario l'indirizzo
" da fn"); // sia lo stesso.
}

C/Lettura e scrittura su file


Dal punto di vista del programma scritto in linguaggio C, il file viene utilizzato in qualità di flusso logico di dati
(stream), ovvero flusso di file. Per la precisione, un file viene aperto attribuendogli un puntatore che rappresenta il
flusso di file relativo; quando poi il flusso viene chiuso, l'associazione con il file si conclude.
La gestione del flusso di file avviene in modo trasparente, con l'ausilio di funzioni standard e una struttura dati
definita nella libreria stdio si tratta del tipo 'FILE'.
L'apertura di un file, attraverso le funzioni standard, coincide con l'ottenimento di un puntatore al tipo FILE;
pertanto, questo puntatore rappresenta il flusso di file e tutti i riferimenti a tale flusso si fanno con quel puntatore.

Apertura e chiusura di un file


L'apertura dei file viene ottenuta normalmente con la funzione fopen() che restituisce il puntatore al file, oppure il
puntatore nullo, NULL, in caso di fallimento dell'operazione. L'esempio seguente mostra l'apertura del file mio_file
contenuto nella directory corrente, con una modalità di accesso in sola lettura.

#include <stdio.h>
...
int main (void)
{
FILE *fp_mio_file;
...
fp_mio_file = fopen ("mio_file", "r");
...
}
C/Lettura e scrittura su file 53

Come si vede dall'esempio, è normale assegnare il puntatore ottenuto a una variabile adatta, che da quel momento
identifica il file, finché questo resta aperto.
La chiusura del file avviene in modo analogo, attraverso la funzione fclose(), che restituisce zero se l'operazione è
stata conclusa con successo, oppure il valore rappresentato da EOF. L'esempio seguente ne mostra l'utilizzo.

...
fclose (fp_mio_file);
...

La chiusura del file conclude l'attività con questo, dopo avere scritto tutti i dati eventualmente ancora rimasti in
sospeso (se il file è stato aperto in scrittura).
I prototipi delle 2 funzioni:

FILE* fopen (const char *nome, const char *modalità);

int fclose (FILE *flusso_di_file);

Il parametro nome rappresenta il percorso del file da aprire, mentre la modalità è una delle seguenti:

Modalità Significato

r file di testo in sola lettura.

w file di testo in sola scrittura. Se esiste ne è cancellato il contenuto altrimenti viene creato

r+ file di testo in lettura e scrittura.

w+ Se il file non esiste, lo crea e lo apre in lettura e scrittura. Se esiste, lo cancella e lo apre in lettura e scrittura.

a Apre il file in sola scrittura, facendo in modo che si possa solo aggiungervi del contenuto, alla fine. Se il file non esiste, viene creato.

a+ Come a, solo che è abilitata anche la lettura.

b se una b viene aggiunta alle modalità precedenti si indica che il file è binario. Ossia: rb, wb, ab, e r+b, w+a, a+b equivalenti a rb+, wb+,
ab+

Flussi standard
Ci sono tre flussi di file che risultano aperti in modo predefinito, all'avvio del programma:
• standard input, corrispondente normalmente alla tastiera;
• standard output, corrispondente normalmente allo schermo del terminale;
• standard error, anch'esso corrispondente normalmente allo schermo del terminale.
Spesso si utilizzano questi flussi di file attraverso funzioni apposite che vi fanno riferimento in modo implicito, ma si
potrebbe accedere anche attraverso funzioni generalizzate, utilizzando come puntatori i nomi: stdin, stdout e
stderr.
C/Lettura e scrittura su file 54

File di testo
I file di testo possono essere gestiti in modo più semplice attraverso due funzioni: fgets() e fputs(). Queste
permettono rispettivamente di leggere e scrivere un file una riga alla volta, intendendo come riga una porzione di
testo che termina con il codice di interruzione di riga, secondo l'astrazione usata dal linguaggio.
La funzione fgets() permette di leggere una riga di testo di una data dimensione massima. Si osservi l'esempio
seguente:

...
fgets (ca, 100, fp);
...

In questo caso, viene letta una riga di testo di una dimensione massima di 99 caratteri, dal file rappresentato dal
puntatore fp. Questa riga viene posta all'interno dell'array ca, con l'aggiunta di un carattere \0 finale. Questo fatto
spiega il motivo per il quale il secondo parametro corrisponde a 100, mentre la dimensione massima della riga letta è
di 99 caratteri. In pratica, l'array di destinazione è sempre una stringa, terminata correttamente.
Nello stesso modo funziona fputs(), che però richiede solo la stringa e il puntatore del file da scrivere. Dal momento
che una stringa contiene già l'informazione della sua lunghezza perché possiede un carattere di conclusione, non è
prevista l'indicazione della quantità di elementi da scrivere.

...
fputs (ca, fp);
...

Seguono i modelli sintattici delle funzioni fputs() e fgets(), in forma di prototipi di funzione:

char *fgets (char *stringa, int dimensione_max, FILE *stream);

int fputs (const char *stringa, FILE *stream);

Se l'operazione di lettura riesce, fgets() restituisce un puntatore corrispondente alla stessa stringa (cioè l'array di
caratteri di destinazione), altrimenti restituisce il puntatore nullo, NULL, per esempio quando è già stata raggiunta la
fine del file.
La funzione fputs() permette di scrivere una stringa in un file di testo. La stringa viene scritta senza il codice di
terminazione finale, \0, ma anche senza aggiungere il codice di interruzione di riga. Il valore restituito è un valore
positivo in caso si successo, altrimenti EOF.
C/Compilatore e precompilatore 55

C/Compilatore e precompilatore
Questa sezione tratta del compilatore e del precompilatore. Ecco le sotto-sezioni:
• Compilatore -
• Istruzioni al precompilatore
• File di intestazione

C/Compilatore e precompilatore/Compilatore
Un compilatore è un programma che traduce una serie di istruzioni scritte in un determinato linguaggio di
programmazione (codice sorgente) in istruzioni di un altro linguaggio (codice oggetto). Questo processo di
traduzione si chiama compilazione.
Il codice oggetto generato da un programma scritto in C è sempre scritto in linguaggio macchina, ovvero istruzioni
semplici (a basso livello) che vengono eseguite successivamente dalla CPU.
Un compilatore C solitamente ha funzionalita' di compilazione per altri linguaggi macchina diversi da quello della
macchina su cui si sta effettivamente compilando il sorgente.
La compilazione scinde la fase di traduzione delle istruzioni contenute nei file sorgenti da quella di esecuzione. Essa
è relativa soltanto alla fase traduzione di un programma in linguaggio macchina ma non è legata in alcun modo alla
sua esecuzione. In particolare, la compilazione di un programma si compone di due fasi distinte rappresentate nel
seguente schema:

Praticamente dopo la traduzione in codice oggetto avviene il collegamento delle varie parti comprese le librerie per
produrre il file eseguibile. In generale il compilatore prevede delle opzioni per dividere le due operazioni in modo da
poter fare il collegamento in un secondo tempo e/o modificare solo il codice sorgente di alcune parti del programma
e compilare così solo i file oggetto modificati.
C/Compilatore e precompilatore/Direttive 56

C/Compilatore e precompilatore/Direttive
Il precompilatore può eseguire diverse istruzioni.

Le direttive
Le direttive sono delle istruzioni al precompilatore e dipendono dal compilatore stesso, per cui è consigliabile
consultarne la documentazione.
Le direttive non finiscono con il punto e virgola ma con la fin di riga.
Ecco le direttive:

#define
La direttiva #define serve per definire macro. Sintassi:

#define nomemacro testomacro

Il testo può essere una costante o un'espressione, anche parametrizzata:

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

int main(void)
{
#define NUM 10
#define EXPR(a) (a)==6 ? 6 : 0

printf("%d\n", NUM);
printf("%d\n", EXPR(7));
return 0;
}

Abbiamo trattato l'operatore ? qui.

#if, #else, #elif ed #endif


Queste direttive servono per la compilazione condizionale. Il loro uso è del tutto analogo a quello dell'istruzione if,
ma con una sostanziale differenza: mentre in una normale istruzione if la condizione viene valutata a tempo di
esecuzione per decidere quale gruppo di istruzioni eseguire, la direttiva #if seleziona a tempo di compilazione quale
gruppo di istruzioni debba essere compilato, mentre le istruzioni scartate vengono completamente rimosse e, ai fini
del codice eseguibile prodotto, è come non fossero esistite.
Come conseguenza immediata, le espressioni lecite in una direttiva #if sono solo ed esclusivamente quelle valutabili
interamente a tempo di compilazione.
Una forma particolare di direttiva #if è la #ifdef e la sua corrispondente negata #ifndef, che restituiscono un valore di
verità rispettivamente vero e falso se la macro fornita come parametro è stata precedentemente definita.
Esempio:

#include <stdio.h>
#include <stdlib.h>
C/Compilatore e precompilatore/Direttive 57

int main(void)
{
#define NUM 10
#define EXPR(a) (a)==6 ? 6 : 0

#ifdef NUM // vera: NUM è stato precedentemente definito


#if NUM==10 // vera
printf("NUM è uguale a 10.\n");
#if EXPR(6) // vera: per un valore pari a 6 del parametro,
l'espressione restituisce 6
printf("EXPR ha dato un risultato! %d\n", EXPR(6));
#else
printf("EXPR non ha dato alcun risultato!\n");
#endif
#elif NUM<10 // falsa
printf("NUM è minore di 10: %d\n", NUM);
#else
printf("NUM è maggiore di 10: %d\n", NUM);
#endif
#else
printf("NUM non è definito.\n");
#endif
return 0;
}

Tale codice, una volta elaborato dal preprocessore, è equivalente al seguente:

/*... trascrizione completa dei file stdio.h e stdlib.h...*/

int main(void)
{
printf("NUM è uguale a 10.\n");
printf("EXPR ha dato un risultato! %d\n", (6)==6 ? 6 : 0);
return 0;
}

Si noti che il preprocessore sostituisce ogni occorrenza delle macro con il loro testo equivalente.
C/Compilatore e precompilatore/Direttive 58

#include
La direttiva #include è molto importante: permette di includere un file C in un altro. La sua sintassi è la
seguente:

#include <file.h>
#include "file.h"

Qual è la differenza fra parentesi angolari e virgolette? Dipende dal compilatore, ma solitamente con le parentesi
angolari il linker cerca nelle directory della libreria standard, mentre con le virgolette si cerca il file prima all'interno
della directory corrente e poi all'interno delle directory della libreria standard.

#line
La direttiva #line permette di alterare il contenuto delle macro __LINE__ e __FILE__. Essa non salta ad un altro
punto del programma; modifica semplicemente queste macro.
La sintassi è:

#line numerolinea "nomefile"

Esempio:

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

#line 70 "prova.c"

int main(void) //linea 71


{ //Linea 72
printf("Linea: %d; File: %s\n", __LINE__, __FILE__); //Linea 73
return 0;
}

Il file è opzionale.

#pragma
La direttiva #pragma serve per inviare particolari istruzioni al compilatore. Le opzioni disponibili variano da
compilatore a compilatore, per cui è consigliabile consultarne la documentazione.

#undef
La direttiva #undef serve per cancellare delle macro definite in precedenza con #define. Sintassi:

#undef macro

Esempio:

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

int main(void)
{
C/Compilatore e precompilatore/Direttive 59

#define NUM 10
printf("NUM: %d\n", NUM);
#undef NUM
return 0;
}

Operatori
Il preprocessore accetta anche degli speciali operatori:

L'operatore #
Questo operatore serve per trasformare una sequenza di caratteri in stringa all'interno di una macro.
Esempio:

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

int main(void)
{
#define str(s) # s
printf(str(Il C mi piace molto.));
return 0;
}

L'operatore ##
Questo operatore si chiama operatore di concatenamento. Esempio:

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

int main(void)
{
#define cat(x,y) x ## y
int cd=10;
printf("%d\n", cat(c,d));
return 0;
}

Questo programma stamperà il numero 10.

L'operatore defined
Questo operatore restituisce vero se la macro che lo segue è definita.

#ifdef MIA_MACRO
puts("mio messaggio");
#endif
/* Queste tre righe si possono scrivere anche così */
#if defined MIA_MACRO
puts("mio messaggio");
C/Compilatore e precompilatore/Direttive 60

#endif

È utile quando si vogliono usare gli operatori logici o per rimediare alla mancanza di una direttiva #elifdef

#if defined MACRO_A


puts("aaa");
#elif defined MACRO_B
puts("bbb");
#elif defined MACRO_C && defined MACRO_D
puts("ccc");
#else
puts("eee");
#endif

Le macro
Il linguaggio C definisce anche delle macro:

__DATE__
La macro __DATE__ contiene la data della compilazione nel formato mese/giorno/anno.

__FILE__
La macro __FILE__ contiene il nome del file correntemente in compilazione. Può essere modificata con la
direttiva #line.

__LINE__
La macro __LINE__ contiene il numero della linea correntemente in compilazione. Può essere modificata con la
direttiva #line.

__TIME__
La macro __TIME__ contiene l'ora della compilazione nel formato ore:minuti:secondi.

__STDC__
Il contenuto della macro __STDC__ varia da compilatore a compilatore. Solitamente, se essa è definita, il
compilatore accetterà soltanto codice C standard.
C/Compilatore e precompilatore/Header 61

C/Compilatore e precompilatore/Header
Nella tradizione del linguaggio C si fa uso di file di intestazione, ovvero porzioni di codice, in cui, tra le altre cose, si
vanno a mettere i prototipi delle funzioni e le dichiarazioni delle variabili globali, a cui tutto il programma deve poter
accedere.
Per semplificare questo lavoro di fusione, spesso un file incluso ne include automaticamente altri, da cui il proprio
codice può dipendere.
Quando i programmi superano certe dimensioni, può essere conveniente suddividere il sorgente in più files; allo
scopo, bisogna definire le variabili utilizzate dai vari moduli come extern. La soluzione migliore si rivela spesso la
seguente: si scrive un header file contenente tutte le dichiarazioni per le variabili e le variabili condivise dai vari
moduli. Poi, si include l'header in ciascuno dei moduli che ne fanno uso. Segue un esempio (abbastanza banale):

// file mio_header.h
extern int miavariabile;
void mostra(void);

Segue il file contenente l'implementazione della funzione mostra()

#include<stdio.h>
#include "mio_header.h"
void mostra(void)
{
printf("\t %d \n" , miavariabile);
}

In maniera tale da poterle utilizzare nella funzione main():

#include<stdio.h>
#include "mio_header.h"
int miavariabile;

int main(void)
{
printf("\t Inserisci valore per la variabile: \n \t");
scanf("%d", &miavariabile);
printf("\t Hai inserito:");
mostra();

return 0;
}
C/Visibilità 62

C/Visibilità
Il problema del campo di azione di variabili e funzioni va visto assieme a quello della compilazione di un
programma composto da più file sorgenti, attraverso la produzione di file-oggetto distinti.

Dalla parte del linker


Il programma che raccoglie assieme diversi file-oggetto per creare un file eseguibile (ovvero il linker), deve
«collegare» i riferimenti incrociati a simboli di variabili e funzioni. In pratica, se nel file uno.o si fa riferimento alla
funzione f() dichiarata nel file due.o, nel programma risultante tale riferimento deve essere risolto con degli indirizzi
appropriati. Naturalmente, lo stesso vale per le variabili globali, dichiarate da una parte e utilizzate anche dall'altra.
Per realizzare questi riferimenti incrociati, occorre che le variabili e le funzioni utilizzate al di fuori del file-oggetto
in cui sono dichiarate, siano pubblicizzate in modo da consentire il richiamo da altri file-oggetto. Per quanto riguarda
invece le variabili e le funzioni dichiarate e utilizzate esclusivamente nello stesso file-oggetto, non serve questa
forma di pubblicità.
Nei documenti che descrivono il linguaggio C standard si usa una terminologia specifica per distinguere le due
situazioni: quando una variabile o una funzione viene dichiarata e usata solo internamente al file-oggetto rilocabile
che si ottiene, è sufficiente che abbia una «collegabilità interna», ovvero un linkage interno; quando invece la si usa
anche al di fuori del file-oggetto in cui viene dichiarata, richiede una «collegabilità esterna», ovvero un linkage
esterno.
Nel linguaggio C, il fatto che una variabile o una funzione sia accessibile al di fuori del file-oggetto rilocabile che si
ottiene, viene determinato in modo implicito, in base al contesto, nel senso che non esiste una classe di
memorizzazione esplicita per definire questa cosa.

Campo d'azione nel file sorgente


Il file sorgente che si ottiene dopo l'elaborazione da parte del precompilatore, è suddiviso in componenti costituite
essenzialmente dalla dichiarazione di variabili e di funzioni (prototipi inclusi). L'ordine in cui appaiono queste
componenti determina la visibilità reciproca: in linea di massima si può accedere solo a quello che è già stato
dichiarato. Inoltre, in modo predefinito, dopo la trasformazione in file-oggetto, queste componenti sono accessibili
anche da altri file, per i quali, l'ordine di dichiarazione nel file originale non è più importante.
Quando la variabile viene definita in una posizione successiva al suo utilizzo, questa deve essere dichiarata
preventivamente come «esterna», attraverso lo specificatore di classe di memorizzazione extern.
Per isolare le funzioni e la variabile, in modo che non siano disponibili per il collegamento con altri file, si
dichiarano per il solo uso locale attraverso lo specificatore di classe di memorizzazione static.
Per accedere a una funzione o a una variabile definita in un altro file si deve dichiarare localmente la funzione o la
variabile con lo specificatore di classe di memorizzazione extern.

Visibilità all'interno delle funzioni


All'interno delle funzioni sono accessibili le variabili globali dichiarate esternamente a loro, inoltre sono dichiarate
implicitamente le variabili che costituiscono i parametri, dai quali si ricevono gli argomenti della chiamata, e si
possono aggiungere altre variabili «locali». I parametri e le altre variabili che si dichiarano nella funzione sono
visibili solo nell'ambito della funzione stessa; inoltre, se i nomi delle variabili e dei parametri sono gli stessi di
variabili dichiarate esternamente, ciò rende temporaneamente inaccessibili quelle variabili esterne.
In condizioni normali, sia le variabili che costituiscono i parametri, sia le altre variabili dichiarate localmente
all'interno di una funzione, vengono eliminate all'uscita dalla funzione stessa.
C/Visibilità 63

All'interno di una funzione è possibile utilizzare variabili che facciano riferimento a porzioni di memoria che non
vengono rilasciate all'uscita della funzione stessa, pur isolandole rispetto alle variabili dichiarate esternamente. Si
ottiene questo con lo specificatore di classe di memorizzazione static che non va confuso con lo stesso specificatore
usato per le variabili dichiarate esternamente alle funzioni. In altre parole, quando in una funzione si dichiara una
variabile con lo specificatore di classe di memorizzazione static, si ottiene di conservare il contenuto di quella
variabile che torna a essere accessibile nelle chiamate successive della funzione.
Di norma, la dichiarazione di una variabile di questo tipo coincide con la sua inizializzazione; in tal caso,
l'inizializzazione avviene solo quando si chiama la funzione la prima volta.

Campo d'azione nei blocchi di istruzioni


Le variabili dichiarate all'interno di raggruppamenti di istruzioni, ovvero all'interno di parentesi graffe, si
comportano esattamente come quelle dichiarate all'interno delle funzioni: il loro campo di azione termina all'uscita
dal blocco.

Visibilità, accessibilità, staticità


Va chiarita la distinzione che c'è tra la visibilità di una variabile e l'accessibilità al suo contenuto. Quando una
funzione dichiara delle variabili automatiche o statiche con un certo nome, se questa funzione chiama a sua volta
un'altra funzione che al suo interno fa uso di variabili con lo stesso nome, queste ultime non si riferiscono alla prima
funzione. Si osservi l'esempio:

#include <stdio.h>

int x = 100;

int f (void)
{
return x;
}

int main (void)


{
int x = 7;
printf ("x == %d\n", x);
printf ("f() == %d\n", f());

return 0;
}

Avviando questo programma si ottiene il testo seguente:

x == 7
f() == 100

In pratica, la funzione f() che utilizza la variabile x, si riferisce alla variabile con quel nome, dichiarata esternamente
alle funzioni, che risulta inizializzata con il valore 100, ignorando perfettamente che la funzione main() la sta
chiamando mentre gestisce una propria variabile automatica con lo stesso nome. Pertanto, la variabile automatica x
della funzione main() non è visibile alle funzioni che questa chiama a sua volta.
C/Visibilità 64

D'altra parte, anche se la variabile automatica x non risulta visibile, il suo contenuto può essere accessibile, dal
momento della sua dichiarazione fino alla fine della funzione (ma questo richiede l'uso di puntatori). Alla fine
dell'esecuzione della funzione, tutte le sue variabili automatiche perdono la propria identità, in quanto scaricate dalla
pila dei dati, e il loro spazio di memoria può essere utilizzato per altri dati (per altre variabili automatiche di altre
funzioni).
Si osservi che lo stesso risultato si otterrebbe anche la variabile x della funzione main() fosse dichiarata come statica:

...
int main (int argc, char *argv[])
{
static int x = 7;
printf ("x == %d\n", x);

printf ("f() == %d\n", f());


return 0;
}

Le variabili statiche, siano esse dichiarate al di fuori o all'interno delle funzioni, hanno in comune il fatto che
utilizzano la memoria dal principio alla fine del funzionamento del programma, anche se dal punto di vista del
programma stesso non sono sempre visibili. Pertanto, il loro spazio di memoria sarebbe sempre accessibile, anche se
sono oscurate temporaneamente o se ci si trova fuori dal loro campo di azione, attraverso l'uso di puntatori.
Naturalmente, il buon senso richiede di mettere la dichiarazione di variabili statiche al di fuori delle funzioni, se
queste devono essere manipolate da più di una di queste.
Le variabili che utilizzano memoria dal principio alla fine dell'esecuzione del programma, ma non sono statiche,
sono quelle variabili dichiarate all'esterno delle funzioni, per le quali il compilatore predispone un simbolo che
consenta la loro identificazione nel file-oggetto. Il fatto di non essere statiche (ovvero il fatto di guadagnare un
simbolo di riconoscimento nel file-oggetto) consente loro di essere condivise tra più file (intesi come unità di
traduzione), ma per il resto valgono sostanzialmente le stesse regole di visibilità. Il buon senso stesso fa capire che
tali variabili possano essere dichiarate solo esternamente alle funzioni, perché dentro le funzioni si usa
prevalentemente la pila dei dati e perché comunque, ciò che è dichiarato dentro la funzione deve avere una visibilità
limitata.
C/Gestione della memoria 65

C/Gestione della memoria


Il linguaggio C, come il lettore ha avuto modo di constatare leggendo il testo, è caratterizzato dal dare al
programmatore il totale controllo sullo sviluppo della sua applicazione, non mascherando niente, anche a costo di
una iniziale difficoltà tutt'altro che scontata.
Pertanto, mentre in linguaggi come Python, Ruby, Java la memoria è gestita dal Garbage collector, nel C è compito
del programmatore provvedere ad allocare la memoria e soprattutto a disallocarla.
Ovviamente, per comprendere i meccanismi di gestione della memoria, è necessario conoscere l'organizzazione della
stessa.

La memoria: lo stack e lo heap


Possiamo immaginare la memoria divisa in due settori: lo stack e lo heap. Lo stack è la memoria "fissa", che non
cambia nel corso dell'esecuzione del programma, al contrario la heap è una memoria "dinamica", le cui dimensione
mutano durante le esecuzione dell'applicativo.
Per meglio comprendere il concetto ricorriamo ad un esempio pratico. Vogliasi creare un software per la somma di
due numeri: si istanziano due variabili con le quali si gestisce il processo logico del suddetto.In questo caso noi
conosciamo il numero di valori con cui dobbiamo operare: due, e pertanto creiamo due variabili. Ma si possono
verificare situazioni nelle quali non si conosce a prescindere il numero dei valori con i quali il programma dovrà
confrontarsi.
Questo è il caso delle liste, un array le cui dimensioni crescono e diminuiscono. Immaginiamo quindi di dover
memorizzare nel nostro programma un numero n di utenti, che non conosciamo a prescindere e che può variare nel
corso dell'esecuzione. Come ovviare a questa situazione?

Funzioni di allocazione dinamica


L'allocazione dinamica della memoria avviene generalmente attraverso la funzione malloc(), oppure calloc(), definite
nella libreria standard stdlib.h. Se queste riescono a eseguire l'operazione, restituiscono il puntatore alla memoria
allocata, altrimenti restituiscono il valore NULL.
void *malloc (size_t dimensione);
void *calloc (size_t quantità, size_t dimensione);
La differenza tra le due funzioni sta nel fatto che la prima, malloc(), viene utilizzata per allocare un'area di una certa
dimensione, espressa generalmente in byte, mentre la seconda, calloc(), permette di indicare una quantità di elementi
e si presta per l'allocazione di array.
Dovendo utilizzare queste funzioni per allocare della memoria, è necessario conoscere la dimensione dei tipi
primitivi di dati, ma per evitare incompatibilità conviene farsi aiutare dall'operatore sizeof.
Il valore restituito da queste funzioni è di tipo void * cioè una specie di puntatore neutro, indipendente dal tipo di
dati da utilizzare. Per questo, in linea di principio, prima di assegnare a un puntatore il risultato dell'esecuzione di
queste funzioni di allocazione, è opportuno eseguire un cast.

int *pi = NULL;


/* ... */
pi = (int *) malloc (sizeof (int));

if (pi != NULL)
{
C/Gestione della memoria 66

// Il puntatore è valido e allora procede.


/* ... */
}
else
{
// La memoria non è stata allocata e si fa qualcosa
// di alternativo.
/* ... */
}

Come si può osservare dall'esempio, il cast viene eseguito con la notazione (int *) che richiede la conversione
esplicita in un puntatore a int. Lo standard C non richiede l'utilizzo di questo cast, quindi l'esempio si può ridurre al
modo seguente:

...
pi = malloc (sizeof (int));
...

La memoria allocata dinamicamente deve essere liberata in modo esplicito quando non serve più. Per questo si
utilizza la funzione free() che richiede semplicemente il puntatore e non restituisce alcunché.

void free (void *puntatore);

È necessario evitare di deallocare più di una volta la stessa area di memoria, perché ciò potrebbe provocare effetti
imprevedibili.

int *pi = NULL;


/* ... */
pi = (int *) malloc (sizeof (int));

if (pi != NULL)
{
// Il puntatore è valido e allora procede.
/* ... */
free (pi); // Libera la memoria
pi = NULL; // e per sicurezza azzera il puntatore.
/* ... */
}
else
{
// La memoria non è stata allocata e si fa qualcosa
// di alternativo.
/* ... */
}
C/Gestione della memoria 67

realloc()
Lo standard prevede una funzione ulteriore, per la riallocazione di memoria: realloc(). Questa funzione si usa per
ridefinire l'area di memoria con una dimensione differente:

void *realloc (void *puntatore, size_t dimensione);

In pratica, la riallocazione deve rendere disponibili gli stessi contenuti già utilizzati, salvo la possibilità che questi
siano stati ridotti nella parte terminale. Se invece la dimensione richiesta nella riallocazione è maggiore di quella
precedente, lo spazio aggiunto può contenere dati casuali. Il funzionamento di realloc() non è garantito, pertanto
occorre verificare nuovamente, dopo il suo utilizzo, che il puntatore ottenuto sia ancora valido.

C/Le applicazioni CGI


Utilizzando il meccanismo dei CGI (Common Gateway Interface) è possibile innescare vere e proprie applicazioni
che hanno la libertà di svolgere qualsiasi funzione eseguibile sul web server da un programma, per poi restituire un
risultato in forma di pagina HTML.

Pagine statiche e pagine dinamiche


Sono dette pagine statiche quella pagine presenti sul web server che non richiedono alcuna elaborazione da parte del
server se non quella di prendere la pagina così com'è e inviarla al browser. Per pagina dinamica si intende una pagina
non presente fisicamente sul disco rigido del Web Server, ma costruita al volo, per mezzo di un'applicazione
(interfaccia CGI) o uno script dedicato (in PHP o ASP). Il meccanismo dei CGI estende e generalizza l'interazione
richiesta/risposta del protocollo HTTP. Ora descriviamo passo passo il meccanismo dei CGI, il processo si può
dividere in 4 fasi:
1. Invio della richiesta - Il browser (client HTTP) effettua una rechiesta a un server HTTP identificato dal seguente
indirizzo o URL: http://www.nomesito.it/cgi-bin/hello.cgi? Possiamo identificare il server
HTTP:http://www.nomesito.it e il riferimento alla procedura CGI: cgi-bin/hello.cgi. La directory /cgi-bin è una
sottodirectory della directory del web server che contiene le applicazioni CGI.
2. Attivazione del CGI - Il server HTTP riceve la URL, la interpreta e lancia il processo (o thread) che esegue il
CGI.
3. Risposta del CGI - Il risultato della computazione deve dar luogo a una pagina HTML di risposta, che il CGI
invia verso il suo Standard Output (per i CGI lo STDOUT viene intercettato dal server HTTP) tenendo conto di
quale deve essere il formato di una risposta HTTP.
4. Risposta del server HTTP - Sarà poi il server HTTP ad inviare la risposta verso il client che aveva effettuato la
richiesta. Passiamo adesso a vedere come si scrive un CGI in C, prendendo come spunto un'applicazione che
stampa all'interno di una pagina web 'hello world':

//Il CGI hello.c


#include <stdio.h>

int main(void) {
/*informazione necessaria per la risposta*/
printf("Content-type: text/html\n\n");

/*Inviamo su STDOUT i tag HTML*/


printf("<html>\n"
"<head>\n"
C/Le applicazioni CGI 68

"<title>Hello World!</title>\n"
"</head>\n"
"<body>\n"
"<h1><p align=\"center\">Hello World</p></h1>\n"
"</body>\n"
"</html>\n");

return 0;
}

Compilando questo programma all'interno della directory /cgi-bin del nostro server web con estensione .cgi (leggere
la documentazione del proprio compilatore per far questo) otteniamo un eseguibile CGI che possiamo richiamare
all'interno del nostro browser nel modo visto sopra http://www.miosito.it/cgi-bin/hello.cgi L'esame del codice non è
nulla di assurdo, tenendo sempre presente che lo STDOUT di una CGI viene rediretto direttamente al client HTTP.
Degna di nota è questa riga:

printf("Content-type: text/html\n\n");

Il suo scopo è quello di specificare al client HTTP il tipo di contenuto che si sta per inviare (in questo caso del testo
HTML). Senza questa specifica il client non sa come comportarsi e stamperà una pagina bianca. Si notino le due
linee vuote; sono assolutamente necessarie, per le convenzioni del protocollo HTTP. Il segnale di STDOUT viene
quindi intercettato dal browser, e viene generata una pagina web con i contenuti specificati. Prendiamo ora in esame
un rudimentale orologio che invia al client HTTP una pagina web contenente l'ora del server sfruttando la libreria
time.h:

/* Ora quasi esatta */


#include <stdio.h>
#include <time.h>

int main(void) {
time_t bintime;
struct tm *curtime;

printf("Content-type: text/html\n\n");

printf ("<html>\n");
printf("<head>\n");
printf("<title>Orologio</title>\n");
printf("</head>\n");
printf("<body>\n");
printf("<h1>\n");
time(&bintime);
curtime = localtime(&bintime);
printf("Data e ora: %s\n", asctime(curtime));
printf("</h1>\n");
printf("</body>\n");
printf ("</html>\n");

return 0;
C/Le applicazioni CGI 69

Richieste GET e POST


L'utilità principale delle CGI, è quella di poter anche interagire con l'utente finale, leggendo dati inseriti da un utente
via form o tramite altre vie. Per far questo il protocollo HTTP prevede due strade: il metodo GET e il metodo POST.

GET
Nel metodo GET i dati inseriti dall'utente o previsti dal programmatore vengono caricati nell'URL, e il loro
contenuto, a livello del sistema server, finisce in una variabile d'ambiente chiamata QUERY_STRING.
Immaginiamo ad esempio di avere il seguente codice HTML (ricordate che la scelta del metodo, GET o POST, va
fatta a livello del codice del form HTML):

<form method=GET action=/cgi-bin/cgi1>


Come ti chiami? <input type="text" name="nome"><br />
<input type="submit" value="Clicca">
</form>

È un semplice form HTML che chiede all'utente come si chiama e invia la stringa inserita dall'utente all'eseguibile
'cgi1' tramite metodo GET (per convenzione gli eseguibili CGI si mettono nella directory /cgi-bin del server). Se
salviamo questa pagina come 'user.html' dopo aver cliccato sul tasto 'Clicca' la richiesta verrà inoltrata tramite
metodo GET a cgi1, che quindi verrà richiamato nel seguente modo:
http://www.miosito.org/cgi-bin/my_cgi?nome=Nome_inserito_dall_utente Nel caso ci fossero stati più campi oltre al
nome (ad esempio un campo 'password') avremmo avuto una cosa del genere:
http://www.miosito.org/cgi-bin/cgi1?nome=Nome_inserito_dall_utente&password=Password_inserita In pratica
quando inviamo una richiesta tramite GET l'eseguibile CGI viene richiamato passando nell'URL una struttura del
genere: http://www.miosito.org/cgi-bin/my_cgi? campo1=val1&campo2=val2&campo3=val3... Ora immaginiamo
che il nostro eseguibile cgi1 debba leggere il nome inserito dall'utente e generare per lui una pagina HTML di
benvenuto (es. 'Benvenuto pippo!'). Ecco un potenziale codice C:

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

/* Funzione che converte eventuali caratteri speciali


all'interno della stringa inserita dall'utente in
caratteri ASCII leggibili
Prende come parametri la stringa sorgente, la stringa
di destinazione e la lunghezza della stringa da 'uncodare' */
void unencode (char *src, char *dest, int len);

/* Funzione per il prelevamento di


un campo da una query
Prende come parametri la query in cui cercare
e il nome del campo da cercare (in questo caso 'nome') */
char* get_field(char *query, char *field);

int main(void) {
char *query,*nome;
int len;
C/Le applicazioni CGI 70

// Genero la pagina HTML


printf ("Content-type: text/html\n\n");
printf ("<html>\n"
"<head>\n"
"<title>Pagina di benvenuto</title>\n"
"</head>\n"
"<body>\n");

/* Se la richiesta GET non contiene niente, la pagina è stata


richiamata
in modo errato, quindi esco */
if ((query=getenv("QUERY_STRING"))==NULL) {
printf ("<h3>Pagina richiamata in modo errato</h3>\n"
"</body></html>\n");
exit(1);
}

/* Controllo la lunghezza della query e


genero una stringa lunga quanto la query
che conterrà il nome inserito dall'utente

Ricordiamo che query ora sarà una stringa


del tipo 'nome=pippo' */
len=strlen(query);
nome = (char*) malloc(len*sizeof(char));

// Ora nome conterrà il campo 'nome' della query


nome=get_field (query,"nome");

printf ("<h3>Benvenuto %s!</h3>\n"


"</body></html>\n",nome);

exit(0);
}

char* get_field(char *query, char *field) {


int i,j,len,pos;
char *tmp,*input;

// len è pari alla lunghezza della querry+1


len = strlen(query)+1;

/* tmp sarà il pattern di ricerca all'interno della query


Nel nostro caso andrà a contenere la stringa 'nome=' */
tmp = (char*) malloc( (strlen(field)+1)*sizeof(char) );
C/Le applicazioni CGI 71

/* input è lunga quanto la query, e andrà a contenere


il campo da noi ricercato */
input = (char*) malloc(len*sizeof(char));

// tmp <- nome=pippo


sprintf (tmp, "%s=", field);

// Se all'interno della query non c'è il campo richiesto, esco


if (strstr(query,tmp)==NULL)
return NULL;

/* Cerco la posizione all'interno della query


in cui è stato trovato il campo nome */
pos = ( (int) strstr(query,tmp) - (int) query) +
(strlen(field)+1);

/* Controllo quanto è lungo il pattern nome=blablabla


Questo ciclo termina quando viene incontrato un
'&' all'interno
della query (ovvero quando comincia un nuovo campo) o quando la
stringa è
terminata
Alla fine i conterrà il numero di caratteri totali nel pattern
di ricerca */
for (i=pos; ; i++) {
if (query[i]=='\0' || query[i]=='&') break;
}

// Salvo il contenuto della query che mi interessa in input


for (j=pos; j<i; j++)
input[j-pos]=query[j];

//'unencodo' input, rendendo eventuali caratteri speciali


umanamente leggibili
unencode(input,input,len);

// Ritorno input
return input;
}

void unencode(char *src, char *dest, int len) {


int i,code;

// Ciclo finché non ho letto tutti i caratteri specificati


for (i=0; i<len; i++, src++, dest++) {
// Se il carattere corrente di src è un '+', lo converto in
uno spazio ' '
C/Le applicazioni CGI 72

if (*src=='+') *dest=' ';

// Se il carattere corrente è un '%'


else if (*src=='%') {
/* Se il carattere successivo non è un carattere valido,
il carattere di destinazione sarà un '?',
altrimenti sarà il carattere ASCII corrispondente */
if (sscanf(src+1, "%2x", &code) != 1) code='?';
*dest = (char) code;

// Leggo il prossimo carattere


src += 2;
}

/* Se è un carattere alfanumerico standard e non un carattere


speciale,
allora il carattere di destinazione è uguale a quello
sorgente */
else *dest=*src;
}

// Termino la stringa di destinazione


dest[len]='\0';
}

La funzione unencode è indispensabile. Infatti, se l'utente dovesse inserire degli spazi o dei caratteri speciali
qualsiasi all'interno del form (ovvero caratteri non alfanumerici) questi all'interno della QUERY_STRING verranno
tradotti con i codici ASCII corrispondenti preceduti da un '%'. Ad esempio, se l'utente dovesse inserire 'pippo pappo',
la query diventerà 'nome=pippo+pappo'. Per convertire il carattere a quello inizialmente inserito dall'utente è quindi
necessario passare per unencode. Per comodità conviene tenersi le funzioni get_field e unencode da qualche parte
pronte per l'uso, vista la loro utilità all'interno delle CGI in C.

POST
La scelta tra metodo GET e metodo POST è legata ad un preciso criterio di programmazione, in genere le richieste
GET vanno usate solo per campi di piccole dimensioni. Non è una buona idea utilizzare richieste GET, ad esempio,
per inviare un messaggio postato da un utente in un forum, in quanto verrà fuori un URL lunghissimo senza senso. È
anche rischioso usare GET per form di login, in quanto i dati di autenticazione passerebbero in chiaro nell'URL. In
tutti questi casi (e altri) è consigliabile l'uso del metodo POST.
Il metodo POST genera una query string che è uguale in tutto e per tutto a quella generata dal metodo GET (nel
nostro caso, sempre nome=nome_inserito). La differenza è che il metodo GET prevede che la query venga inviata al
server tramite la variabile d'ambiente QUERY_STRING, mentre a livello client viene integrata nell'URL stesso. Il
metodo POST invece prevede che la query venga inviata dal client al server all'interno del pacchetto HTTP stesso, e
viene letta dal server come se fosse un normale input (quindi con scanf, gets o fgets). Prima di inviare la query vera e
propria il client invia al server una stringa che identifica la lunghezza della query che sta per essere inviata. Questa
stringa viene salvata dal server nella variabile d'ambiente CONTENT_LENGTH. In questo modo il server riceve la
lunghezza della query che sta per essere inviata, prepara un buffer di dimensioni adatte e quindi legge la query con le
funzioni per la lettura dell'input già viste. Dopo la procedura rimane uguale (ovvero lettura del contenuto di una
C/Le applicazioni CGI 73

variabile con un metodo come get_field e decodifica dei caratteri con un metodo come unencode).
Ecco un esempio di codice HTML che invia i dati di un form tramite metodo POST (esempio tipico, l'invio di un
messaggio in un form che viene poi inviato ad un eseguibile CGI e stampato su schermo):

<form method="POST" action="/cgi-bin/cgi2">


Inserisci qui il tuo messaggio:<br>
<textarea cols=50 rows=4 wrap="physical" name="msg"/><br>
<input type="submit" value="Invia"/>
</form>

Ed ecco una potenziale applicazione CGI per elaborarlo:

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

/* Il contenuto delle funzioni get_field() e unencode()


è lo stesso visto nel codice precedente */
void unencode (char *src, char *dest, int len);
char* get_field(char *query, char *field);

int main(void) {
char *query,*msg;
int len;

// Genero la pagina HTML


printf ("Content-type: text/html\n\n"
"<html>\n"
"<head>\n"
"<title>Messaggio inserito</title>\n"
"</head>\n"
"<body>\n");

/* Se la variabile d'ambiente CONTENT_LENGTH è nulla, oppure


la conversione in intero con sscanf non produce un intero valido,
esco */
if (getenv("CONTENT_LENGTH") == NULL ||
sscanf ((char*) getenv("CONTENT_LENGTH"),
"%d", &len) != 1)
{
printf ("Contenuto non valido\n"
"</body></html>\n");
exit(1);
}

/* Query sarà grande tanto quanto il pacchetto


che sta per inviarmi il client */
query = (char*) malloc (++len*sizeof(char));

/* Leggo la richiesta POST inviatami dal client


C/Le applicazioni CGI 74

come se fosse un normale input con fgets */


fgets (query,len,stdin);

// Leggo il campo
msg = get_field (query,"msg");

printf ("Messaggio inserito:<br>\n%s\n",msg);


exit(0);
}

C/Appendice/Librerie standard
La libreria C ANSI standard consiste in 24 header file C che possono essere inclusi nel progetto di un
programmatore con una singola direttiva.
Di questi, 15 fanno parte della normativa ANSI che venne pubblicata nel 1989 (chiamata C89), 3 vennero aggiunti
con il Normative Amendment 1 (NA1), ratificato nel 1995, e gli ultimi 6 vennero aggiunti nel 1999 con una ulteriore
aggiunta allo standard ANSI (C99).

Libreria degli header file del C ANSI (C89)

Nome Descrizione

<assert.h> Contiene la macro assert, utilizzata per indentificare errori logici ed altri tipi di bug nelle versioni di debug di un programma.

<ctype.h> Questo header file contiene funzioni usate per classificare i caratteri in base ai loro tipi o per convertirli da maiuscoli a minuscoli,
indipendentemente dal set di caratteri utilizzato (tipicamente ASCII, ma esistono anche implementezioni che usano l'EBCDIC).

<errno.h> Per testare i codici di errore restituiti dalle funzioni di libreria.

<float.h> Contiene delle costanti definite che indicano le proprietà specifiche dell'implementazione della libreria in virgola mobile, come ad
esempio la minima differenza tra due numeri in virgola mobile (_EPSILON), il massimo numero di cifre significative (_DIG) e
l'intervallo di numeri che possono essere rappresentati (_MIN, _MAX).

<limits.h> Contiene delle costanti definite che indicano le proprietà specifiche dell'implementazione dei tipi interi, come l'intervallo dei numeri
rappresentabili (_MIN, _MAX).

<locale.h> Per setlocale() e le costanti relative. Utilizzato per scegliere il codice locale adatto.

<math.h> Per le funzioni matematiche comuni.

<setjmp.h> Dichiara setjmp/longjmp, utilizzate per salti non locali.

<signal.h> Per controllare varie condizioni d'eccezione.

<stdarg.h> Utilizzato da funzioni che accettano un numero variabile di parametri.

<stddef.h> Per definire vari tipi e macro utili.

<stdio.h> Fornisce le funzionalità basilari di input/output del C. Questo file include il prototipo della venerabile funzione printf.

<stdlib.h> Per eseguire un gran numero di operazioni, tra cui conversioni, numeri pseudocasuali, allocazione di memoria, controllo del processo,
variabili d'ambiente, segnali, ricerca ed ordinamento.

<string.h> Per manipolare le stringhe.

<time.h> Per convertire tra vari formati di data e ora.


C/Appendice/Librerie standard 75

Aggiunte del NA1

Nome Descrizione

<iso646.h> Per programmare nel set di caratteri ISO 646.

<wchar.h> Per manipolare stream o stringhe contenenti caratteri estesi - fondamentale per supportare una lunga serie di lingue con caratteri non
occidentali.

<wctype.h> Per la classificazione dei caratteri estesi.

Aggiunte del C99

Nome Descrizione

<complex.h> Un gruppo di funzioni usate per manipolare numeri complessi.

<inttypes.h> Per effettuare conversioni precise tra i tipi interi.

<fenv.h> Per controllare l'ambiente in virgola mobile.

<stdbool.h> Per un tipo di dato booleano.

<stdint.h> Per definire vari tipi interi.

<tgmath.h> Funzioni matematiche utilizzabili indipendentemente dal tipo dell'argomento.

C/Appendice/Librerie standard/assert.h
assert.h è l'header file della libreria standard del C che definisce la macro assert(). Questa macro implementa un
sistema di controllo delle asserzioni, che può essere utilizzato per verificare e controllare l'eventuale verificarsi di
casi "impossibili" nel programma.

Nome Descrizione

assert() Quando questa macro viene eseguita, essa computa il risultato dell'espressione fornìtale: se essa risulta essere falsa (in altre parole, se il
valore finale risulta essere 0), assert scrive alcune informazioni di debug su stderr e, successivamente, richiama la funzione abort(). Le
informazioni riportate su stderr includono:
• il testo dell'espressione che ha fornito il risultato 0
• il nome del file sorgente (la macro predefinita __FILE__)
• il numero della linea in cui si è fallita l'asserzione (la macro predefinita __LINE__)

L'utilità della macro assert() risiede nella semplicità con la quale si può verificare un'affermazione che si ritiene, in
un dato contesto, banalmente ovvia: ad esempio, ricontrollare il valore di una variabile sulla quale si sono già
effettuate delle verifiche. Nel codice che segue, la macro assert() viene utilizzata per controllare che il valore della
variabile "scelta" sia effettivamente compreso nel range valido, nonostante l'uscita dal ciclo while implicitamente
affermi la veridicità di questa condizione.

int scelta = 0;

do {

/* Visualizzo un menu con 4 scelte numerate da 1 a 4 */


scanf("%d", &scelta);

} while (scelta<1 || scelta>4);


C/Appendice/Librerie standard/assert.h 76

/* Primo blocco di operazioni eseguite in base al valore scelto */

assert(scelta>=1 && scelta <=4);

/* Secondo blocco di operazioni eseguite in base al valore scelto */

Utilizzando assert(), il programmatore si accerta nuovamente che la condizione che aveva provocato l'uscita dal ciclo
while sia ancora vera: in questo caso, ad esempio, verifica di non aver accidentalmente modificato il valore della
variabile scelta all'interno del primo blocco di operazioni.
L'utilizzo di assert() dovrebbe essere limitato alla sola fase di sviluppo di un programma: per la brutalità del metodo
di chiusura dell'eseguibile e per la scarsezza di informazioni sull'errore fornite a video, risulterebbe abbastanza
frustrante per l'utente finale se utilizzato nella versione definitiva di un applicativo. Buona programmazione
vorrebbe, infatti, che venga spiegato all'utente quale causa abbia scatenato il problema e, nel caso non fosse possibile
proseguirne l'esecuzione, almeno permettere una salvataggio parziale dello stato del programma. Per questo, un
codice simile a:

int *ptr = malloc(sizeof(int) * 10);


assert(ptr != NULL);
/* utilizzo di ptr */

non rappresenta un uso ottimale di assert(), perché, anche se raramente, è possibile che un'allocazione dinamica di
memoria fallisca.
Quando il programmatore non ha più bisogno delle asserzioni, invece di eliminarle tutte manualmente può definire la
costante NDEBUG prima di includere nel file sorgente l'header assert.h: in questo modo, la macro assert() viene
espansa semplicemente in:

#define assert(ignore)((void) 0)

eliminando quindi tutte le possibili interazioni con il programma. Bisogna però far notare che, in questo caso,
l'espressione passata ad assert() non viene valutata: eventuali espressioni con effetti collaterali, come ad esempio

assert(*i++);

potrebbero modificare la logica del programma a seconda della definizione o meno della costante NDEBUG. In
questo esempio, infatti, al termine della verifica dell'asserzione, il valore del puntatore i risulterebbe incrementato di
una unità nel caso NDEBUG non sia definito, mentre resterebbe inalterato nel caso opposto.
C/Appendice/Librerie standard/ctype.h 77

C/Appendice/Librerie standard/ctype.h
ctype.h è l'header file che, all'interno della libreria standard del C, dichiara funzioni utilizzate per la classificazione
dei caratteri.

Funzioni
Il file ctype.h contiene una dozzina di funzioni di classificazione dei caratteri: esse sono tutte dipendenti dalle
impostazioni locali del sistema, tranne isdigit(). Inoltre, le funzioni possono essere divise in due sottogruppi: quelle
utilizzate per la verifica delle proprietà dei caratteri e quelle per la conversione degli stessi.

Nome Descrizione

Funzioni per la verifica delle proprietà dei caratteri


Ritornano zero se falso o un numero diverso da zero se vero.
int isalnum(int Controlla che il carattere passato sia alfanumerico.
ch)

int isalpha(int Controlla che il carattere passato sia alfabetico.


ch)

int isblank(int Controlla che il carattere passato sia bianco, cioè non visibile a schermo (spazio o tabulazione). (introdotto dal C99)
ch)

int iscntrl(int ch) Controlla che il carattere passato sia di controllo.

int isdigit(int ch) Controlla che il carattere passato sia numerico. (non dipendente dalle impostazioni locali)

int isgraph(int Controlla che il carattere passato sia grafico, cioè abbia un glifo ad esso associato. I caratteri di spaziatura, ad esempio, non sono
ch) considerati grafici.

int islower(int Controlla che il carattere passato sia minuscolo.


ch)

int isprint(int ch) Controlla che il carattere passato sia stampabile.

int ispunct(int Controlla che il carattere passato sia di punteggiatura.


ch)

int isspace(int Controlla che il carattere passato sia di spaziatura.


ch)

int isupper(int Controlla che il carattere passato sia maiuscolo.


ch)

int isxdigit(int Controlla che il carattere passato sia esadecimale, cioè sia compreso in 0-9, oppure a-f, oppure A-F.
ch)

Funzioni per la conversione dei caratteri


Ritornano il carattere convertito.
int tolower(int Converte il carattere passato nel suo corrispondente minuscolo, se applicabile.
ch)

int toupper(int Converte il carattere passato nel suo corrispondente maiuscolo, se applicabile.
ch)
C/Appendice/Librerie standard/ctype.h 78

Errori comuni
Lo standard C99 afferma chiaramente (§7.4-1):
In tutti i casi gli argomenti sono degli int, il valore dei quali deve essere rappresentabile come un unsigned
char oppure deve essere equivalente al valore della macro EOF. Se l'argomento assume un qualsiasi altro
valore, il comportamento è indefinito.
Sfortunatamente molti programmatori dimenticano che una variabile di tipo char può essere sia con segno che senza
segno, a seconda dell'implementazione. Se il tipo char è con segno, allora la conversione implicita da char a int
potrebbe generare dei valori negativi, che generano un comportamento indefinito. Generalmente accade che
l'argomento negativo viene usato come indice in una tabella di ricerca, accedendo ad un'area fuori dalla tabella stessa
e potenzialmente fuori dalla memoria allocata dal programma.
Il modo corretto per utilizzare i parametri char è, quindi, quello di eseguire un casting ad unsigned char.

C/Appendice/Librerie standard/errno.h
errno.h è l'header della libreria standard del C che contiene definizioni di macro per la gestione delle situazioni di
errore.

Nome Descrizione

EDOM Costante intera positiva che indica un errore di dominio, come in sqrt(-1).

EILSEQ Costante intera positiva che indica un sequenza illegale di byte.

ERANGE Costante intera positiva che indica un risultato troppo grande e pertanto non rappresentabile.

La libreria funziona in questo modo: ogni volta che una funzione matematica (definite in math.h) incappa in un
errore, restituisce un valore significativo e documentato per segnalare genericamente la situazione.
Contemporaneamente, imposta errno (un lvalue modificabile, cioè, semplificando, una variabile), definito in questa
libreria, al valore che indica lo specifico errore occorso. Il valore di errno è zero all'avvio del programma ed è
garantito che nessuna funzione di libreria lo azzeri: il programmatore, quindi, dovrebbe, nella funzione chiamante
quella matematica che potrebbe generare l'errore, azzerare il valore di errno prima della chiamata e, successivamente
ad essa, verificarne il valore, prima di richiamare altre funzioni che potrebbero modificare errno.
C/Appendice/Librerie standard/float.h 79

C/Appendice/Librerie standard/float.h
float.h è un header file della libreria standard del C che contiene delle macro che vengono espanse ai vari limiti e
parametri dei tipi in virgola mobile (floating-point) standard.
Le macro, così come definite nello standard ISO 9899:1999 sezione 5.2.4.2.2 sono:
• FLT_ROUNDS - specifica il tipo di arrotondamento eseguito nelle addizioni in virgola mobile, con i seguenti
valori:
• -1 indeterminabile;
• 0 troncamento (rounding toward zero);
• 1 arrotondamento al più vicino;
• 2 arrotondamento verso l'infinito positivo;
• 3 arrotondamento verso l'infinito negativo;
• altri valori indicano arrotondamenti definiti dall'implementazione.
• FLT_EVAL_METHOD - determina la modalità di valutazione di espressioni che coinvolgono tutti i tipi in
virgola mobile:
• -1 indeterminabile;
• 0 valuta tutte le operazioni e le costanti esclusivamente alla precisione dei tipi di appartenenza;
• 1 valuta tutte le operazioni e le costanti del tipo float e double nell'intervallo e alla precisione del tipo double;
• 2 valuta tutte le operazioni e le costanti nell'intervallo e alla precisione del tipo long double;
• altri valori indicano comportamenti definiti dall'implementazione.
• FLT_RADIX - base della rappresentazione esponenziale (almeno 2).
• FLT_MANT_DIG, DBL_MANT_DIG, LDBL_MANT_DIG - numero di cifre nella parte mantissa.
• DECIMAL_DIG - (almeno 10)
• FLT_DIG, DBL_DIG, LDBL_DIG - (almeno 6, 10, 10)
• FLT_MIN_EXP, DBL_MIN_EXP, LDBL_MIN_EXP
• FLT_MIN_10_EXP, DBL_MIN_10_EXP, LDBL_MIN_10_EXP, (almeno -37)
• FLT_MAX_EXP, DBL_MAX_EXP, LDBL_MAX_EXP
• FLT_MAX_10_EXP, DBL_MAX_10_EXP, LDBL_MAX_10_EXP (almeno +37)
• FLT_MAX, DBL_MAX, LDBL_MAX - (almeno 1E+37)
• FLT_EPSILON, DBL_EPSILON, LDBL_EPSILON - (almeno 1E-5, 1E-9, 1E-9)
• FLT_MIN, DBL_MIN, LDBL_MIN - (almeno 1E-37)
C/Appendice/Librerie standard/limits.h 80

C/Appendice/Librerie standard/limits.h
Il file di intestazione (header) limits.h definisce le caratteristiche dei tipi di variabili.

Valori definiti
Nome descrizione

CHAR_BIT numero di bit in un byte.

SCHAR_MIN rispettivamente minimo e massimo di un carattere con segno.

SCHAR_MAX

UCHAR_MAX Massimo valore di un carattere con segno.

CHAR_MIN Questi definiscono i valori minimo e massimo per un carattere. Se esso è rappresentato come un intero con segno, allora i loro
valori sono gli stessi di un carattere con segno (SCHAR). Altrimenti CHAR_MIN è 0 e CHAR_MAX è uguale a
CHAR_MAX UCHAR_MAX.

MB_LEN_MAX Massimo numero di byte di un carattere multibyte.

SHRT_MIN Rispettivamente minimo e massimo di un intero corto con segno.

SHRT_MAX

USHRT_MAX Massimo di un intero corto senza segno.

INT_MIN Rispettivamente minimo e massimo di un intero.

INT_MAX

UINT_MAX Massimo di un intero senza segno.

LONG_MIN Rispettivamente minimo e massimo di un intero. lungo.

LONG_MAX

ULONG_MAX Massimo di un intero lungo senza segno.


C/Appendice/Librerie standard/locale.h 81

C/Appendice/Librerie standard/locale.h
L'header locale.h è utile per impostare informazioni specifiche. Sono definite la struttura lconv e le costanti NULL,
LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC,LC_TIME e le funzioni:
localeconv(),setlocale();

Struttura lconv e uso


La struttura lconv è così definita

struct lconv {
char *decimal_point;
char *thousands_sep;
char *grouping;
char *int_curr_symbol;
char *currency_symbol;
char *mon_decimal_point;
char *mon_thousands_sep;
char *mon_grouping;
char *positive_sign;
char *negative_sign;
char int_frac_digits;
char frac_digits;
char p_cs_precedes;
char p_sep_by_space;
char n_cs_precedes;
char n_sep_by_space;
char p_sign_posn;
char n_sign_posn;
}

Tale struttura è usata da localeconv() così definita

struct lconv *localeconv(void);

Essa imposta la struttura lconv alle impostazioni locali correnti. I puntatori a stringa nella struttura possono puntare a
una stringa nulla che indica che il valore non è disponibile. I tipi char sono numeri non negativi. Se il valore è
CHAR_MAX, allora il valore non è disponibile.

Descrizione membri della struttura


C/Appendice/Librerie standard/locale.h 82

decimal_point Carattere punto decimale per valori non monetari.

thousands_sep Separatore migliaia utilizzato per i non-valori monetari.

grouping Una stringa che indica la dimensione di ciascun gruppo di cifre di quantità non monetarie. Ogni carattere rappresenta un
valore intero che indica il numero di cifre nel gruppo corrente. Il valore 0 significa che il valore precedente, deve essere
utilizzato per il resto dei gruppi.

int_curr_symbol la stringa utilizzata per i simboli internazionali di valuta.

currency_symbol Il simbolo locale utilizzato per la valuta.

mon_decimal_point Carattere punto decimale usato per i valori monetari.

mon_thousands_sep Carattere raggruppamento migliaia per i valori monetari.

mon_grouping Una stringa i cui elementi definiscono le dimensioni del gruppo di cifre per i valori monetari. Ogni carattere rappresenta un
valore intero che indica il numero di cifre nel gruppo corrente. Il valore 0 significa che il valore precedente, deve essere
utilizzato per il resto dei gruppi.

positive_sign carattere da usare per il segno positivo nei valori monetari.

negative_sign carattere per il segno negativo nei valori monetari.

int_frac_digits numero di cifre decimali dopo il punto decimale nei valori monetari internazionali.

frac_digits numero di cifre decimali dopo il punto decimale nei valori monetari.

p_cs_precedes Se uguale a 1, allora currency_symbol appare prima di un valore monetario positivo. Se uguale a 0, currency_symbol appare
dopo un valore monetario positivo.

p_sep_by_space Se uguale a 1, allora currency_symbol è separata da uno spazio da un valore monetario positivo. Se uguale a 0, allora non c'è
spazio tra currency_symbol e un valore monetario positivo.

n_cs_precedes Se uguale a 1, il currency_symbol precede un valore monetario negativo. Se uguale a 0, il currency_symbol segue un valore
monetario negativo.

n_sep_by_space Se uguale a 1, allora currency_symbol è separato da uno spazio da un valore negativo monetario. Se uguale a 0, allora non c'è
spazio tra currency_symbol e un valore monetario negativo.

p_sign_posn Rappresenta la posizione di positive_sign in un valore monetario positivo.

n_sign_posn Rappresenta la posizione di negative_sign in un valore monetario negativo.

Esempio
#include<locale.h>
#include<stdio.h>
int main(void)
{
struct lconv *ptrLocale = localeconv();

printf("Punto decimale: %s",


ptrLocale->decimal_point);
printf("Separatore migliaia: %s",
ptrLocale->thousands_sep);
printf("Raggruppamento: %s", ptrLocale->grouping);
printf("Simbolo valuta internazionale: %s",
ptrLocale->int_curr_symbol);
printf("Simbolo valuta: %s",
ptrLocale->currency_symbol);
printf("Punto decimale monetario: %s",
C/Appendice/Librerie standard/locale.h 83

ptrLocale->mon_decimal_point);
printf("Separatore migliaia monetario: %s",
ptrLocale->mon_thousands_sep);
printf("Raggruppamento monetario: %s",
ptrLocale->mon_grouping);
printf("Segno positivo monetario: %s",
ptrLocale->positive_sign);
printf("Segno negativo monetario: %s",
ptrLocale->negative_sign);

return 0;
}

setlocale definizione e uso


La funzione setlocale() definita:

char *setlocale(int category, const char *locale);

imposta o legge le informazioni dipendenti dalla località. category può essere una delle seguenti:

LC_ALL imposta tutto.

LC_COLLATE agisce sulle funzioni strcoll e strxfrm.

LC_CTYPE agisce su tutte le funzioni per i caratteri.

LC_MONETARY agisce sulle informazioni monetarie di fornite da localeconv.

LC_NUMERIC agisce sulla formattazione del punto decimale e sulle informazioni fornite da localeconv.

LC_TIME agisce sulla funzione strftime.

Il valore "C" per locale mette le impostazioni di base del C. Una stringa vuota ("") per locale definisce le
impostazioni native d'ambiente. Un puntatore nullo (NULL) fa restituire a setlocale un puntatore alla stringa
associata alla categoria per le impostazioni correnti (non si verificano dei cambiamenti). Se il cambiamento ha
successo, setlocale restituisce un puntatore a una stringa che rappresenta l'impostazione precedente. In caso di
fallimento restituisce NULL.

Esempio
#include<locale.h>
#include<stdio.h>

int main(void)
{
char *ptrOldLocale;

ptrOldLocale=setlocale(LC_ALL,"C");
printf(Le impostazioni precedenti erano %s.\n",ptrOldLocale);
return 0;
}
C/Appendice/Librerie standard/locale.h 84

Esempio: Scrivere correttamente lettere localizzate


#include <stdio.h>
#include <locale.h>

int main (void)


{
setlocale(LC_CTYPE,"");
printf(Lettere accentate: àèéìòù);
return 0;
}

La costante LC_CTYPE indica che si vuol agire sui caratteri, mentre "" indica alla funzione di impostare la
localizzazione a quella definita per il sistema, di conseguenza per un sistema operativo localizzato in italiano le
lettere si dovrebbero leggere correttamente.

C/Appendice/Librerie standard/math.h
math.h è l'header file della libreria standard del C che contiene definizioni di macro, costanti e dichiarazioni di
funzioni e tipi usati per le operazioni matematiche.

Funzioni membro Pre-C99


Membro Descrizione

acos arcocoseno

asin arcoseno

atan arcotangente

atan2 arcotangente di due parametri

ceil l'intero minore non minore del parametro

cos coseno

cosh coseno iperbolico

exp(double x) funzione esponenziale, calcola ex

fabs valore assoluto

floor l'intero maggiore non maggiore del parametro

fmod resto del numero in virgola mobile

frexp frazione e potenza di due.

ldexp operazione in virgola mobile

log logaritmo naturale

log10 logaritmo in base 10

pow(x,y) eleva un valore dato ad esponente, xy

sin seno

sinh seno iperbolico

sqrt radice quadrata


C/Appendice/Librerie standard/math.h 85

tan tangente

tanh tangente iperbolica

Esempio d'uso
#include <math.h>
#include <stdio.h>

int main()
{
float num,radice,quadrato;
printf ("Inserisci un numero ");
scanf ("%f",& num);
quadrato= pow(num,2);
radice= sqrt(num);
printf ("Il quadrato del numero e' %f\n",quadrato);
printf ("La radice del numero e' %f\n",radice);
}

C/Appendice/Librerie standard/setjmp.h
setjmp.h è un'intestazione definita nella libreria C standard per fornire salti non-locali, o flusso di controllo, al posto
della solita sequenza di chiamata a funzione e ritorno. La coppia di funzioni setjmp e longjmp forniscono questa
funzionalità. Prima setjmp salva l'ambiente della funzione chiamante mettendolo in una struttura dati, e dopo
longjmp può utilizzare questa struttura per "saltare" indietro.
Un uso tipico di setjmp/longjmp è per la gestione delle eccezioni, chiamando longjmp, permettendo al programma di
uscire da chiamate a funzioni pluri-nidificate senza doversi preoccupare si impostare variabili di controllo per ogni
funzione.

Funzioni
Prototipo Descrizione

int setjmp(jmp_buf env) Imposta il buffer locale jmp_buf e lo inizializza per il salto. Questa routine
salva l'ambiente di chiamata del programma nel buffer di ambiente specificato
con l'argomento env per un utilizzo successivo da parte longjmp. Se il valore
di ritorno è di una chiamata diretta, setjmp restituisce 0. Se viene da una
chiamata a longjmp, setjmp restituisce un valore diverso da 0.

void longjmp(jmp_buf env, int value) Ripristina il contesto del buffer ambiente env che è stato salvato dalla
invocazione della funzione setjmp. Se non c'è un'invocazione a setjmp o la
funzione che contiene setjmp ha terminato, il comportamento è indefinito. Se
longjmp è chiamata da un gestore di segnali nidificato il comportamento non è
definito. Il valore specificato da value è passato da longjmp a setjmp. Dopo
che longjmp ha finito, l'esecuzione del programma prosegue come se
l'invocazione corrispondente a setjmp abbia appena ritornato value. Se il
valore passato a longjmp è 0, setjmp si comporterà come se fosse ritornato 1,
in caso contrario, essa si comporta come se fosse ritornato value.
C/Appendice/Librerie standard/setjmp.h 86

Dopo che setjmp ha salvato lo stato corrente in jmp_buf, una chiamata a longjmp può trasferire il controllo al punto
subito dopo la chiamata a setjmp. Il valore di ritorno di setjmp indica se setjmp viene chiamata direttamente o da
longjmp. Un uso tipico è un contollo del tipo:

if (setjmp()) {...}

Esempio
#include <stdio.h>
#include <setjmp.h>

jmp_buf buf;

void fn2(void) {
printf("fn2\n"); // stampa
longjmp(buf,1); // salta indietro dove setjmp è stata
chiamata - ora setjmp ritorna 1
}

void fn1(void) {
fn2();
printf("fn1\n"); // non stampa
}

int main(void) {
if (!setjmp(buf))
fn1(); // una volta eseguito, setjmp ritorna 0
else // quando longjmp salta indietro, setjmp
ritorna 1
printf("main\n"); // stampa
return 0;
}
C/Appendice/Librerie standard/signal.h 87

C/Appendice/Librerie standard/signal.h
Il file signal.h della libreria standard definisce principalmente delle funzioni per la gestione dei segnali che
riguardano il programma. Assieme alle funzioni definisce anche delle macro-variabili per classificare i segnali e per
fare riferimento a delle funzioni predefinite, destinate astrattamente al trattamento dei segnali.

Dichiarazione
Per la gestione dei segnali ci sono due funzioni che vengono dichiarate nel file signal.h: signal() e raise(). La
funzione raise() serve ad azionare un segnale specificato, come dire che serve ad attivare manualmente un allarme
interno al programma, specificato da un numero particolare che ne definisce il tipo. Il programma contiene sempre
una procedura predefinita che stabilisce ciò che deve essere fatto in presenza di un certo allarme, ma il
programmatore può ridefinire la procedura attraverso l'uso della funzione signal(), con la quale si associa l'avvio di
una funzione particolare in presenza di un certo segnale. Il modello sintattico seguente rappresenta, in modo
estremamente semplificato, l'uso della funzione signal():

signal (n_segnale, funzione_da_associare)

Logicamente la funzione che si associa a un certo numero di segnale viene indicata negli argomenti della chiamata
come puntatore a funzione. La funzione che viene passata come argomento è un gestore di segnale e deve avere una
certa forma:

void gestore (n_segnale)

In pratica, quando viene creata l'associazione tra segnale e funzione che deve gestirlo, la funzione in questione deve
avere un parametro tale da poter rappresentare il numero del segnale che la riguarda e non restituisce alcun valore
(pertanto è di tipo void).
Avendo determinato questo, il modello della funzione signal() può essere precisato un po' di più:

signal (n_segnale, void (*gestore)(int))

Ciò significa che il secondo argomento della funzione signal() è un puntatore a una funzione (gestore()) con un
parametro di tipo int, la quale non restituisce alcunché (void).
Ma non è ancora stato specificato cosa deve restituire la funzione signal(): un puntatore a una funzione che ha un
parametro di tipo int e che a sua volta non restituisce alcunché. In pratica, signal() deve restituire il puntatore a una
funzione che ha le stesse caratteristiche di quella del proprio secondo parametro. A questo punto, si arriva al
prototipo completo, ma molto difficile da interpretare a prima vista:

void (*signal(int n_segnale, void gestore (int)))(int)

Per ovviare a questo problema di comprensibilità, anche se lo standard non lo prescrive, di norma, nel file signal.h si
dichiara un tipo speciale, in qualità di puntatore a funzione con le caratteristiche del gestore di segnale:

typedef void (*SIGPTR) (int);

Così facendo, la funzione signal() può essere dichiarata in modo più gradevole:

SIGPTR signal (n_segnale, SIGPTR gestore);


C/Appendice/Librerie standard/signal.h 88

Tipo sig_atomic_t
il file signal.h definisce il tipo sig_atomic_t, il cui uso non viene precisato dai documenti ufficiali. Si chiarisce solo
che deve trattarsi di un valore intero, possibilmente di tipo volatile, a cui si possa accedere attraverso una sola
istruzione elementare del linguaggio macchina (in modo tale che la lettura o la modifica del suo contenuto non possa
essere sospesa a metà da un'interruzione di qualunque genere).

typedef int sig_atomic_t;

Nell'esempio, il tipo sig_atomic_t viene dichiarato come equivalente al tipo int, supponendo che l'accesso alla
memoria per un tipo intero normale corrisponda a un'operazione «atomica» nel linguaggio macchina. A ogni modo, il
tipo a cui corrisponde sig_atomic_t può dipendere da altri fattori, mentre l'unico vincolo nel rango è quello di poter
contenere i valori rappresentati dalle macro-variabili SIG..., che individuano mnemonicamente i segnali.
Il programmatore che deve memorizzare un segnale in una variabile, potrebbe usare per questo il tipo sig_atomic_t.

Denominazione dei segnali


Un gruppo di macro-variabili definisce l'elenco dei segnali gestibili. Lo standard del linguaggio ne prescrive solo una
quantità minima, mentre il sistema operativo può richiederne degli altri. Teoricamente l'associazione del numero al
nome simbolico del segnale è libera, ma in pratica la concordanza con altri standard prescrive il rispetto di un
minimo di uniformità.

Denominazione dei segnali indispensabili al linguaggio

Denominazione Significato Descrizione


mnemonico

SIGABRT abort Deriva da una terminazione anomala che può essere causata espressamente dall'uso della funzione
abort().

SIGFPE floating point Viene provocato da un'operazione aritmetica errata, come la divisione per zero o uno straripamento del
exception risultato.

SIGILL illegal Istruzione «illegale».

SIGINT interrupt Deriva dalla ricezione di una richiesta interattiva di attenzione, quale può essere quella di
un'interruzione.

SIGSEGV segmentation violation Deriva da un accesso alla memoria non valido, per esempio oltre i limiti fissati.

SIGTERM termination Indica la ricezione di una richiesta di terminazione del funzionamento del programma.

Macro-variabili per la gestione predefinita dei segnali.


Denominazione Significato Descrizione
mnemonico

SIG_DFL default Indica simbolicamente che l'azione da compiere alla ricezione del segnale deve essere quella
predefinita.

SIG_IGN ignore Indica simbolicamente che alla ricezione del segnale si procede come se nulla fosse accaduto.

SIG_ERR error Rappresenta un risultato errato nell'uso della funzione signal().


C/Appendice/Librerie standard/signal.h 89

Uso delle funzioni


La funzione signal() viene usata per associare un «gestore di segnale», costituito dal puntatore a una funzione, a un
certo segnale; tutto questo allo scopo di attivare automaticamente quella tale funzione al verificarsi di un certo
evento che si manifesta tramite un certo segnale.
La funzione signal() restituisce un puntatore alla funzione che precedentemente si doveva occupare di quel segnale.
Se invece l'operazione fallisce, signal() esprime questo errore restituendo il valore SIG_ERR, spiegando così il
motivo per cui questo debba avere l'apparenza di un puntatore a funzione.
Per la stessa ragione per cui esiste SIG_ERR, le macro-variabili SIG_DFL e SIG_IGN vanno usate come gestori di
segnali, rispettivamente, per ottenere il comportamento predefinito o per far sì che i segnali siano ignorati
semplicemente.
In linea di principio si può ritenere che nel proprio programma esista una serie iniziale di dichiarazioni implicite per
cui si associano tutti i segnali gestibili a SIG_DFL:

...
signal (segnale, SIG_DFL);
...

L'altra funzione da considerare è raise(), con la quale si attiva volontariamente un segnale, dal quale poi dovrebbero
o potrebbero sortire delle conseguenze, come stabilito in una fase precedente attraverso signal(). La funzione raise()
è molto semplice:

int raise (int sig);

La funzione richiede come argomento il numero del segnale da attivare e restituisce un valore pari a zero in caso di
successo, altrimenti restituisce un valore diverso da zero. Naturalmente, a seconda dell'azione che viene intrapresa
all'interno del programma, a seguito della ricezione del segnale, può darsi che dopo questa funzione non venga
eseguito altro, pertanto non è detto che possa essere letto il valore che la funzione potrebbe restituire.

Esempio
Esempio
Viene proposto un esempio che serve a dimostrare il meccanismo di provocazione e intercettazione dei segnali:

#include <stdio.h>
#include <signal.h>

void sig_generic_handler (int sig)


{
printf ("Ho intercettato il segnale n. %d.\n", sig);
}

void sigfpe_handler (int sig)


{
printf ("Attenzione: ho intercettato il segnale SIGFPE (%d)\n",
sig);
printf (" e devo concludere il funzionamento!\n", sig);
exit (sig);
}
C/Appendice/Librerie standard/signal.h 90

void sigterm_handler (int sig)


{
printf ("Attenzione: ho intercettato il segnale SIGTERM (%d),\n",
sig);
printf (" però non intendo rispettarlo.\n");
}

void sigint_handler (int sig)


{
printf ("Attenzione: ho intercettato il segnale SIGINT (%d),\n",
sig);
printf (" però non intendo rispettarlo.\n");
}

int main (void)


{
signal (SIGFPE, sigfpe_handler);
signal (SIGTERM, sigterm_handler);
signal (SIGINT, sigint_handler);
signal (SIGILL, sig_generic_handler);
signal (SIGSEGV, sig_generic_handler);

int c;
int x;

printf ("[0][Invio] divisione per zero\n");


printf ("[c][Invio] provoca un segnale SIGINT\n");
printf ("[t][Invio] provoca un segnale SIGTERM\n");
printf ("[q][Invio] conclude il funzionamento\n");
while (1)
{
c = getchar();
if (c == '0')
{
printf ("Sto per eseguire una divisione per zero:\n");
x = x / 0;
}
else if (c == 'c')
{
raise (SIGINT);
}
else if (c == 't')
{
raise (SIGTERM);
}
else if (c == 'q')
{
C/Appendice/Librerie standard/signal.h 91

return 0;
}
}
return 0;
}

All'inizio del programma vengono definite delle funzioni per il trattamento delle situazioni che hanno provocato un
certo segnale. Nella funzione main(), prima di ogni altra cosa, si associano tali funzioni ai segnali principali, quindi
si passa a un ciclo senza fine, nel quale possono essere provocati dei segnali premendo un certo tasto, come suggerito
da un breve menù. Per esempio è possibile provocare la condizione che si verifica tentando di dividere un numero
per zero.
La divisione per zero fa scattare il segnale SIGFPE che viene intercettato dalla funzione sigfpe_handler(), la quale
però non può far molto e così conclude anche il funzionamento del programma.

C/Appendice/Librerie standard/stdarg.h
Il file stdarg.h della libreria standard definisce principalmente delle macro-istruzioni per gestire gli argomenti
variabili passati a una funzione, assieme a un tipo di variabile, va_list, specifico per gestire il puntatore a tali
parametri non dichiarati.

Macro-istruzioni standard per la gestione di argomenti variabili.

Macro-istruzione Descrizione

void va_start (va_list ap, Inizializza la variabile ap, di tipo va_list, in modo che punti all'area di memoria immediatamente successiva al
parametro_n); parametro indicato, il quale deve essere l'ultimo.

tipo va_arg (va_list ap, tipo); Restituisce il contenuto dell'area di memoria a cui punta ap, utilizzando il tipo indicato, incrementando
contestualmente il puntatore in modo che, al termine, si trovi nell'area di memoria immediatamente successiva.

void va_copy (va_list dst, Copia il puntatore org nella variabile dst.
va_list org);

void va_end (va_list ap); Conclude l'utilizzo del puntatore ap.


C/Appendice/Librerie standard/stddef.h 92

C/Appendice/Librerie standard/stddef.h
Il file stddef.h della libreria standard definisce alcuni tipi di dati e delle macro fondamentali.

typedef long int ptrdiff_t;


typedef unsigned long int size_t;
typedef unsigned int wchar_t;

#define NULL ((void *)0)


#define offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)

Di tutte le definizioni merita attenzione la macro-istruzione offsetof che serve a misurare lo scostamento di un
membro di una struttura, per la quale è il caso di scomporre i suoi componenti:
• l'espressione ((tipo_struttura *)0) rappresenta un puntatore nullo trasformato, con un cast, in un puntatore nullo al
tipo di struttura alla quale si sta facendo riferimento;
• l'espressione ((tipo_struttura *)0)->nome_membro rappresenta il contenuto del membro indicato, preso a partire
dall'indirizzo zero;
• l'espressione &((tipo_struttura *)0)->nome_membro rappresenta l'indirizzo del membro indicato, preso a partire
dall'indirizzo zero.
Pertanto, l'indirizzo del membro, relativo all'indirizzo zero, corrisponde anche al suo scostamento a partire dall'inizio
della struttura. Così, tale valore viene convertito con un cast nel tipo size_t.

C/Appendice/Librerie standard/stdio.h
stdio.h, che sta per "standard input-output header", è l'header file che contiene definizioni di macro, costanti e
dichiarazioni di funzioni e tipi usati per le varie operazioni di input/output.

Tipi di dati

FILE
Struttura contenente le informazioni su un file (od in generale uno stream), necessarie per eseguire su di esso le
operazioni di input/output, come ad esempio:
• la posizione attuale nello stream
• un indicatore di fine file
• un indicatore d'errore
• un puntatore al buffer dello stream, se applicabile
Un puntatore a questa struttura è richiesto come parametro da tutte le funzioni di gestione dell'input/output da file
(cioè le funzioni della libreria stdio che iniziano con la lettera f)
C/Appendice/Librerie standard/stdio.h 93

fpos_t
Un tipo scalare capace di identificare univocamente la posizione di ogni byte in un file.

size_t
Un tipo intero che è il tipo del valore restituito dall'operatore sizeof.

Costanti

EOF
End Of File. Un numero intero negativo di tipo int usato per indicare la condizione di "raggiunta la fine file".

BUFSIZ
Un intero indicante la dimensione del buffer usato dalla funzione setbuf().

FILENAME_MAX
La dimensione di un array di char grande abbastanza da contenere il nome di un qualsiasi file gestibile su una
particolare architettura.

FOPEN_MAX
La dimensione di un array di char grande abbastanza da contenere il nome di un qualsiasi file gestibile su una
particolare architettura.
Valore: intero >= 8.

_IOFBF
I/O fully buffered, cioè "I/O completamente bufferizzato". Un intero che può essere passato alla funzione setvbuf()
per richiedere che uno stream sia bufferizzato a blocchi.

_IOLBF
I/O line buffered, cioè "I/O bufferizzato per linee". Un intero che può essere passato alla funzione setvbuf() per
richiedere che uno stream sia bufferizzato per linee.

_IONBF
I/O not buffered, cioè "I/O non bufferizzato". Un intero che può essere passato alla funzione setvbuf() per richiedere
che uno stream non sia bufferizzato.

L_tmpnam
La dimensione di un array di char grande abbastanza per conservare il nome di file temporaneo generato dalla
funzione tmpnam().
C/Appendice/Librerie standard/stdio.h 94

NULL
Una macro che espande nella costante puntatore nullo; in altre parole, una costante che rappresenta un valore che è
garantito essere l'indirizzo di una posizione non valida nella memoria.
Valore: 0, 0L oppure (void*)0, a seconda delle architetture e delle implementazioni.

SEEK_CUR
Un intero che può essere passato alla funzione fseek() per richiedere un posizionamento relativo rispetto alla
posizione attuale nel file.

SEEK_END
Un intero che può essere passato alla funzione fseek() per richiedere il posizionamento alla fine del file.

SEEK_SET
Un intero che può essere passato alla funzione fseek() per richiedere il posizionamento all'inizio del file.

TMP_MAX
Il massimo numero di nomi di file unici generabili dalla funzione tmpnam().
Valore: intero >= 25.

Variabili

stdin
Tipo: FILE *
Puntatore a una struttura FILE che si riferisce allo stream di standard input, generalmente la tastiera.

stdout
Tipo: FILE *
Puntatore a una struttura FILE che si riferisce allo stream di standard output, generalmente un terminale.

stderr
Tipo: FILE *
Puntatore a una struttura FILE che si riferisce allo stream di standard error, generalmente un terminale.

Funzioni

clearerr()
Dichiarazione:

void clearerr(FILE* stream);

• Parametri in ingresso:
• FILE* stream: descrittore del file o dello stream che si vuole modificare.
• Valore di ritorno: void.
Cancella l'indicatore di fine file e quello d'errore per un dato stream.
C/Appendice/Librerie standard/stdio.h 95

fclose()
Dichiarazione:

int fclose(FILE* stream);

• Parametri in ingresso:
• FILE* stream: descrittore del file o dello stream da chiudere.
• Valore di ritorno: int. Ritorna 0 in caso di successo, EOF altrimenti.
Chiude il file o lo stream specificato, eventualmente dopo aver svuotato i buffer ad esso associati (vedi fflush()).

feof()
Dichiarazione:

int feof(FILE* stream);

• Parametri in ingresso:
• FILE* stream: descrittore del file o dello stream da controllare.
• Valore di ritorno: int. Ritorna un valore non nullo (<>0) se la fine del file è stata raggiunta, 0 altrimenti.
Controlla l'indicatore di fine file per un dato stream.
Esempio: iterare una procedura di lettura fino alla fine di un file

while(!feof(f))
{
// lettura dati dal file f
}

ferror()
Dichiarazione:

int ferror(FILE* stream);

• Parametri in ingresso:
• FILE* stream: descrittore del file o dello stream da controllare.
• Valore di ritorno: int. Ritorna un valore non nullo (<>0) se l'indicatore di errore è attivo, 0 altrimenti.
Controlla l'indicatore di errore per un dato stream. Se La funzione ritorna un valore diverso da zero significa che è
avvenuto un errore durante una precedente operazione di input/output.

fflush()
Dichiarazione:

int fflush(FILE* stream);

• Parametri in ingresso:
• FILE* stream: descrittore del file o dello stream.
• Valore di ritorno: int. ritorna 0 in caso di successo, EOF altrimenti..
Svuota i buffer per un dato stream trasferendo i dati dalla memoria al supporto fisico.
C/Appendice/Librerie standard/stdio.h 96

fgetpos()
Dichiarazione:

int fgetpos(FILE* stream, fpos_t* pos);

• Parametri in ingresso:
• FILE* stream: descrittore del file o dello stream.
• fpos_t* pos: indirizzo della variabile in cui registrare la posizione corrente.
• Valore di ritorno: int. ritorna 0 in caso di successo, altrimenti un valore non-nullo..
Riporta la posizione corrente di lettura o scrittura nel file identificato dal primo parametro e la salva nella variabile
specificata nel secondo. In caso di errore, la variabile errno viene impostata a un codice identificativo dell'errore.

fopen()
Dichiarazione:

FILE* fopen(const char* path, const char* mode);

• Parametri in ingresso:
• const char* path: indirizzo del file da aprire.
• const char* mode: sequenza di caratteri che identifica la modalità di apertura.
• Valore di ritorno: FILE*. ritorna un puntatore ad una struttura di tipo FILE in caso di successo, altrimenti
NULL.
Apre un file identificato dal parametro path con la modalità descritta da mode e vi associa una struttura descrittiva di
tipo FILE. In caso di errore, la variabile errno viene impostata a un codice identificativo dell'errore.
Il parametro mode può assumere i seguenti valori:
• "r": il file è aperto in sola lettura. La posizione è impostata all'inizio del file. Restituisce un errore nel caso in cui
il file non esista.
• "r+": il file viene aperto in lettura/scrittura. La posizione è impostata all'inizio del file. Restituisce un errore nel
caso in cui il file non esista.
• "w": il file viene aperto in scrittura. La posizione è impostata all'inizio del file. Se il file non esiste, viene creato;
se invece il file esiste, ne viene cancellato il contenuto.
• "w+": il file viene aperto in lettura/scrittura. La posizione è impostata all'inizio del file. Se il file non esiste, viene
creato; se invece il file esiste, ne viene cancellato il contenuto.
• "a": il file viene aperto in scrittura (modalità append, cioè aggiunta). La posizione è impostata alla fine del file.
Se il file non esiste, viene creato; se invece il file esiste, il contenuto viene conservato inalterato ed i nuovi dati
scritti vengono aggiunti dopo quelli preesistenti.
• "a+": il file viene aperto in lettura/scrittura (modalità append, cioè aggiunta). La posizione di lettura è impostata
all'inizio del file, mentre la scrittura è permessa solo alla fine del file. Se il file non esiste, viene creato; se invece
il file esiste, il contenuto viene conservato inalterato ed i nuovi dati scritti vengono aggiunti dopo quelli
preesistenti.
Aggiungere il carattere b al parametro mode apre i file in modalità binaria, altrimenti essi vengono aperti in modalità
testuale. Questo non ha alcun effetto sui sistemi compatibili POSIX (ad esempio GNU/LINUX), mentre invece è
importante nei sistemi non-POSIX, come Microsoft Windows, in cui i caratteri di carriage returno (codice ASCII
0x0D) vengono trasformati in carriage return + line feed (codici ASCII 0x0D + 0x0A) quando vengono scritti in un
file in modalità testo.
Quando si aprono file in modalità lettura/scrittura, lo standard prevede che ogni volta che si passa da un'operazione
di scrittura a una di lettura o viceversa, è necessario chiamare la funzione fflush() oppure fseek() per poter svuotare i
C/Appendice/Librerie standard/stdio.h 97

buffer prima del cambio di modalità.

fread()
Dichiarazione:

size_t fread(void* data, size_t size, size_t count, FILE* stream);

• Parametri in ingresso:
• void* data: indirizzo di un array in cui memorizzare i dati letti.
• size_t size: dimensione in byte del tipo di dati da leggere.
• size_t count: numero di elementi da leggere.
• FILE* stream: puntatore a una struttura di tipo FILE che rappresenta lo stream da cui leggere i dati.
• Valore di ritorno: size_t. numero di elementi letti. Normalmente è uguale al parametro count, ma può essere un
numero inferiore se viene incontrata la fine del file o se avviene un errore durante la lettura..
Legge dallo stream stream un numero di elementi specificato da count, ognuno della dimensione in byte size. Il
numero totale di byte che la funzione tenta di leggere dallo stream è size*count. I dati letti vengono memorizzati
nell'array data. La dimensione size è normalmente specficata come sizeof(tipoDati).

freopen()
Dichiarazione:

FILE* freopen(const char* path, const char* mode, FILE* stream);

• Parametri in ingresso:
• const char* path: indirizzo del file da aprire.
• const char* mode: sequenza di caratteri che identifica la modalità di apertura (vedi fopen()).
• FILE* stream: puntatore allo stream da riassegnare.
• Valore di ritorno: FILE*. ritorna il parametro stream in caso di successo, altrimenti NULL.
chiude lo stream stream e associa allo stesso un nuovo file. È grossomodo equivalente a fclose() seguito da fopen(),
salvo per il fatto che il parametro stream è garantito rimanere inalterato tramite la funzione freopen(). Un comune
utilizzo di questa funzione è il riassegnamento degli stream standard stdin, stdout e stderr a file arbitrari.

fseek()
Dichiarazione:

int fseek(FILE* stream, long int offset, int partenza);

• Parametri in ingresso:
• FILE* stream: puntatore allo stream.
• long int offset: numero di byte di cui spostarsi nello stream.
• int partenza: una delle costanti SEEK_SET, SEEK_CUR, SEEK_END, che identificano il punto di riferimento
da cui iniziare a contare i byte (rispettivamente: inizio file, posizione corrente, fine file).
• Valore di ritorno: int. ritorna 0 in caso di successo, altrimenti un valore non-nullo.
Sposta la posizione attuale nello stream: la seguente operazione di lettura o scrittura avverrà alla posizione indicata.
In caso di corretta esecuzione, il flag di end of file viene resettato e vengono cancellati tutti i caratteri inseriti nel
buffer di lettura tramite la funzione ungetc().
C/Appendice/Librerie standard/stdio.h 98

fsetpos()
Dichiarazione:

int fsetpos(FILE* stream, const fpos_t* pos);

• Parametri in ingresso:
• FILE* stream: puntatore allo stream.
• const fpos_t* pos: posizione (valore impostato da fgetpos().
• Valore di ritorno: int. ritorna 0 in caso di successo, altrimenti un valore non-nullo.
Sposta la posizione attuale nello stream ad una posizione precedentemente memorizzata attraverso la funzione
fgetpos(). In caso di corretta esecuzione, il flag di end of file viene resettato e vengono cancellati tutti i caratteri
inseriti nel buffer di lettura tramite la funzione ungetc(). In caso di errore, la variabile errno viene impostata a un
codice identificativo dell'errore.

ftell()
Dichiarazione:

long int ftell(FILE* stream);

• Parametri in ingresso:
• FILE* stream: puntatore allo stream.
• Valore di ritorno: long int. ritorna la posizione corrente nello stream, oppure -1L in caso di errore.
Riporta la posizione corrente del puntatore di lettura/scrittura del file stream. In caso di errore, la variabile errno
viene impostata a un codice identificativo dell'errore.

fwrite()
Dichiarazione:

size_t fwrite(const void * data, size_t size, size_t count, FILE* stream);

• Parametri in ingresso:
• const void * data: puntatore ad un array di dati.
• size_t size: dimensione di un oggetto da scrivere (sizeof(*data)).
• size_t count: numero di elementi da scrivere.
• FILE* stream: puntatore allo stream.
• Valore di ritorno: size_t. ritorna il parametro count in caso di successo, un valore diverso indica un errore..
Scrive un numero count di elementi di dimensione size dall'array data nello stream stream.

remove()
Dichiarazione:

int remove(const char* filename);

• Parametri in ingresso:
• const char* filename: nome del file.
• Valore di ritorno: int. ritorna 0 in caso di successo, altrimenti un valore non-nullo.
Cancella il file di nome filename.
C/Appendice/Librerie standard/stdio.h 99

rename()
Dichiarazione:

int rename(const char* oldfilename, const char* newfilename);

• Parametri in ingresso:
• const char* oldfilename: nome originale.
• const char* newfilename: nuovo nome.
• Valore di ritorno: int. ritorna 0 in caso di successo, altrimenti un valore non-nullo.
Modifica il nome del file oldfilename in newfilename. newfilename può trovarsi in un'altra directory rispetto al file
originale, ma sullo stesso file system. In caso di errore, la variabile errno viene impostata a un codice identificativo
dell'errore.

rewind()
Dichiarazione:

void rewind(FILE* stream);

• Parametri in ingresso:
• FILE* stream: puntatore allo stream.
• Valore di ritorno: void.
Reimposta la posizione del puntatore di lettura/scrittura del file stream all'inizio del file. Gli indicatori di errore e di
fine file sono reimpostati a zero.

setbuf()
Dichiarazione:

void setbuf(FILE* stream, char* buf);

• Parametri in ingresso:
• FILE* stream: puntatore allo stream.
• char* buf: array da usare come buffer (almeno di dimensione BUFSIZ), oppure NULL.
• Valore di ritorno: void.
Imposta il buffer per lo stream stream. Se il parametro buf è uguale a NULL, la funzione è equivalente a
setvbuf(stream, NULL, _IONBF, 0), altrimenti è equivalente a setvbuf(stream, buf, _IOFBF,
BUFSIZ).
Nota: la funzione setvbuf() è da preferire.

setvbuf()
Dichiarazione:

int setbuf(FILE* stream, char* buf, int mode, size_t size);

• Parametri in ingresso:
• FILE* stream: puntatore allo stream.
• char* buf: array da usare come buffer (almeno di dimensione size), oppure NULL.
• int mode: modalità di bufferizzazione. Può assumere come valori _IOFBF, _IOLBF oppure _IONBF.
• size_t size: dimensione del buffer.
• Valore di ritorno: int. ritorna 0 in caso di successo, altrimenti un valore non-nullo.
C/Appendice/Librerie standard/stdio.h 100

Imposta il buffer per lo stream stream. Il parametro mode controlla il tipo di bufferizzazione:
• _IOFBF: bufferizzazione completa;
• _IOLBF: bufferizzazione di una singola riga;
• _IONBF: nessuna bufferizzazione.
Se il parametro buf è uguale a NULL e il parametro mode è diverso da _IONBF, il buffer viene allocato
internamente tramite la funzione malloc(), e liberato alla chiusura dello stream.
Nota: finché l'array buf rimane associato ad uno stream, è bene non cercare di accedere ai suoi elementi
direttamente, perché gli effetti non sono definiti.

tmpfile()
Dichiarazione:

FILE* tmpfile();

• Valore di ritorno: FILE*. Restituisce un puntatore a uno stream valido in caso di successo, o NULL in caso di
errore.
Crea un file temporaneo e lo apre in modalità "wb+" (vedi fopen()). Alla chiusura dello stream o al termine del
programma, il file viene cancellato.

tmpnam()
Dichiarazione:

char* tmpnam(char* result);

• Parametri in ingresso:
• char* result: un array di almeno L_tmpnam caratteri, oppure NULL.
• Valore di ritorno: char*. Restituisce un puntatore ad una scringa contenente il nome del file.
Genera un nome di file valido che può essere usato come nome di un file temporaneo. Nel caso in cui il parametro
result sia NULL, il nome viene memorizzato in un array interno e viene restituito un puntatore a tale array, il che
rende la funzione non-rientrante (cioè modifica il suo stato se viene chiamata successivamente). Se invece il
parametro result è un array valido, il nome del file viene memorizzato in esso.
Nota: l'uso della funzione tmpfile è generalmente più sicuro.
C/Appendice/Librerie standard/stdlib.h 101

C/Appendice/Librerie standard/stdlib.h
stdlib.h è l'header file che, all'interno della libreria standard del C, dichiara funzioni e costanti di utilità generale:
allocazione della memoria, controllo dei processi, conversione tra tipi e così via.

Funzioni
Le funzioni di stdlib.h possono essere classificate nelle seguenti categorie: conversione tra tipi, gestione della
memoria, controllo dei processi, ricerca ed ordinamento, matematica semplice.

Nome Descrizione

Conversione tra tipi


double atof(const char *str) Converte una stringa in un numero in virgola mobile. Equivalente a strtod(s, (char**)NULL).

int atoi(const char *str) Converte una stringa in un numero intero. Equivalente a (int)strtol(s, (char**)NULL, 10).

long int atol(const char *str) Converte una stringa in un numero intero lungo (long int). Equivalente a strtol(s, (char**)NULL, 10).

double strtod(const char *str, char Converte una stringa in un double (numero a virgola mobile), effettuando dei controlli sull'overflow e
**endptr) restituendo anche l'eventuale parte non convertita della stringa.

long int strtol(const char *str, char Converte una stringa, che rappresenta un numero in una base arbitraria compresa tra 2 e 36, in un double
**endptr, int base) (numero a virgola mobile), effettuando dei controlli sull'overflow e restituendo anche l'eventuale parte non
convertita della stringa.

unsigned long int strtoul(const char Equivalente a strtol() tranne per il tipo del risultato, che è unsigned long.
*str, char **endptr, int base)

Generazione di numeri pseudocasuali


int rand(void) Restituisce un numero intero pseudocasuale compreso tra 0 e RAND_MAX.

void srand(unsigned int seed) Inizializza il seme per la sequenza di numeri pseudocasuali della funzione rand().

Allocazione e deallocazione di memoria


void* calloc(size_t nitems, size_t Funzioni che si occupano dell'allocazione dinamica della memoria.
size),
void* malloc(size_t size) e
void* realloc(void *ptr, size_t size)

void free(void *ptr) Libera la memoria allocata dinamicamente dalla famiglia di funzioni di allocazione dinamica.

Controllo dei processi


void abort(void) Causa la terminazione immediata ed anormale del programma, come se fosse stato invocato
raise(SIGABRT).

int atexit(void (*func)(void)) Registra una funzione, della quale le viene passato il puntatore, affinché venga eseguita appena prima della
normale terminazione del programma.

void exit(int status) Causa la normale terminazione del programma. Tutte le funzioni registrate con atexit() vengono eseguite con
ordine inverso rispetto alla loro registrazione, gli stream associati al programma vengono liberati, i file
vengono scritti su disco (vedere flush()) ed il controllo viene restituito all'ambiente chiamante, assieme ad un
valore numerico, che generalmente indica lo stato del programma o la causa della sua terminazione, che deve
essere fornito alla funzione stessa.

char* getenv(const char *name) Restituisce la stringa che nell'ambiente di lavoro del programma è associata al nome fornito, oppure NULL
se non esiste alcuna stringa. I dettagli della funzione sono strettamente dipendenti dal sistema operativo.

int system(const char *string) Passa la stringa fornitale all'ambiente di lavoro per l'esecuzione e restituisce il codice d'uscita del comando
invocato. Se si fornisce NULL, informa sulla eventuale presenza nel sistema di un processore di comandi.

Ricerca ed ordinamento
C/Appendice/Librerie standard/stdlib.h 102

void *bsearch(const void *key, const Implementa in maniera generica l'algoritmo di ricerca dicotomica.
void *base, size_t nitems, size_t
size, int (*compar)(const void *,
const void *))

void qsort(void *base, size_t nitems, Implementa in maniera generica l'algoritmo di ordinamento quicksort.
size_t size, int (*compar)(const void
*, const void*))

Matematica semplice - presenti anche in math.h


int abs(int x),, Calcola il valore assoluto dell'argomento.
long int labs(long int x),

div_t div(int numer, int denom), Calcola il quoziente ed il resto della divisione intera tra il dividendo ed il divisore forniti.
ldiv_t ldiv(long int numer, long int
denom)

C/Appendice/Librerie standard/string.h
string.h è l'header file della libreria standard del C che contiene definizioni di macro, costanti e dichiarazioni di
funzioni e tipi usati non solo nella manipolazione delle stringhe ma anche nella manipolazione della memoria.

Costanti e tipi
Nome Descrizione

NULL Una macro che rappresenta la costante puntatore nullo; in altre parole, una costante che rappresenta un valore che è garantito essere
l'indirizzo di una posizione non valida nella memoria.

size_t Un intero senza segno restituito dell'operatore sizeof.

Funzioni
Nome Descrizione

void *memcpy(void *dest, const void Copia n bytes tra due aree di memoria che non devono sovrapporsi.
*src, size_t n);

void *memmove(void *dest, const void Copia n bytes tra due aree di memoria; a differenza di memcpy le aree di memoria possono sovrapporsi.
*src, size_t n);

void *memchr(const void *s, int c, size_t Ritorna un puntatore alla prima occorrenza di c in s, o NULL se c non compare tra i primi n caratteri di
n); s.

int memcmp(const void *s1, const void Confronta i primi n caratteri di s1 con s2.
*s2, size_t n);

void *memset(void *s, int c, size_t n); Colloca c nei primi n caratteri di s.

char *strcat(char *dest, const char *src); Concatena src alla stringa dest.

char *strncat(char *dest, const char *src, Concatena al massimo n caratteri src alla stringa dest.
size_t n);

char *strchr(const char *s, int c); Restituisce un puntatore alla prima occorrenza di c in s.

char *strrchr(const char *s, int c); Restituisce un puntatore all'ultima occorrenza di c in s.

int strcmp(const char *s1, const char Confronta la stringa s1 con s2.
*s2);
C/Appendice/Librerie standard/string.h 103

int strncmp(const char *, const char *, Confronta al massimo n caratteri della stringa s1 con s2.
size_t);

int strcoll(const char *, const char *); Confronta due stringhe utilizzando l'ordine lessicografico stabilito dalla localizzazione utilizzata

char *strcpy(char *s1, const char *s2); Copia la stringa s2 nella stringa s1, incluso il carattere di terminazione \0.

char *strncpy(char *s1, const char *s2, Copia al massimo n caratteri della stringa s2 in s1.
size_t n);

char *strerror(int n); Restituisce un puntatore alla stringa che corrisponde all'errore n.

size_t strlen(const char *s); Restituisce la lunghezza della stringa s.

size_t strspn(const char *s, const char Restituisce la lunghezza della porzione iniziale della stringa s di lunghezza massima composta
*accept); esattamente dai caratteri della stringa accept

size_t strcspn(const char *s, const char Restituisce la lunghezza della porzione iniziale della stringa s di lunghezza massima composta
*reject); esattamente da caratteri diversi da quelli della stringa reject

char *strpbrk(const char *s, const char Restituisce la prima occorrenza di un carattere presente nella stringa s che sia uguale ad un qualsiasi
*accept); carattere presente nella stringa accept

char *strstr(const char *haystack, const Trova la prima occorrenza della stringa needle all'interno della stringa haystack
char *needle);

char *strtok(char *s, const char Spezza la stringa s in una serie di stringhe chiamate [token] in corrispondenza dei carattere delimitatore
*delimiters); delimiters

size_t strxfrm(char *dest, const char Trasforma la stringa puntata da src secondo la localizzazione in uso e e copia i primi n caratteri di src
*src, size_t n); nella stringa dest

C/Appendice/Librerie standard/time.h
time.h è l'header file della libreria standard del C che fornisce un accesso standardizzato alle funzioni di acquisizione
e manipolazione del tempo.

Cicli CPU
La funzione clock() consente di ottenere il tempo di utilizzo del microprocessore (CPU), espresso virtualmente in
cicli di CPU. In pratica, viene definita la macro-variabile CLOCKS_PER_SEC, contenente il valore che esprime
convenzionalmente la quantità di cicli di CPU per secondo; quindi, il valore restituito dalla funzione clock() si
traduce in secondi dividendolo per CLOCKS_PER_SEC. Il valore restituito dalla funzione clock() e l'espressione in
cui si traduce la macro-variabile CLOCKS_PER_SEC sono di tipo clock_t:

/* Unità di tempo convenzionale che


rappresenta un ciclo virtuale di CPU. */
typedef long int clock_t;

/* Valore convenzionale di 1 s, in
termini di cicli virtuali di CPU. */
#define CLOCKS_PER_SEC 1000000L

clock_t clock (void); /* Tempo di utilizzo della CPU. */

La funzione clock() restituisce il tempo di CPU espresso in unità clock_t, utilizzato dal processo elaborativo a partire
dall'avvio del programma. Se la funzione non è in grado di dare questa indicazione, allora restituisce il valore -1, o
più precisamente (clock_t) (-1).
C/Appendice/Librerie standard/time.h 104

Per valutare l'intervallo di tempo di utilizzo della CPU, da una certa posizione del programma, a un'altra, occorre
memorizzare i valori ottenuti dalla funzione e poi procedere a una sottrazione.

Rappresentazione del tempo


La libreria definisce il tipo time_t che, secondo lo standard, rappresenta la quantità di unità di tempo trascorsa a
partire da un'epoca di riferimento.

typedef long int time_t;

Rappresentazione del tempo in una struttura


La libreria standard prescrive che sia definito il tipo struct tm, con il quale è possibile rappresentare tutte le
informazioni relative a un certo tempo, secondo le convenzioni umane. Lo standard prescrive con precisione i
membri minimi della struttura e l'intervallo di valori che possono contenere:

struct tm {
int tm_sec; // Secondi: da 0 a 60.
int tm_min; // Minuti: da 0 a 59.
int tm_hour; // Ora: da 0 a 23.
int tm_mday; // Giorno del mese: da 1 a 31.
int tm_mon; // Mese dell'anno: da 0 a 11.
int tm_year; // Anno dal 1900.
int tm_wday; // Giorno della settimana: da 0 a 6
// con lo zero corrispondente alla domenica.
int tm_yday; // Giorno dell'anno: da 0 a 365.
int tm_isdst; // Ora estiva. Contiene un valore positivo
// se è in vigore l'ora estiva; zero se l'ora
// è quella «normale» ovvero quella invernale;
// un valore negativo se l'informazione non è
// disponibile.
};

Si può osservare che il mese viene rappresentato con valori che vanno da 0 a 11, pertanto gennaio si indica con lo
zero e dicembre con il numero 11; inoltre, l'intervallo ammesso per i secondi consente di rappresentare un secondo in
più, dato che l'intervallo corretto sarebbe da 0 a 59; infine, il fatto che i giorni dell'anno vadano da 0 (il primo) a 365
(l'ultimo), significa che negli anni normali i valori vanno da 0 a 364, mentre negli anni bisestili si arriva a contare
fino a 365.
C/Appendice/Librerie standard/time.h 105

Funzioni per il tempo


Un gruppo di funzioni dichiarate nel file time.h ha lo scopo di elaborare in qualche modo le informazioni legate al
tempo. Queste funzioni trattano il tempo in forma di variabili di tipo time_t o di tipo struct tm.
La variabile di tipo time_t che viene usata in queste funzioni potrebbe esprimere un valore riferito al tempo
universale (UT), mentre le funzioni che la utilizzano dovrebbero tenere conto del fuso orario.

time()
La funzione time() determina il tempo attuale secondo il calendario del sistema operativo, restituendolo nella forma
del tipo time_t. La funzione richiede un parametro, costituito da un puntatore di tipo time_t *: se questo puntatore è
valido, la stessa informazione che viene restituita viene anche memorizzata nell'indirizzo indicato da tale puntatore.

time_t time (time_t *timer);

In pratica, se è possibile (valore diverso da NULL), l'informazione data-orario raccolta dalla funzione, viene anche
memorizzata in *timer.
Se la funzione non può fornire l'informazione richiesta, allora restituisce il valore -1, o più precisamente: (time_t)
(-1).

difftime()
La funzione difftime() calcola la differenza tra due date, espresse in forma time_t e restituisce l'intervallo in secondi,
in una variabile in virgola mobile, di tipo double:

double difftime (time_t time1, time_t time0);

Per la precisione, viene eseguito time1-time0 e di conseguenza va il segno del risultato.

mktime()
La funzione mktime() riceve come argomento il puntatore a una variabile strutturata di tipo struct tm, contenente le
informazioni sull'ora locale, e determina il valore di quella data secondo la rappresentazione interna, di tipo time_t:

time_t mktime (struct tm *timeptr);

La funzione tiene in considerazione solo alcuni membri della struttura; per la precisione, non considera il giorno
della settimana e il giorno dell'anno; inoltre, ammette anche valori al di fuori degli intervalli stabiliti per i vari
membri della struttura; infine, considera un valore negativo per il membro timeptr->tm_isdst come la richiesta di
determinare se sia o meno in vigore l'ora estiva per la data indicata.
Se la funzione non è in grado di restituire un valore rappresentabile nel tipo time_t, o comunque se non può eseguire
il suo compito, restituisce il valore -1, o più precisamente (time_t) (-1). Se invece tutto procede regolarmente, la
funzione provvede anche a correggere i valori dei vari membri della struttura e a ricalcolare il giorno della settimana
e dell'anno.
C/Appendice/Librerie standard/time.h 106

gmtime() e localtime()
Le funzioni gmtime() e localtime() hanno in comune il fatto di ricevere come argomento il puntatore di tipo time_t *,
a un'informazione data-orario, per restituire il puntatore a una variabile strutturata di tipo struct tm *. In altri termini,
le due funzioni convertono una data espressa nella forma del tipo time_t, in una data suddivisa nella struttura tm:

struct tm *gmtime (const time_t *timer);


struct tm *localtime (const time_t *timer);

Nell'ambito di queste funzioni, è ragionevole supporre che l'informazione di tipo time_t a cui fanno riferimento, sia
espressa in termini di tempo universale e che le funzioni stesse abbiano la possibilità di stabilire il fuso orario e la
modalità di regolazione dell'ora estiva.
In ogni caso, la differenza tra le due funzioni sta nel fatto che gmtime() traduce il tempo a cui punta il suo argomento
in una struttura contenente la data tradotta secondo il tempo coordinato universale, mentre localtime() la traduce
secondo l'ora locale.
Va osservato che queste funzioni restituiscono un puntatore a un'area di memoria che può essere sovrascritta da altre
chiamate alle stessi funzioni o a funzioni simili.

Conversione in stringhe
Un gruppo di funzioni del file time.h è destinato alla conversione dei valori data-orario in stringhe, per
l'interpretazione umana.

asctime()
La funzione asctime() converte un'informazione data-orario, espressa nella forma di una struttura struct tm, in una
stringa che esprime l'ora locale, usando però una rappresentazione fissa in lingua inglese:

char *asctime (const struct tm *timeptr);

ctime()
La funzione ctime() converte un'informazione data-orario, espressa nella forma del tipo time_t in una stringa che
esprime l'ora locale, usando però una rappresentazione fissa in lingua inglese:

char *ctime (const time_t *timer);

strftime()
La funzione strftime() si occupa di interpretare il contenuto di una struttura di tipo struct tm e di tradurlo in un testo,
secondo una stringa di composizione libera. In altri termini, questa funzione si comporta in modo simile a printf(),
dove l'input è costituito dalla struttura contenente le informazioni data-orario.

size_t strftime (char * restrict s, size_t maxsize,


const char * restrict format,
const struct tm * restrict timeptr);

Dal modello del prototipo della funzione, si vede che questa restituisce un valore numerico di tipo size_t. Questo
valore rappresenta la quantità di elementi che sono stati scritti nella stringa di destinazione, rappresentata dal primo
parametro. Dal computo di questi elementi è escluso il carattere nullo di terminazione, ma questo viene comunque
aggiunto dalla funzione.
La funzione richiede, nell'ordine: un array di caratteri da utilizzare per comporre il testo; la dimensione massima di
questo array; la stringa di composizione, contenente del testo costante e degli specificatori di conversione; il
C/Appendice/Librerie standard/time.h 107

puntatore alla struttura contenente le informazioni data-orario da usare nella conversione.


La funzione termina il proprio lavoro con successo solo se può scrivere nell'array di destinazione il testo composto
secondo le indicazioni della stringa di composizione, includendo anche il carattere nullo di terminazione. Se ciò non
avviene, il valore restituito dalla funzione è zero e il contenuto dell'array di destinazione è imprecisato.
Il listato successivo mostra un programma completo che dimostra il funzionamento di strftime(). Va osservato che la
conversione eseguita da tale funzione è sensibile alla configurazione locale; precisamente dipende dalla categoria
LC_TIME:

#include <stdio.h>
#include <locale.h>
#include <time.h>

int main (void)


{
char s[100];
int dim;
time_t t = time (NULL);
struct tm *tp = localtime (&t);

setlocale (LC_ALL, "");


dim = strftime (s, 100, "Salve: sono le %H:%M del %d %B %Y.", tp);
printf ("%d: %s\n", dim, s);

return 0;
}

Ecco cosa si potrebbe ottenere eseguendo questo programma in un sistema con localizzazione in italiano:
42 Salve: sono le 10:31 del 30 aprile 2010.
Nella tabella successiva vengono elencati gli specificatori di conversione principali. Sono ammissibili delle varianti,
con l'aggiunta di modificatori, che però non vengono descritte. Per esempio è ammissibile l'uso degli specificatori
%Ec e %Od, per indicare rispettivamente una variante di %c e %d.

Specificatori di conversione usati dalla funzione strftime()

Specificatore Corrispondenza

%C Il secolo, ottenuto dividendo l'anno per 100 e ignorando i decimali.

%y L'anno: nel primo caso si mostrano solo le ultime due cifre, mentre nel secondo si mostrano tutte.

%Y

%b Rispettivamente, il nome abbreviato e il nome per esteso del mese.

%B

%m Il numero del mese, da 01 a 12.

%d Il giorno del mese, in forma numerica, da 1 a 31, utilizzando sempre due cifre: nel primo caso si aggiunge eventualmente uno zero;
nel secondo si aggiunge eventualmente uno spazio.
%e

%a Rispettivamente, il nome abbreviato e il nome per esteso del giorno della settimana.

%A
C/Appendice/Librerie standard/time.h 108

%H L'ora, espressa rispettivamente a 24 ore e a 12 ore.

%L

%p La sigla da usare, secondo la configurazione locale, per specificare che si tratta di un'ora antimeridiana o pomeridiana. Nella
convenzione inglese si ottengono, per esempio, le sigle «AM» e «PM».

%r L'ora espressa a 12 ore, completa dell'indicazione se trattasi di ora antimeridiana o pomeridiana, secondo le convenzioni locali.

%R L'ora e i minuti, equivalente a %H:%M.

%M I minuti, da 00 a 59.

%S I secondi, espresso con valori da 00 a 60.

%T L'ora, i minuti e i secondi, equivalente a %H:%M:%S.

%z La rappresentazione del fuso orario, nel primo caso come distanza dal tempo coordinato universale (UTC), mentre nel secondo si
usa una rappresentazione conforme alla configurazione locale.
%Z

%j Il giorno dell'anno, usando sempre tre cifre numeriche: da 001 a 366.

%g L'anno a cui appartiene la settimana secondo lo standard ISO 8601: nel primo caso si mostrano solo le ultime due cifre, mentre nel
secondo si ha l'anno per esteso. Secondo lo standard ISO 8601 la settimana inizia con lunedì e la prima settimana dell'anno è quella
%G che include il 4 gennaio.

%V Il numero della settimana secondo lo standard ISO 8601. I valori vanno da 01 a 53. Secondo lo standard ISO 8601 la settimana
inizia con lunedì e la prima settimana dell'anno è quella che include il 4 gennaio.

%u Il giorno della settimana, espresso in forma numerica, dove, rispettivamente, si conta da 1 a 7, oppure da 0 a 6. Zero e sette
rappresentano la domenica; uno è il lunedì.
%w

%U Il numero della settimana, contando, rispettivamente, dalla prima domenica o dal primo lunedì di gennaio. Si ottengono cifre da 00 a
53.
%W

%x La data, rappresentata secondo le convenzioni locali.

%X L'ora, rappresentata secondo le convenzioni locali.

%c La data e l'ora, rappresentate secondo le convenzioni locali.

%D La data, rappresentata come %m/%d/%Y.

%F La data, rappresentata come %Y-%m-%d.

%n Viene rimpiazzato dal codice di interruzione di riga.

%t Viene rimpiazzato da una tabulazione orizzontale.

%% Viene rimpiazzato da un carattere di percentuale.


C/Appendice/Approfondimenti 109

C/Appendice/Approfondimenti
Una volta imparato il linguaggio C, si possono imparare altri linguaggi basati sul C. Ecco una lista dei manuali di
questi linguaggi:
• Dal C al C++
• Objective-C
• Linguaggio Java

Altri progetti
• Wikipedia contiene una voce riguardante Il linguaggio C

C/Appendice/Strumenti
Per sviluppare in C sono necessari due strumenti:
1. un sistema di compilazione (un insieme di programmi, come compilatore, linker, debugger, ecc.) che controlla gli
errori e traduce il sorgente C in un eseguibile
2. un editor di testi
È possibile usare qualsiasi editor di testi disponibile sul proprio sistema operativo, ma si segnalano le soluzioni
gratuite, per venire incontro alle più svariate esigenze, da chi vuole scrivere il codice direttamente dalla shell Linux a
chi vuole un sistema integrato sotto Windows che permetta anche di compilare.

Per Windows
Ci sono diversi compilatori e ambienti di sviluppo integrato (IDE) per Windows qui si segnalano i più leggeri:
• Tcc, Tiny C Compiler. Si tratta di un compilatore sotto licenza libera (LGPL) estremamente piccolo che ha come
scopo quello produrre codice molto leggero, inoltre può funzionare da interprete. È scritto e mantenuto da Fabrice
Bellard ed è reperibile qui [1]
• MinGW, Minimalist GNU for Windows, è l'adattamento in ambiente Windows del famoso compilatore GCC per
GNU/Linux rilasciato sotto licenza libera. Inoltre ci sono alcune utilità tipiche di GNU/Linux. Infatti questo
progetto è nato proprio per rendere facile la programmazione a chi era abituato a sviluppare con GCC. Si può
ottenere a questo indirizzo [2] nella sezione download.
• Pelles C, è un kit completo di sviluppo per Windows e Windows Mobile che supporta molte funzionalità del C99.
Il compilatore si basa su LCC (di Chris Fraser e David Hanson) ed è stato creato da Pelle Orinius ed è gratuito e
liberamente distribuibile per scopi non commerciali, inoltre è un IDE leggero e specifico per i linguaggio C. È
reperibile al seguente indirizzo [3].
• Dev-C++ è un IDE gratuito distribuito sotto la Licenza GNU per la programmazione in C/C++. Il programma ha
un aspetto simile al più largamente usato Microsoft Visual Studio. Una caratteristica in più di Dev-C++ è il suo
uso dei DevPaks, estensioni sull'ambiente di programmazione con librerie addizionali, template e strumenti. È
reperibile al seguente indirizzo [4].
C/Appendice/Strumenti 110

Per GNU/Linux
Praticamente lo standard è GCC.
• GCC (GNU Compiler Collection, in origine GNU C Compiler) è un compilatore creato inizialmente dal
fondatore della Free Software Foundation, Richard Stallman, come parte del Progetto GNU. Le versioni recenti
sono incorporate nelle principali distribuzioni del sistema operativo GNU/Linux. Ovviamente è software libero.
• KDevelop. IDE Realizzato come parte integrante dello stesso desktop KDE, è rilasciato con licenza GPL e
permette la programmazione in Bash, C/C++, Fortran, Java, Perl ed altri linguaggi, offrendo funzionalità diverse a
seconda di quello utilizzato. Una delle sue caratteristiche più interessanti riguarda la leggerezza che offre in
termini di risorse necessarie al suo funzionamento.
• Anjuta. Permette di programmare in C e C++. Risulta un'applicazione elegante ed ordinata, con un'interfaccia
grafica semplice da utilizzare. Di solito non è installato insieme al desktop, e per questo è necessario scaricarlo
dal sito ufficiale [5] o utilizzare i pacchetti inclusi nei repository della propria distribuzione.

Note
[1] http:/ / bellard. org/ tcc/
[2] http:/ / www. mingw. org
[3] http:/ / www. smorgasbordet. com/ pellesc
[4] http:/ / www. bloodshed. net/ dev
[5] http:/ / projects. gnome. org/ anjuta/ index. shtml

C/Indice
• __DATE__: »1
• __FILE__: »1
• __LINE__: »1
• __STDC__: »1
• __TIME__: »1
• AND: »1 »2
• argc: »1
• argv: »1 »2
• aritmetica dei puntatori: »1
• array, accesso: »1
• array, assegnamento: »1
• array, dichiarazione: »1
• array, dimensioni: »1
• array, indice: »1
• array, passaggio ad una funzione: »1
• array: »1 »2
• break: »1 »2
• BSD: »1
• C#: »1
• C++: »1 »2
• campi bit: »1
• carattere nullo: »1
• case-sensitive: »1
• case: »1
C/Indice 111

• char: »1 »2
• compilatore: »1
• compilazione condizionale: »1
• compilazione: »1
• complemento a uno: »1
• const: »1 »2
• continue: »1
• conversione cast: »1
• costante: »1
• costanti: »1 »2 »3
• CPU: »1
• default: »1
• define: »1
• definizione induttiva: »1
• direttiva: »1 »2
• direttive #define: »1
• direttive #if, #else, #elif ed #endif: »1
• direttive #include: »1
• direttive #line: »1
• direttive #pragma: »1
• direttive #undef: »1
• direttive: »1
• do-while: »1
• double: »1
• driver: »1
• else: »1
• enum: »1
• enumerazioni: »1
• espressioni: »1
• exit: »1
• extern: »1
• falsità: »1
• fattoriale: »1
• fclose: »1
• fflush: »1
• fgets: »1
• Fibonacci, successione: »1
• File di header: »1
• float: »1
• fopen, modalità: »1
• fopen: »1
• for: »1
• fprintf: »1
• fputs: »1
• fscanf: »1
• fseek: »1
• funzione: »1 »2
C/Indice 112

• funzioni, parametri: »1
• funzioni, puntatori a: »1
• funzioni: »1
• garbage: »1
• gcc: »1
• gdb: »1
• getc: »1
• Hello World: »1
• if: »1
• indirizzo di memoria: »1
• int: »1
• intero: »1
• Java: »1 »2
• libreria standard: »1
• linker: »1
• Linux: »1
• long: »1
• Mac OS X: »1
• macro: »1 »2
• main: »1 »2 »3
• memoria: »1
• metodo induttivo: »1
• NOT: »1
• Objective-C: »1 »2
• operatore ##: »1
• operatore #: »1
• operatore ?: »1 »2
• operatore aritmetico: »1
• operatore defined: »1
• operatore di decremento: »1
• operatore di incremento: »1
• operatore freccia: »1
• operatore punto: »1
• operatore uguale: »1
• operatori logici: »1 »2 »3
• operatori sui bit: »1
• OR: »1 »2
• Panoramica: »1
• POSIX: »1
• precompilatore: »1
• preprocessore: »1
• puntatore: »1 »2 »3 »4
• puntatori a funzioni: »1
• puntatori a puntatori: »1
• puntatori a strutture: »1
• puntatori, accesso: »1
• puntatori, aritmetica: »1 »2
C/Indice 113

• puntatori, array: »1
• puntatori, assegnamento: »1
• puntatori, dichiarazione: »1
• puntatori: »1 »2
• putc: »1
• register: »1
• return: »1
• rewind: »1
• ricorsione, base: »1
• ricorsione, passo: »1
• ricorsione: »1
• ricorsività: »1
• scorrimento a destra: »1
• scorrimento a sinistra: »1
• short-circuit: »1
• short: »1
• signed: »1
• sistema operativo: »1
• static: »1
• stdio: »1
• strcpy: »1 »2
• stream: »1
• stringhe: »1
• struct: »1
• struttura: »1
• strutture, membri: »1
• strutture, puntatori a: »1
• strutture: »1
• switch: »1
• union: »1
• unione, membri: »1
• unioni, dichiarazione: »1
• unioni: »1
• UNIX: »1
• unsigned: »1
• variabile: »1 »2
• variabili, dichiarazione: »1
• variabili, inizializzazione: »1
• variabili, nomi: »1
• variabili: »1 »2
• verità: »1
• vettori, inizializzazione: »1
• vettori: »1 »2 »3
• void: »1 »2
• volatile: »1
• while: »1
• XOR: »1 »2
Fonti e autori delle voci 114

Fonti e autori delle voci


C/Copertina  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182298  Autori:: Diablo, Frank50 s, Pietrodn, 2 Modifiche anonime

C/Linguaggio  Fonte:: http://it.wikibooks.org/w/index.php?oldid=145883  Autori:: Carlo.milanesi, Daniele, Massimiliano Lincetto, Pietrodn, Thom, 2 Modifiche anonime

C/Linguaggio/Panoramica  Fonte:: http://it.wikibooks.org/w/index.php?oldid=181695  Autori:: Farkite, Frank50 s, Otrebla86, Pietrodn, Rojelio, The Doc, Wiso, 8 Modifiche anonime

C/Linguaggio/Struttura del linguaggio  Fonte:: http://it.wikibooks.org/w/index.php?oldid=183270  Autori:: Frank50 s, Ramac, 1 Modifiche anonime

C/Variabili, operatori e costanti  Fonte:: http://it.wikibooks.org/w/index.php?oldid=90678  Autori:: G4, Pietrodn

C/Variabili, operatori e costanti/Variabili  Fonte:: http://it.wikibooks.org/w/index.php?oldid=193405  Autori:: Carlo.milanesi, Diablo, Frank50 s, Jhc, MarcoBardelli, Pietrodn, Qualc1, Rojelio,
Wiso, 35 Modifiche anonime

C/Variabili, operatori e costanti/Operatori aritmetici  Fonte:: http://it.wikibooks.org/w/index.php?oldid=109670  Autori:: Pietrodn, 1 Modifiche anonime

C/Variabili, operatori e costanti/Operatori sui bit  Fonte:: http://it.wikibooks.org/w/index.php?oldid=190941  Autori:: Carlo.milanesi, Pietrodn, Ramac, 3 Modifiche anonime

C/Variabili, operatori e costanti/Costanti  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182973  Autori:: Frank50 s, G4, Pietrodn, 1 Modifiche anonime

C/Blocchi e funzioni  Fonte:: http://it.wikibooks.org/w/index.php?oldid=181765  Autori:: Az1568, Frank50 s, G4, Pietrodn, 1 Modifiche anonime

C/Blocchi e funzioni/Blocchi if e switch  Fonte:: http://it.wikibooks.org/w/index.php?oldid=193407  Autori:: Bouncey2k, Carlo.milanesi, Frank50 s, Pietrodn, Ramac, 5 Modifiche anonime

C/Blocchi e funzioni/Operatori logici  Fonte:: http://it.wikibooks.org/w/index.php?oldid=183139  Autori:: Arcturus4669, Frank50 s, Pietrodn, Rojelio, 4 Modifiche anonime

C/Blocchi e funzioni/Cicli  Fonte:: http://it.wikibooks.org/w/index.php?oldid=186569  Autori:: Beat, Pietrodn, 6 Modifiche anonime

C/Blocchi e funzioni/Funzioni  Fonte:: http://it.wikibooks.org/w/index.php?oldid=187699  Autori:: Carlo.milanesi, Frank50 s, Otrebla86, Pietrodn, Rojelio, 5 Modifiche anonime

C/Blocchi e funzioni/main  Fonte:: http://it.wikibooks.org/w/index.php?oldid=184663  Autori:: Arcturus4669, Frank50 s, Pietrodn, 8 Modifiche anonime

C/Blocchi e funzioni/Librerie  Fonte:: http://it.wikibooks.org/w/index.php?oldid=146584  Autori:: Diablo, G4, Pietrodn, Pigr8, 1 Modifiche anonime

C/Blocchi e funzioni/Ricorsività  Fonte:: http://it.wikibooks.org/w/index.php?oldid=112660  Autori:: Beat, Pietrodn, Simmyg89, The Doc, 5 Modifiche anonime

C/Vettori e puntatori  Fonte:: http://it.wikibooks.org/w/index.php?oldid=90682  Autori:: Diablo, G4, Pietrodn

C/Vettori e puntatori/Vettori  Fonte:: http://it.wikibooks.org/w/index.php?oldid=183351  Autori:: Frank50 s, Pietrodn, 7 Modifiche anonime

C/Vettori e puntatori/Puntatori  Fonte:: http://it.wikibooks.org/w/index.php?oldid=193432  Autori:: Bonecio, Carlo.milanesi, Frank50 s, Pietrodn, Rojelio, 8 Modifiche anonime

C/Vettori e puntatori/Interscambiabilità tra puntatori e vettori  Fonte:: http://it.wikibooks.org/w/index.php?oldid=181768  Autori:: Frank50 s, Pietrodn, Ramac, 3 Modifiche anonime

C/Variabili, operatori e costanti/Stringhe  Fonte:: http://it.wikibooks.org/w/index.php?oldid=185273  Autori:: Frank50 s, Pietrodn, The Doc, 20 Modifiche anonime

C/Enumerazioni, strutture e unioni  Fonte:: http://it.wikibooks.org/w/index.php?oldid=90683  Autori:: G4, Pietrodn

C/Enumerazioni, strutture e unioni/Enumerazioni  Fonte:: http://it.wikibooks.org/w/index.php?oldid=163369  Autori:: Davide89v, Massimiliano Lincetto, Pietrodn

C/Enumerazioni, strutture e unioni/Strutture  Fonte:: http://it.wikibooks.org/w/index.php?oldid=70952  Autori:: Pietrodn, Ryuujin, 3 Modifiche anonime

C/Enumerazioni, strutture e unioni/Unioni  Fonte:: http://it.wikibooks.org/w/index.php?oldid=115925  Autori:: Baruneju, Loreto, Pietrodn, 1 Modifiche anonime

C/Conversioni di tipo  Fonte:: http://it.wikibooks.org/w/index.php?oldid=185314  Autori:: Frank50 s, 17 Modifiche anonime

C/Lettura e scrittura su file  Fonte:: http://it.wikibooks.org/w/index.php?oldid=192297  Autori:: Diablo, Frank50 s, Pietrodn, 7 Modifiche anonime

C/Compilatore e precompilatore  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182942  Autori:: Frank50 s, G4, Pietrodn

C/Compilatore e precompilatore/Compilatore  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182999  Autori:: Arcturus4669, Blacksheep, Diablo, Frank50 s, Netx512k, Pietrodn, Ramac,
3 Modifiche anonime

C/Compilatore e precompilatore/Direttive  Fonte:: http://it.wikibooks.org/w/index.php?oldid=137173  Autori:: Ivanscillone, Pietrodn, Rojelio, 2 Modifiche anonime

C/Compilatore e precompilatore/Header  Fonte:: http://it.wikibooks.org/w/index.php?oldid=183092  Autori:: Frank50 s, Pietrodn, Wim b

C/Visibilità  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182906  Autori:: Frank50 s

C/Gestione della memoria  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182846  Autori:: Frank50 s, Ramac, 4 Modifiche anonime

C/Le applicazioni CGI  Fonte:: http://it.wikibooks.org/w/index.php?oldid=187110  Autori:: Frank50 s, Ramac, 4 Modifiche anonime

C/Appendice/Librerie standard  Fonte:: http://it.wikibooks.org/w/index.php?oldid=78949  Autori:: IngFrancesco, Wim b, 1 Modifiche anonime

C/Appendice/Librerie standard/assert.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=183004  Autori:: Frank50 s

C/Appendice/Librerie standard/ctype.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=183005  Autori:: Frank50 s

C/Appendice/Librerie standard/errno.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=183007  Autori:: Frank50 s

C/Appendice/Librerie standard/float.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=183008  Autori:: Frank50 s

C/Appendice/Librerie standard/limits.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182280  Autori:: Frank50 s

C/Appendice/Librerie standard/locale.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182324  Autori:: Frank50 s, 5 Modifiche anonime

C/Appendice/Librerie standard/math.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182248  Autori:: Frank50 s

C/Appendice/Librerie standard/setjmp.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=183814  Autori:: Frank50 s

C/Appendice/Librerie standard/signal.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182556  Autori:: Frank50 s

C/Appendice/Librerie standard/stdarg.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182422  Autori:: Frank50 s

C/Appendice/Librerie standard/stddef.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182420  Autori:: Frank50 s

C/Appendice/Librerie standard/stdio.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=90687  Autori:: G4, IngFrancesco, Wim b


Fonti e autori delle voci 115

C/Appendice/Librerie standard/stdlib.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182175  Autori:: Frank50 s

C/Appendice/Librerie standard/string.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182115  Autori:: Diablo, Frank50 s, Ramac, 1 Modifiche anonime

C/Appendice/Librerie standard/time.h  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182411  Autori:: Frank50 s, 1 Modifiche anonime

C/Appendice/Approfondimenti  Fonte:: http://it.wikibooks.org/w/index.php?oldid=182183  Autori:: Frank50 s

C/Appendice/Strumenti  Fonte:: http://it.wikibooks.org/w/index.php?oldid=183032  Autori:: Frank50 s

C/Indice  Fonte:: http://it.wikibooks.org/w/index.php?oldid=186166  Autori:: Diablo, Frank50 s, G4, Pietrodn


Fonti, licenze e autori delle immagini 116

Fonti, licenze e autori delle immagini


File:ImgCopertinaC.png  Fonte:: http://it.wikibooks.org/w/index.php?title=File:ImgCopertinaC.png  Licenza: Creative Commons Attribution-Sharealike 2.5  Autori:: User:frank50_s
Immagine:Pointers_in_programming_it.svg  Fonte:: http://it.wikibooks.org/w/index.php?title=File:Pointers_in_programming_it.svg  Licenza: Creative Commons Attribution-Sharealike 2.5
 Autori:: Pietrodn
File:Compilazione.png  Fonte:: http://it.wikibooks.org/w/index.php?title=File:Compilazione.png  Licenza: GNU Free Documentation License  Autori:: Frank50 s
Immagine:Wikipedia-logo.png  Fonte:: http://it.wikibooks.org/w/index.php?title=File:Wikipedia-logo.png  Licenza: logo  Autori:: Abigor, Bastique, Cary Bass, Guillom, Krinkle,
Mike.lifeguard, Richie, Rocket000, Schaengel89
Licenza 117

Licenza
Creative Commons Attribution-Share Alike 3.0 Unported
http:/ / creativecommons. org/ licenses/ by-sa/ 3. 0/

Potrebbero piacerti anche