[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home] [volume] [parte]


Capitolo 605.   C: «stdio.h»

Il file stdio.h della libreria standard è quello che fornisce le funzioni più importanti e in generale è il più complesso da realizzare, in quanto dipende strettamente dal meccanismo di gestione dei file del sistema operativo. L'elemento più delicato che viene definito qui è il tipo di dati FILE, da cui dipende quasi tutto il resto.

Alle complicazioni che esistevano già alla nascita del linguaggio, nei primi sistemi Unix, si aggiungono attualmente quelle relative alla distinzione tra file di testo e file binari, oltre che quelle relative alla gestione dei caratteri multibyte, per cui la lettura o la scrittura attraverso un flusso di dati deve tenere conto dello stato di completamento di tali informazioni.

Il file stdio.h definisce le funzioni principali per l'accesso ai file e una serie di funzioni per la lettura e scrittura di dati formattati (si vedano print(), scanf() e altre analoghe), ma altre funzioni realizzate espressamente per caratteri e stringhe estese (format e da elementi wchar_t) si trovano nel file wchar.h.

I file proposti che si basano sugli esempi del capitolo sono incompleti, in quanto manca la dichiarazione del tipo FILE e del tipo fpos_t.

605.1   Tipi

Il file stdio.h, oltre a size_t che fa già parte del file stddef.h, dichiara due tipi di dati a uso specifico per la gestione dei file: FILE e fpos_t, realizzati normalmente attraverso delle strutture.

Il tipo fpos_t serve a rappresentare tutte le informazioni necessarie a specificare univocamente le posizioni interne a un file, per gli scopi delle funzioni fgetpos() e fsetpos(). Il tipo FILE deve poter esprimere tutte le informazioni necessarie a controllare un flusso di file (ovvero le operazioni su un file aperto), in particolare le posizioni correnti, il puntatore alla memoria tampone (buffer), l'indicatore di errore e di fine file.

typedef struct { /* omissis */ } fpos_t;

typedef struct { /* omissis */ } FILE;

L'organizzazione effettiva delle strutture che costituiscono i tipi fpos_t e FILE dipende strettamente dal sistema operativo (nel contesto particolare della propria architettura); pertanto, per poterne approfondire le caratteristiche, occorre prima uno studio dettagliato delle funzionalità del sistema operativo stesso.

605.2   Macro-variabili varie

Il file stdio.h dichiara la macro-variabile NULL, come già avviene nel file stddef.h, assieme ad altre macro-variabili a uso delle funzioni dichiarate al proprio interno. Quelle più semplici sono descritte nella tabella 605.3. L'esempio proposto della dichiarazione di tali macro-variabili è molto approssimativo:

#define _IOFBF          0       // Input-output fully buffered.
#define _IOLBF          1       // Input-output line buffered.
#define _IONBF          2       // Input-output with no buffering.

#define EOF             (-1)

#define FOPEN_MAX       10
#define FILENAME_MAX    254
#define L_tmpnam        FILENAME_MAX

#define SEEK_SET        0       // Dall'inizio.
#define SEEK_CUR        1       // Dalla posizione corrente.
#define SEEK_END        2       // Dalla fine del file.

#define TMP_MAX         100000  // Si ipotizza di usare nomi da
                                // «TMP00000.tmp» a «TMP99999.tmp».

Tabella 605.3. Macro-variabili comuni per le funzioni di stdio.h.

Denominazione Significato mnemonico Descrizione
_IOFBF
input output fully buffered Indica simbolicamente la richiesta di utilizzo di una memoria tampone a blocchi.
_IOLBF
input output line buffered Indica simbolicamente la richiesta di utilizzo di una memoria tampone gestita a righe di testo.
_IONBF
input output with no buffering Indica simbolicamente la richiesta di non utilizzare alcuna memoria tampone.
BUFSIZE
buffer size Rappresenta la dimensione predefinita della memoria tampone.
EOF
end of file È un numero intero di tipo int, negativo, che rappresenta il raggiungimento della fine del file. È in pratica ciò che si ottiene leggendo oltre la fine del file.
FOPEN_MAX
file open max Il numero di file che un processo elaborativo può aprire simultaneamente, in base alle limitazioni poste dal sistema operativo.
FILENAME_MAX
La dimensione di un array di elementi char, tale da essere abbastanza grande da contenere il nome del file più lungo (incluse le eventuali sequenze multibyte) che il sistema consenta di gestire.
L_tmpnam
temporary name La dimensione di un array di elementi char, tale da essere abbastanza grande da contenere il nome di un file temporaneo generato dalla funzione tmpnam().
SEEK_CUR
seek current Indica di eseguire un posizionamento a partire dalla posizione corrente del file.
SEEK_END
Indica di eseguire un posizionamento a partire dalla fine di un file.
SEEK_SET
Indica di eseguire un posizionamento a partire dall'inizio di un file.
TMP_MAX
Rappresenta la quantità massima di nomi di file differenti che possono essere generati dalla funzione tmpnam().

605.3   Ipotesi di gestione del tipo «FILE»

Pur non essendo necessario che sia così, si può ipotizzare che per ogni file che possa essere aperto simultaneamente, sia disponibile un elemento di tipo FILE organizzato in un array. In tal caso, potrebbe essere dichiarato così, già nel file stdio.h, anche se il nome usato è puramente indicativo:

...
FILE _stream[FOPEN_MAX];
...

L'uso della macro-variabile FOPEN_MAX garantisce che siano predisposti esattamente tutti gli elementi necessari alla gestione simultanea del limite di file previsti.

605.4   Flussi standard

Lo standard del linguaggio C prescrive che i nomi dei flussi standard previsti siano delle macro-variabili, tali da espandersi in espressioni che rappresentino puntatori di tipo FILE *, diretti ai flussi standard rispettivi. Nel caso del compilatore GNU C i puntatori sono già definiti con lo stesso nome dei flussi e, nel file stdio.h vi si fa riferimento in qualità di variabili esterne (in quanto dichiarate nella libreria precompilata):

extern FILE *stdin;     // Si ipotizza che la libreria C definisca
extern FILE *stdout;    // già i puntatori ai flussi standard, usando
extern FILE *stderr;    // i nomi predefiniti.
                        //
#define stdin  stdin    // In questo caso, è facile definire le
#define stdout stdout   // macro che fanno riferimento ai flussi
#define stderr stderr   // standard.

Diversamente, nell'ipotesi in cui si gestisca un array di elementi FILE, si potrebbe supporre che i primi tre elementi siano usati per i flussi standard e in tal caso le dichiarazioni delle macro-variabili potrebbero essere fatte così:

...
#define stdin  (&_stream[0])
#define stdout (&_stream[1])
#define stderr (&_stream[2])
...

605.5   Funzioni per la rimozione e la ridenominazione dei file

Le funzioni remove() e rename() consentono, rispettivamente di eliminare o di rinominare un file. Il file in questione viene individuato da una stringa, il cui contenuto deve conformarsi alle caratteristiche del sistema operativo. Le due funzioni hanno in comune il fatto di restituire un valore intero (di tipo int), dove il valore zero rappresenta il completamento con successo dell'operazione, mentre un valore differente indica un fallimento.

int remove (const char *filename);
int rename (const char *old, const char *new);

La sintassi per l'uso della funzione remove() è evidente dal suo prototipo, in quanto si attende un solo argomento che è costituito dal nome del file da eliminare; nel caso della funzione rename(), invece, il primo argomento è il nome del file preesistente e il secondo è quello che si vuole attribuirgli.

È importante ribadire che il comportamento delle due funzioni dipende dal sistema operativo. Per esempio, la ridenominazione può provocare la cancellazione di un file preesistente con lo stesso nome che si vorrebbe attribuire a un altro, oppure potrebbe limitarsi a fallire. In un sistema Unix o simile, molto dipende dalla configurazione dei permessi.

605.6   Funzioni per la gestione dei file temporanei

Le funzioni tmpfile() e tmpnam() servono per facilitare la creazione di file temporanei. La prima crea automaticamente un file di cui non si conosce il nome e la collocazione, aprendolo in aggiornamento (modalità wb+); la seconda si limita a generare un nome che potrebbe essere usato per creare un file temporaneo:

FILE *tmpfile (void);
char *tmpnam (char *s);

L'uso della funzione tmpfile() è evidente, in quanto non richiede argomenti e restituisce il puntatore al file creato; la seconda richiede l'indicazione di un array di caratteri da poter modificare, restituendo comunque il puntatore all'inizio dello stesso array. In ogni caso va chiarito che il file creato con la funzione tmpfile, una volta chiuso, viene rimosso automaticamente.

Le due funzioni devono essere in grado di poter generare un numero di nomi differente pari almeno al valore rappresentato da TMP_MAX, rimanendo il fatto che non possano essere aperti più di FOPEN_MAX file e che non possono essere generati file con nomi già esistenti.

Se si utilizza la funzione tmpnam(), l'array di caratteri che costituisce il primo argomento (s), viene usato dalla funzione per scriverci il nome del file temporaneo, restituendone poi il puntatore; tale array deve avere una dimensione di almeno L_tmpnam elementi, come si vede nell'esempio seguente:

#include <stdio.h>
int main (void)
{
    char t[L_tmpnam];
    char *p;
    p = tmpnam (t);
    printf ("%s %s\n", t, p);
    return 0;
}

Se la funzione tmpnam() riceve come argomento il puntatore nullo, il nome del file temporaneo viene scritto in un'area di memoria statica che viene sovrascritta a ogni chiamata successiva della funzione stessa.

Entrambe le funzioni, se non possono eseguire il loro compito, restituiscono un puntatore nullo.

605.7   Funzioni per l'apertura e la chiusura dei flussi di file

Le funzioni fopen(), freopen() e fclose(), consentono di aprire e chiudere i file, gestendoli attraverso un puntatore al flusso di file loro associato (stream). Il puntatore in questione è di tipo FILE *.

FILE *fopen   (const char *restrict filename,
               const char *restrict io_mode);

FILE *freopen (const char *restrict filename,
               const char *restrict io_mode,
               FILE *restrict stream);

int   fclose  (FILE *stream);

Quando viene aperto un file, gli si associa una variabile strutturata (un'area di memoria suddivisa in più componenti scalari) di tipo FILE, contenente tutte le informazioni che servono a gestirne l'accesso. Questa variabile deve rimanere univoca e vi si accede normalmente attraverso un puntatore (FILE *). Dal momento che per il linguaggio C un file aperto è un flusso, la variabile strutturata che contiene le informazioni necessarie a gestirne l'accesso viene identificata come il flusso stesso, pertanto nei prototipi la variabile che contiene il puntatore FILE * viene denominata generalmente stream.

Dal momento che non è compito del programmatore dichiarare la variabile di tipo FILE, in pratica ci si riferisce al flusso di file sempre solo attraverso un puntatore a quella variabile. Pertanto, è più propriamente il puntatore a tale variabile che rappresenta il flusso di file.

L'apertura di un file, oltre che l'indicazione del nome del file, richiede di specificare la modalità, ovvero il tipo di accesso che si intende gestire. Sono previste le modalità elencate nella tabella 605.11.

Tabella 605.11. Modalità di accesso ai file.

Sigla Mnemonico Descrizione
r
read Accesso in sola lettura di un file di testo.
w
write Accesso a un file di testo in scrittura, che implica la creazione del file all'apertura, ovvero il suo troncamento a zero, se esiste già.
a
append Accesso a un file di testo in aggiunta, che implica la creazione del file all'apertura, ovvero la sua estensione se esiste già.
rb
wb
ab
binary Accesso in lettura, scrittura o aggiunta, ma di tipo binario.
r+
w+
a+
update Accesso a un file di testo in lettura, scrittura o aggiunta, assieme alla modalità di aggiornamento. In pratica, con la lettura è consentita anche la scrittura; con la scrittura e l'aggiunta è consentita anche la rilettura.
rb+|r+b
wb+|w+b
ab+|a+b
Accesso a un file binario in lettura, scrittura o aggiunta, assieme alla modalità di aggiornamento. In pratica, con la lettura è consentita anche la scrittura; con la scrittura e l'aggiunta è consentita anche la rilettura. Si può osservare che il segno + può essere messo indifferentemente in mezzo o alla fine.

La funzione fopen() apre il file indicato come primo argomento (una stringa), con la modalità specificata nel secondo (un'altra stringa), restituendo il puntatore al flusso che consente di accedervi (se l'operazione fallisce, la funzione restituisce il puntatore nullo). La modalità di accesso viene espressa attraverso le sigle elencate nella tabella 605.11.

La funzione freopen() consente di associare un file differente a un flusso già esistente, cambiando anche la modalità di accesso, cosa che viene fatta normalmente per ridirigere i flussi standard. I primi due argomenti della funzione sono gli stessi di fopen(), con l'aggiunta alla fine del puntatore al flusso che si vuole ridirigere. La funzione restituisce il puntatore al flusso ridiretto se l'operazione ha successo, altrimenti produce soltanto il puntatore nullo. Se nel primo argomento, al posto di indicare il nome del file, si mette un puntatore nullo, la chiamata della funzione serve solo per modificare la modalità di accesso a un file già aperto, senza ridirigerne il flusso. Va osservato che il cambiamento della modalità di accesso, in ogni caso, dipende dal sistema operativo e non è detto che si possano applicare tutte le combinazioni.

La funzione fclose() permette di chiudere il flusso indicato come argomento, restituendo un valore numerico pari a zero se l'operazione ha successo, oppure il valore corrispondente alla macro-variabile EOF in caso contrario. La chiusura di un flusso implica la scrittura di dati rimasti in sospeso (in una memoria tampone). Un flusso già chiuso non deve essere chiuso nuovamente.

605.8   Funzioni per la gestione della memoria tampone

Le funzioni setvbuf() e setbuf() consentono di attribuire una memoria tampone (buffer) a un certo flusso di dati (un file già aperto), mentre fflush() consente di richiedere espressamente lo scarico della memoria in modo che le operazioni sospese di scrittura siano portate a termine completamente.

int  setvbuf (FILE *restrict stream, char *restrict buffer,
              int buf_mode, size_t size);

void setbuf  (FILE *restrict stream, char *restrict buffer);

int  fflush  (FILE *stream);

La funzione setvbuf() permette di attribuire una memoria tampone a un file che è appena stato aperto e per il quale non è ancora stato eseguito alcun accesso. Il primo argomento della funzione è il puntatore al flusso relativo e il secondo è il puntatore all'inizio dell'array di caratteri da usare come memoria tampone. Se al posto del riferimento alla memoria tampone si indica un puntatore nullo, si intende che la funzione debba allocare automaticamente lo spazio necessario; se invece l'array viene fornito, è evidente che deve rimanere disponibile per tutto il tempo in cui il flusso rimane aperto.

Il terzo argomento atteso dalla funzione setvbuf() è un numero che esprime la modalità di funzionamento della memoria tampone. Questo numero viene fornito attraverso l'indicazione di una tra le macro-variabili _IOFBF, _IOLBF e _IONBF. Il quarto argomento indica la dimensione dell'array da usare come memoria tampone: se l'array viene fornito effettivamente, si tratta della dimensione che può essere utilizzata; altrimenti è la dimensione richiesta per l'allocazione automatica.

La funzione setvbuf() restituisce zero se l'operazione richiesta è eseguita con successo; diversamente restituisce un valore differente.

La funzione setbuf() è una semplificazione di setvbuf() che non restituisce alcun valore, dove al posto di indicare la modalità di gestione della memoria tampone, si intende implicitamente quella corrispondente alla macro-variabile _IOFBF (pertanto si tratta di una gestione completa della memoria tampone), mentre al posto di indicare la dimensione dell'array che costituisce la memoria tampone si intende il valore corrispondente alla macro-variabile BUFSIZ. In pratica, è come utilizzare la funzione setvbuf() così:

(void) setvbuf (stream, buffer, _IOFBF, BUFSIZ);

La funzione fflush si usa per i file aperti in scrittura, allo scopo di aggiornare i file se ci sono dati sospesi nella memoria tampone che devono ancora essere trasferiti effettivamente. La funzione si attende come argomento il puntatore al flusso per il quale eseguire questo aggiornamento, ma se si fornisce il puntatore nullo (la macro-variabile NULL), si ottiene l'aggiornamento di tutti i file aperti in scrittura. A parte questo, la funzione non altera lo stato del flusso.

La funzione fflush() restituisce zero se riesce a completare con successo il proprio compito, altrimenti restituisce il valore corrispondente a EOF e aggiorna la variabile individuata dall'espressione errno in modo da poter risalire al tipo di errore che si è presentato.

La funzione fflush() interviene solo nella memoria tampone gestita internamente al programma, ma bisogna tenere presente che il sistema operativo potrebbe gestire un'altra memoria del genere, per il cui scarico occorre eventualmente intervenire con funzioni specifiche del sistema stesso.

605.9   Funzioni per la composizione dell'output

Alcune funzioni del file stdio.h sono realizzate con lo scopo principale di comporre una stringa attraverso l'inserzione di componenti di vario genere, convertendo i dati in modo da poterli rappresentare in forma «tipografica», nel senso di sequenza di caratteri che hanno una rappresentazione grafica.

Queste funzioni hanno in comune una stringa contenente degli specificatori di conversione, caratterizzati dal fatto che iniziano con il simbolo di percentuale (%) e dalla presenza di un elenco indefinito di argomenti, il cui valore viene utilizzato in sostituzione degli specificatori di conversione. Il modo in cui si esprime uno specificatore di conversione può essere complesso, pertanto viene mostrato un modello sintattico che descrive la sua struttura:

%[simbolo][n_ampiezza][.n_precisione][hh|h|l|ll|j|z|t|L]tipo

La prima cosa da individuare in uno specificatore di conversione è il tipo di argomento che viene interpretato e, di conseguenza, il genere di rappresentazione che se ne vuole produrre. Il tipo viene espresso da una lettera alfabetica, alla fine dello specificatore di conversione. La tabella successiva riepiloga i tipi principali.

Tabella 605.13. Tipi di conversione principali.

Simbolo Tipo di argomento Conversione applicata
%...d
%...i
int
Numero intero con segno da rappresentare in base dieci.
%...u
unsigned int
Numero intero senza segno da rappresentare in base dieci.
%...o
unsigned int
Numero intero senza segno da rappresentare in ottale (senza lo zero iniziale che viene usato spesso per caratterizzare un tale tipo di rappresentazione).
%...x
%...X
unsigned int
Numero intero senza segno da rappresentare in esadecimale (senza il prefisso 0x o 0X che viene usato spesso per caratterizzare un tale tipo di rappresentazione).
%...c
int
Un carattere singolo, dopo la conversione in unsigned char.
%...s
char *
Una stringa.
%...f
double
Un numero a virgola mobile, da rappresentare in notazione decimale fissa:
[-]iii.dddddd
%...e
%...E
double
Un numero a virgola mobile, da rappresentare in notazione esponenziale:
[-]i.ddddddxx
[-]i.ddddddxx
%...g
%...G
double
Un numero a virgola mobile, rappresentato in notazione decimale fissa o in notazione esponenziale, a seconda di quale si presti meglio in base ai vincoli posti da altri componenti dello specificatore di conversione.
%p
void *
Un puntatore generico rappresentato in qualche modo in forma grafica.
%n
int *
Questo specificatore non esegue alcuna conversione e si limita a memorizzare un valore intero (di tipo int) nella variabile a cui punta l'argomento. Per la precisione, viene memorizzata la quantità di caratteri generati fino a quel punto dalla conversione.
%%
Questo specificatore si limita a produrre un carattere di percentuale (%) che altrimenti non sarebbe rappresentabile.

Nel modello sintattico che descrive lo specificatore di conversione, si vede che subito dopo il segno di percentuale può apparire un simbolo (flag). I simboli principali che possono essere utilizzati sono descritti nella tabella successiva.

Tabella 605.14. Alcuni simboli, o flag.

Simbolo Corrispondenza
%+...
%#+...
%+0ampiezza...
%#+0ampiezza...
Il segno «+» fa sì che i numeri con segno lo mostrino anche se è positivo. Può combinarsi con lo zero e il cancelletto.
%0ampiezza...
%+0ampiezza...
%#0ampiezza...
%#+0ampiezza...
Lo zero fa sì che siano inseriti degli zeri a sinistra per allineare a destra il valore, nell'ambito dell'ampiezza specificata. Può combinarsi con il segno «+» e il cancelletto.
%ampiezza...
ampiezza...

In mancanza di uno zero iniziale, in presenza dell'indicazione dell'ampiezza, il valore viene allineato a destra usando degli spazi. È possibile esprimere esplicitamente l'intenzione di usare gli spazi mettendo proprio uno spazio, ma in generale non è richiesto. Se si mette lo spazio letteralmente, questo non è poi compatibile con lo zero, mentre le combinazioni con gli altri simboli sono ammissibili.
%-ampiezza...
%-+ampiezza...
%#-ampiezza...
%#-+ampiezza...
Il segno meno, usato quando la conversione prevede l'uso di una quantità fissa di caratteri con un valore che appare di norma allineato a destra, fa sì che il risultato sia allineato a sinistra. Il segno meno si può combinare il segno «+» e il cancelletto.
%#...

Il cancelletto richiede una modalità di rappresentazione alternativa, ammesso che questa sia prevista per il tipo di conversione specificato. È compatibili con gli altri simboli, ammesso che il suo utilizzo serva effettivamente per ottenere una rappresentazione alternativa.

Subito prima della lettera che definisce il tipo di conversione, possono apparire una o due lettere che modificano la lunghezza del valore da interpretare (per lunghezza si intende qui la quantità di byte usati per rappresentarlo). Per esempio, %...Lf indica che la conversione riguarda un valore di tipo long double. Tra questi specificatori della lunghezza del dato in ingresso ce ne sono alcuni che indicano un rango inferiore a quello di int, come per esempio %...hhd che si riferisce a un numero intero della dimensione di un signed char; in questi casi occorre comunque considerare che nella trasmissione degli argomenti alle funzioni interviene sempre la promozione a intero, pertanto viene letto il dato della dimensione specificata, ma viene «consumato» il risultato ottenuto dalla promozione. La tabella successiva riepiloga i modificatori di lunghezza principali.

Tabella 605.15. Alcuni modificatori della lunghezza del dato in ingresso.

Simbolo Tipo Simbolo Tipo
%...hhd
%...hhi
signed char
%...hhu
%...hho
%...hhx|%...hhX
unsigned char
%...hd
%...hi
short int
%...hu
%...ho
%...hx|%...hX
unsigned short int
%...ld
%...li
long int
%...lu
%...lo
%...lx|%...lX
unsigned long int
%...lc
wint_t
%...ls
wchar_t *
%...lld
%...lli
long long int
%...llu
%...llo
%...llx|%...llX
unsigned long long int
%...jd
%...ji
intmax_t
%...ju
%...jo
%...jx|%...jX
uintmax_t
%...zd
%...zi
size_t
%...zu
%...zo
%...zx|%...zX
size_t
%...td
%...ti
ptrdiff_t
%...tu
%...to
%...tx|%...tX
ptrdiff_t
%...Le|%...LE
%...Lf|%...LF
%...Lg|%...LG
long double

I modificatori di lunghezza si possono utilizzare anche con il tipo %...n. In tal caso, si intende che il puntatore sia del tipo specificato dalla lunghezza. Per esempio, %tn richiede di memorizzare la quantità di byte composta fino a quel punto in una variabile di tipo ptrdiff_t, a cui si accede tramite il puntatore fornito.

Tra il simbolo (flag) e il modificatore di lunghezza può apparire un numero che rappresenta l'ampiezza da usare nella trasformazione ed eventualmente la precisione: ampiezza[.precisione]. Il concetto parte dalla rappresentazione dei valori in virgola mobile, dove l'ampiezza indica la quantità complessiva di caratteri da usare e la precisione indica quanti di quei caratteri usare per il punto decimale e le cifre successive, ma si applica anche alle stringhe.

In generale, per quanto riguarda la rappresentazione di valori numerici, la parte intera viene sempre espressa in modo completo, anche se l'ampiezza indicata è inferiore; ai numeri interi la precisione non si applica; per i numeri in virgola mobile con rappresentazione esponenziale, la precisione riguarda le cifre decimali che precedono l'esponente; per le stringhe la precisione specifica la quantità di caratteri da considerare, troncando il resto.

In un altro capitolo, la tabella 585.9 riporta un elenco di esempi di utilizzo della funzione printf() dove si può valutare l'effetto dell'indicazione dell'ampiezza e della precisione.

L'ampiezza, o la precisione, o entrambe, potrebbero essere indicate da un asterisco, come per esempio %*.*f. L'asterisco usato in questo modo indica che il valore corrispondente (ampiezza, precisione o entrambe) viene tratto dagli argomenti come intero (int). Pertanto, per tornare all'esempio composto come %*.*f, dagli argomenti viene prelevato un intero che rappresenta l'ampiezza, un altro intero che rappresenta la precisione, quindi si preleva un valore double che è quanto va rappresentato secondo l'ampiezza e la precisione richieste.

605.9.1   Funzioni che ricevono gli argomenti direttamente

Un gruppo di funzioni per la composizione dell'output riceve direttamente gli argomenti variabili che servono agli specificatori di conversione:

int sprintf  (char *restrict s, const char *restrict format, ...);
int snprintf (char *restrict s, size_t n, const char *restrict format, ...);
int fprintf  (FILE *restrict stream, const char *restrict format, ...);
int printf   (const char *restrict format, ...);

Tutte le funzioni di questo gruppo hanno in comune la stringa di composizione, costituita dal parametro format, e gli argomenti successivi che sono in quantità e qualità indeterminata, in quanto per la loro interpretazione contano gli specificatori di conversione inseriti nella stringa di composizione. Inoltre, tutte queste funzioni restituiscono la quantità di caratteri prodotti dall'elaborazione della stringa di composizione. Va osservato che il conteggio riguarda solo i caratteri e non include, eventualmente, il carattere nullo di terminazione di stringa che viene usato per le funzioni sprintf() e snprintf(). Se durante il procedimento di composizione si verifica un errore, queste funzioni possono restituire un valore negativo.

La funzione sprintf() produce il risultato della composizione memorizzandolo a partire dal puntatore indicato come primo parametro (s) e aggiungendo il carattere nullo di terminazione. La funzione snprintf(), invece, produce al massimo n-1 caratteri, aggiungendo sempre il carattere nullo di terminazione.

La funzione fprintf() scrive il risultato della composizione attraverso il flusso di file stream, mentre printf() lo scrive attraverso lo standard output.

605.9.2   Funzioni che ricevono gli argomenti da un'altra funzione

A fianco delle funzioni descritte nella sezione precedente, un gruppo analogo svolge le stesse operazioni, ma ricevendo gli argomenti variabili per riferimento. In pratica si tratta di ciò che serve quando gli argomenti variabili sono stati ottenuti da un'altra funzione e non da una chiamata diretta.

int vsprintf  (char *restrict s, const char *restrict format, va_list arg);
int vsnprintf (char *restrict s, size_t n, const char *restrict format, va_list arg);
int vfprintf  (FILE *restrict stream, const char *restrict format, va_list arg);
int vprintf   (const char *restrict format, va_list arg);

Il funzionamento è conforme a quello delle funzioni che non hanno la lettera v iniziale; per esempio, vsprintf() si comporta conformemente a sprintf(). Per comprendere la differenza si potrebbe dimostrare la realizzazione ipotetica della funzione printf() avvalendosi di vprintf():

int printf   (const char *restrict format, ...)
{
    va_list arg;
    va_start (arg, format);
    int count;
    count = vprintf (format, arg);
    va_end (arg);
    return count;
}

605.10   Funzioni per l'interpretazione dell'input

Un piccolo gruppo di funzioni del file stdio.h è specializzato nell'interpretazione di una stringa, dalla quale si vanno a estrapolare dei componenti da collocare in variabili di tipo opportuno. In altri termini, da una stringa che rappresenta un valore espresso attraverso caratteri grafici, si vuole estrarre il valore e assegnare a una certa variabile.

Il meccanismo è opposto a quello usato dalle funzioni del tipo ...printf() e anche in questo caso si parte da una stringa contenente principalmente degli specificatori di conversione, seguita da un numero indefinito di argomenti. Gli specificatori delle funzioni che interpretano l'input sono simili a quelli usati per la composizione dell'output, ma non possono essere equivalenti in tutto. Sinteticamente si possono descrivere così:

%[*][n_ampiezza][hh|h|l|ll|j|z|t|L]tipo

Come si può vedere, all'inizio può apparire un asterisco, il cui scopo è quello di annullare l'assegnamento del valore a una variabile. In pratica, con l'asterisco il dato corrispondente allo specificatore viene interpretato, ma poi non viene salvato.

Successivamente può apparire un numero che rappresenta l'ampiezza del dato da interpretare, in byte, il cui scopo è quello di limitare la lettura fino a un certo carattere (inteso come char, pertanto le sequenze multibyte contano per più di una unità singola).

Dopo può apparire una sigla, composta da una o più lettere, il cui scopo è quello di modificare la dimensione predefinita della variabile di destinazione. In altri termini, senza questo modificatore si intende che la variabile ricevente debba essere di una certa grandezza, ma con l'aggiunta del «modificatore di lunghezza» si precisa invece qualcosa di diverso. In pratica, il modificatore di lunghezza usato da queste funzioni è equivalente a quello delle funzioni di composizione dell'output.

Al termine dello specificatore di conversione appare una lettera che dichiara come deve essere interpretato il dato in ingresso e, in mancanza del modificatore di lunghezza, indica anche la dimensione della variabile ricevente.

Tabella 605.19. Tipi di conversione principali.

Simbolo Tipo di argomento Conversione applicata
%...d
int *
Numero intero con segno rappresentato in base dieci.
%...i
int *
Numero intero con segno rappresentare in base dieci o in base otto, avendo come prefisso uno zero, oppure in base sedici, avendo come prefisso 0x o 0X.
%...u
unsigned int *
Numero intero senza segno rappresentato in base dieci.
%...o
unsigned int *
Numero intero senza segno rappresentato in ottale (con o senza lo zero iniziale).
%...x
unsigned int *
Numero intero senza segno rappresentato in esadecimale (con o senza il prefisso 0x o 0X).
%...c
char *
Interpreta un solo carattere, o più caratteri se si specifica l'ampiezza. Nella lettura contano anche gli spazi o qualunque altro carattere e non viene aggiunto il carattere nullo di terminazione.
%...s
char *
Interpreta una sequenza di caratteri che non siano spazi, aggiungendo alla fine il carattere nullo di terminazione.
%...a
%...e
%...f
%...g
double *
Un numero a virgola mobile rappresentato in notazione decimale fissa o in notazione esponenziale:
[-]iii.dddddd
[-]i.ddddddxx
[-]i.ddddddxx
%p
void *
Interpreta il valore di un puntatore che sia rappresentato nello stesso modo in cui farebbe la funzione printf("%p", puntatore).
%n
int *
Questo specificatore non esegue alcuna conversione e si limita a memorizzare la quantità di caratteri (char) letti fino a quel punto.
%...[...]
char *
Interpreta una stringa non vuota contenente solo i caratteri elencati tra parentesi quadre, aggiungendo alla fine il carattere nullo di terminazione. Se tra i caratteri si cerca anche la parentesi quadra chiusa, questa va messa all'inizio dell'elenco: %...[]...].
%...[^...]
char *
Interpreta una stringa non vuota contenente solo caratteri diversi da quelli elencati tra parentesi quadre, aggiungendo alla fine il carattere nullo di terminazione. Se tra i caratteri da escludere si vuole indicare anche la parentesi quadra chiusa, questa va messa all'inizio dell'elenco: %...[^]...].
%%
Interpreta un carattere di percentuale tra i dati in ingresso, ma senza memorizzare alcunché.

Tabella 605.20. Alcuni modificatori della lunghezza del dato in uscita.

Simbolo Tipo Simbolo Tipo
%...hhd
%...hhi
signed char *
%...hhu
%...hho
%...hhx
%...hhn
unsigned char *
%...hd
%...hi
short int *
%...hu
%...ho
%...hx
%...hn
unsigned short int *
%...ld
%...li
long int *
%...lu
%...lo
%...lx
%...ln
unsigned long int *
%...lc
%...ls
%...lc
%...l[...]
wchar_t *
%...lld
%...lli
long long int *
%...llu
%...llo
%...llx
%...lln
unsigned long long int *
%...jd
%...ji
intmax_t *
%...ju
%...jo
%...jx
%...jn
uintmax_t *
%...zd
%...zi
size_t *
%...zu
%...zo
%...zx
%...zn
size_t *
%...td
%...ti
ptrdiff_t *
%...tu
%...to
%...tx
%...tn
ptrdiff_t *
%...Le
%...Lf
%...Lg
long double *

A proposito dell'interpretazione di caratteri e di stringhe, va precisato cosa accade quando si usa il modificatore l (elle). Se nello specificatore di conversione appare un valore numerico che esprime un'ampiezza, questa indica una quantità di caratteri, in ingresso, da intendersi come byte. Utilizzando gli specificatori %...lc e %...ls, la quantità di questi caratteri continua a riferirsi a byte, ma si interpretano le sequenze multibyte in ingresso per generare caratteri di tipo wchar_t.

Il documento che descrive lo standard del linguaggio afferma che la stringa di conversione è composta da direttive, ognuna delle quali è formata da: uno o più spazi (spazi veri e propri o caratteri di tabulazione orizzontale); un carattere multibyte (diverso da % e diverso di caratteri che rappresentano spazi, oppure uno specificatore di conversione.

[spazi]carattere_multibyte|%...

Dalla sequenza multibyte che costituisce i dati in ingresso da interpretare, vengono eliminati automaticamente gli spazi iniziali e finali (tutto ciò che si può considerare spazio, anche il codice di interruzione di riga), quando all'inizio o alla fine non ci sono corrispondenze con specificatori di conversione che possono interpretarli.

Quando la direttiva di interpretazione inizia con uno o più spazi orizzontali, significa che si vogliono ignorare gli spazi a partire dalla posizione corrente nella lettura dei dati in ingresso; inoltre, la presenza di un carattere che non fa parte di uno specificatore di conversione indica che quello stesso carattere deve essere incontrato nell'interpretazione dei dati in ingresso, altrimenti il procedimento di lettura e valutazione si deve interrompere. Se due specificatori di conversione appaiono adiacenti, i dati in ingresso corrispondenti possono essere separati da spazi orizzontali o da spazi verticali (il codice di interruzione di riga).

Purtroppo, la sintassi per la scrittura delle stringhe di conversione non è molto soddisfacente e diventa difficile spiegarne il comportamento, a meno di rimanere fermi su esempi molto semplici.

605.10.1   Funzioni che ricevono gli argomenti direttamente

Un gruppo di funzioni l'interpretazione dell'input riceve direttamente gli argomenti variabili che servono agli specificatori di conversione:

int fscanf (FILE *restrict stream, const char *restrict format, ...);
int sscanf (const char *restrict s, const char *restrict format, ...);
int scanf  (const char *restrict format, ...);

Tutte le funzioni di questo gruppo hanno in comune la stringa di conversione, costituita dal parametro format, e gli argomenti successivi che sono puntatori di tipo indeterminato, in quanto per la loro interpretazione contano gli specificatori di conversione inseriti nella stringa. Inoltre, tutte queste funzioni restituiscono la quantità valori assegnati alle variabili rispettive, oppure il valore corrispondente alla macro-variabile EOF nel caso si verifichi un errore prima di qualunque conversione.

La funzione sscanf() scandisce il contenuto della stringa indicata come primo parametro (s); la funzione fscanf() scandisce l'input proveniente dal flusso di file indicato come primo argomento (stream), mentre la funzione scanf() scandisce direttamente lo standard input.

605.10.2   Funzioni che ricevono gli argomenti da un'altra funzione

A fianco delle funzioni descritte nella sezione precedente, un gruppo analogo svolge le stesse operazioni, ma ricevendo gli argomenti variabili per riferimento. In pratica si tratta di ciò che serve quando gli argomenti variabili sono stati ottenuti da un'altra funzione e non da una chiamata diretta.

int vfscanf (FILE *restrict stream, const char *restrict format, va_list arg);
int vsscanf (const char *restrict s, const char *restrict format, va_list arg);
int vscanf  (const char *restrict format, va_list arg);

Il funzionamento è conforme a quello delle funzioni che non hanno la lettera v iniziale; per esempio, vscanf() si comporta conformemente a scanf(). Per comprendere la differenza si potrebbe dimostrare la realizzazione ipotetica della funzione scanf() avvalendosi di vscanf():

int scanf (const char *restrict format, ...);
{
    va_list arg;
    va_start (arg, format);
    int count;
    count = vscanf (format, arg);
    va_end (arg);
    return count;
}

605.11   Funzioni per la lettura e la scrittura di un carattere alla volta

Le funzioni fgetc() e getc() leggono un carattere (char) attraverso il flusso di file indicato come argomento:

int fgetc (FILE *stream);

#define getc(STREAM) (fgetc (STREAM))

Lo standard prescrive che la funzione getc() sia in realtà una macro-istruzione, così come si ipotizza nella dichiarazione appena mostrata. A questo proposito occorre tenere presente che, se si usa getc(), l'espressione usata per individuare il flusso di file potrebbe essere valutata più di una volta.

Il carattere letto da fgetc() viene interpretato senza segno e trasformato in un intero (pertanto deve risultare essere di segno positivo). Se viene tentata la lettura oltre la fine del file, la funzione restituisce il valore rappresentato da EOF e memorizza questa condizione nella variabile strutturata che rappresenta il flusso di file. Se invece si verifica un errore di lettura, viene impostato il contenuto dell'indicatore di errore relativo al flusso di file e la funzione restituisce sempre il valore EOF.

Secondo lo standard, la funzione getchar() è equivalente a getc (stdin), senza specificare altro. Ciò può significare ragionevolmente che se getc() è una macro-istruzione, anche getchar() dovrebbe esserlo, altrimenti potrebbe trattarsi di una funzione vera e propria:

#define getchar (getc (stdin))

La funzione ungetc() ha lo scopo di annullare l'effetto della lettura dell'ultimo carattere, ma il modo in cui viene gestita la cosa rende la questione molto delicata:

int ungetc (int c, FILE *stream);

Semplificando il problema, la funzione ungetc() rimanda indietro il carattere c nel flusso di file stream dal quale è appena stata eseguita una lettura. Tuttavia, non è garantito che il carattere in questione sia effettivamente quello che è stato letto per ultimo, ma la fase successiva di lettura deve fornire per primo tale carattere.

Si comprende intuitivamente che, se si eseguono operazioni di spostamento della posizione corrente relativa al flusso di file in questione, il carattere rimandato indietro con la funzione ungetc() debba essere dimenticato, soprattutto se questo non corrispondeva a quello che effettivamente era stato letto per ultimo in quel momento.

L'uso della funzione ungetc() implica un aggiornamento della posizione corrente relativa al flusso di file, ma questa modifica, in presenza di file di testo che non siano realizzati secondo lo standard tradizionale dei sistemi Unix, implica che l'entità di questa modifica non possa essere predeterminabile.(1)

La funzione ungetc() può fallire nel suo intento e lo standard prescrive che sia «garantita» la possibilità di rimandare indietro almeno un carattere. Se la funzione riesce a eseguire l'operazione, restituisce il valore positivo corrispondente al carattere rinviato; altrimenti restituisce il valore della macro-variabile EOF.

Le funzioni fputc(), putc() e putchar() eseguono l'operazione inversa, rispettivamente, di fgetc, getc() e getchar(); anche in questo caso vale il fatto che putc() possa essere realizzata come macro-istruzione:

int fputc (int c, FILE *stream);

#define putc(CHAR, STREAM) (fputc ((CHAR), (STREAM)))

#define putchar(CHAR) (putc ((CHAR), stdout)))

La funzione fputc() scrive un carattere (fornito come numero intero positivo) attraverso il flusso di file indicato; putc() fa lo stesso, ma potrebbe essere una macro-istruzione; putchar() scrive attraverso lo standard output.

Se la scrittura fallisce, le funzioni (o le macro-istruzioni) restituiscono il valore EOF; diversamente restituiscono il valore positivo corrispondente al carattere scritto.

Per come sono state proposte queste funzioni, non c'è differenza nell'uso di getc() al posto di fgetc(), così come tra putc() e fputc(). Evidentemente, se la propria libreria può esprimere le macro-istruzioni getc() e putc() richiamando funzioni del sistema operativo (funzioni che dovrebbero essere richiamate anche da fgetc() e fputc()), si può risparmiare un livello di chiamate per accelerare leggermente l'esecuzione del programma.

605.12   Funzioni per l'input e l'output di file di testo

Le funzioni fgets() e fputs() sono utili per l'accesso a file di testo, quando si vuole indicare il flusso di file:

char *fgets (char *restrict s, int n, FILE *restrict stream);

int   fputs (const char *restrict s, FILE *restrict stream);

La funzione fgets() legge al massimo n-1 caratteri (nel senso di elementi char) attraverso il flusso di file stream, copiandoli in memoria a partire dall'indirizzo s e aggiungendo alla fine il carattere nullo di terminazione delle stringhe. La lettura si esaurisce prima di n-1 caratteri se viene incontrato il codice di interruzione di riga, il quale viene rappresentato nella stringa a cui punta s, ovvero se si raggiunge la fine del file. In ogni caso, la stringa s viene terminata correttamente con il carattere nullo.

La funzione fgets() restituisce la stringa s se la lettura avviene con successo, ovvero se ha prodotto almeno un carattere; altrimenti, il contenuto dell'array a cui punta s non viene modificato e la funzione restituisce il puntatore nullo. Se si creano errori imprevisti, la funzione potrebbe restituire il puntatore nullo, ma senza garantire che l'array s sia rimasto intatto.

La funzione fputs() serve a copiare la stringa a cui punta s nel file rappresentato dal flusso di file stream. La copia della stringa avviene escludendo però il carattere nullo di terminazione. Va osservato che questa funzione, pur essendo contrapposta evidentemente a fgets(), non conclude la riga del file, ovvero, non aggiunge il codice di interruzione di riga. Per ottenere la conclusione della riga di un file di testo, occorre inserire nella stringa, espressamente, il carattere \n.

La funzione fputs() restituisce il valore rappresentato da EOF se l'operazione di scrittura produce un errore; altrimenti restituisce un valore positivo qualunque.

Le funzioni gets() e puts() sono utili per l'accesso a file di testo, quando si vogliono utilizzare i flussi standard. In linea di massima, assomigliano a fgets() e fputs(), ma il funzionamento non è perfettamente conforme a quelle:

char *gets (char *s);

int   puts (const char *s);

Il funzionamento di gets() è perfettamente conforme a quello di fgets(), con la sola differenza che il flusso di file da cui si leggono i caratteri è lo standard input. Nel caso di puts(), a parte il fatto che si usa lo standard output per la scrittura, occorre sottolineare che alla fine della stringa viene accodata la scrittura del codice di interruzione di riga.

605.13   Funzioni per l'input e output diretto

Le funzioni fread() e fwrite() consentono di leggere e scrivere attraverso un flusso di file aperto, il quale deve essere specificato espressamente tra gli argomenti. Lo standard prescrive che queste funzioni si avvalgano rispettivamente di fgetc() e di fputc().

size_t fread  (void *restrict ptr,
               size_t size,
               size_t nmemb,
               FILE *restrict stream);

size_t fwrite (const void *restrict ptr,
               size_t size,
               size_t nmemb,
               FILE *restrict stream);

Le due funzioni (fread() e fwrite()) hanno praticamente gli stessi argomenti, usati in modo analogo. La lettura e la scrittura avviene a blocchi da size byte, ripetuta per nmemb volte, attraverso il flusso di file specificato come stream. La lettura implica la memorizzazione dei caratteri in forma di elementi unsigned char, a partire dall'indirizzo indicato dal puntatore ptr; la scrittura copia nello stesso modo i caratteri a partire dal puntatore ptr, verso il flusso di file.

L'aggiornamento della posizione corrente interna al file a cui si riferisce il flusso avviene esattamente come per le funzioni fgetc() e fputc().

Il valore restituito dalle funzioni fread() e fwrite() rappresenta la quantità di blocchi, ovvero la quantità di elementi nmemb che sono stati copiati con successo. Pertanto, se si ottiene un valore inferiore a nmemb, significa che l'operazione è stata interrotta a causa di un errore.

605.14   Funzioni per il posizionamento

Sono previste diverse funzioni per modificare la posizione corrente dei flussi di file. Le funzioni più semplici, per iniziare sono fseek(), ftell() e rewind():

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

long int ftell  (FILE *stream);

void     rewind (FILE *stream);

In generale, la funzione fseek() sposta la posizione corrente relativa al flusso di file stream, nella nuova posizione determinata dai parametri whence e offset. Il parametro whence viene fornito attraverso una macro-variabile che può essere SEEK_SET, SEEK_CUR o SEEK_END, indicando rispettivamente l'inizio del file, la posizione corrente o la fine del file. Dalla posizione indicata dal parametro whence viene aggiunta, algebricamente, la quantità di byte indicata dal parametro offset.

Quanto descritto a proposito del posizionamento con la funzione fseek() riguarda i file che vengono gestiti in modo binario, perché con i file di testo è opportuno avere maggiore accortezza: il valore del parametro offset deve essere zero, oppure quanto restituito in precedenza dalla funzione ftell() per lo stesso flusso di file, ma in tal caso, ovviamente, il parametro whence deve corrispondere a SEEK_SET.

La funzione fseek() restituisce zero se può eseguire l'operazione, altrimenti dà un risultato diverso.

La funzione ftell() restituisce la posizione corrente del flusso di file indicato come argomento. Questo valore può essere usato con fseek(), al posto dello scostamento (il parametro offset), indicando come posizione di riferimento l'inizio del file, ovvero SEEK_SET. Se la funzione ftell() non riesce a fornire la posizione, restituisce il valore -1 (tradotto in long int) e annota il fatto nella variabile errno.

La funzione rewind() si limita a riposizionare il flusso di file all'inizio. In pratica è come utilizzare la funzione fseek() specificando uno scostamento pari a zero a partire da SEEK_SET, ignorando il valore restituito:

(void) fseek (stream, 0L, SEEK_SET)

Va osservato che il riposizionamento di un flusso di file implica l'azzeramento dell'indicatore di fine file, se questo risulta impostato, e la cancellazione dei caratteri che eventualmente fossero stati rimandati indietro con la funzione ungetc().

Le funzioni fseek() e ftell sono utili particolarmente per i file binari ed eventualmente i file di testo con una rappresentazione dei caratteri tradizionale. Ma quando il file di testo contiene anche caratteri espressi attraverso sequenze multibyte, il posizionamento al suo interno dovrebbe tenere anche conto del progresso nell'interpretazione di queste sequenze. Pertanto, esistono altre due funzioni per leggere la posizione e ripristinarla in un secondo momento:

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

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

Entrambe le funzioni che appaiono nei due prototipi restituiscono zero se l'operazione è stata compiuta con successo, altrimenti restituiscono un valore differente. Nel caso particolare di fsetpos(), nel caso si verifichi un errore, questo viene annotato nella variabile errno.

Le due funzioni richiedono come primo argomento il flusso di file a cui ci si riferisce; come secondo argomento richiedono il puntatore a una variabile di tipo fpos_t. La funzione fgetpos() memorizza nella variabile a cui punta il parametro pos le informazioni sulla posizione corrente del file, assieme allo stato di interpretazione relativo alle sequenze multibyte; la funzione fsetpos(), per converso, utilizza la variabile a cui punta pos per ripristinare la posizione memorizzata, assieme allo stato di avanzamento dell'interpretazione di una sequenza multibyte.

605.15   Gestione degli errori

Un gruppo di funzioni di stdio.h consente di verificare ed eventualmente azzerare lo stato degli indicatori di errore riferiti a un certo flusso di file:

void clearerr (FILE *stream);
int  feof     (FILE *stream);
int  ferror   (FILE *stream);
void perror   (const char *s);

La funzione clearerr() azzera gli indicatori di errore e di fine file per il flusso di file indicato come argomento, senza restituire alcunché.

La funzione feof() controlla lo stato dell'indicatore di fine file per il flusso di file indicato. Se questo non è attivo restituisce zero, altrimenti restituisce un valore diverso da zero.

La funzione ferror() controlla lo stato dell'indicatore di errore per il flusso di file indicato. Se questo non è attivo restituisce zero, altrimenti restituisce un valore diverso da zero.

La funzione perror() prende in considerazione la variabile errno e cerca di tradurla in un messaggio testuale da emettere attraverso lo standard error (con tanto di terminazione della riga, in modo da riposizionare a capo il cursore). Se il parametro s corrisponde a una stringa non vuota, il testo di questa viene posto anteriormente al messaggio, separandolo con due punti e uno spazio (). Il contenuto del messaggio è lo stesso che si otterrebbe con la funzione strerror(), fornendo come argomento la variabile errno.

605.16   Realizzazione di «vsnprintf()» e altre collegate

Le funzioni per la composizione dell'output che possono essere realizzate senza avere definito la gestione dei file, sono quelle che si limitano a produrre una stringa. La funzione che va realizzata per prima è vsnprintf(), in quanto snprintf() si può limitare a richiamarla. Naturalmente, anche vsprintf() e sprintf() possono avvalersi della stessa vsnprintf(), ponendo un limite massimo abbastanza grande alla stringa da generare. Nella sezione 614.12 è disponibile un esempio di realizzazione parziale di vsnprintf(). L'esempio seguente mostra come si ottiene snprintf(), una volta che è disponibile vsnprintf():

#include <stdio.h>
int
snprintf (char *restrict string, size_t n, const char *restrict format, ...)
{
    va_list ap;
    va_start (ap, format);
    return vsnprintf (string, n, format, ap);
}

Eventualmente, per realizzare le funzioni vsprintf() e sprintf(), secondo le limitazioni già descritte, sono sufficienti due macro-istruzioni:

#define vsprintf(s, format, arg) (vsnprintf (s, SIZE_MAX, format, arg))
#define sprintf(s, ...)          (snprintf (s, SIZE_MAX, __VA_ARGS__))

605.17   Riferimenti


1) L'arretramento di un carattere nella posizione corrente di un file di testo non è detto corrisponda alla sottrazione di una unità, perché bisogna tenere in considerazione il modo in cui un file di testo è strutturato nel proprio sistema operativo.


Appunti di informatica libera 2008 --- Copyright © 2000-2008 Daniele Giacomini -- <appunti2 (ad) gmail·com>


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome c_171_stdio_h_187.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home]

Valid ISO-HTML!

CSS validator!

Gjlg Metamotore e Web Directory