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


Capitolo 580.   C: tipi di dati speciali, di uso comune

Il linguaggio C prevede un insieme di tipi di dati tradizionali, a cui ci si riferisce con maggiore frequenza, e vari altri tipi, alcuni dei quali è bene conoscere.

580.1   Tipo «_Bool»

Lo standard C prevede un tipo particolare per la rappresentazione di valori logici, ovvero solo per i valori zero e uno. Nella tradizione del linguaggio, questo tipo manca e di norma si è rimediato rimpiazzandolo semplicemente con un valore intero, dal tipo char in su. Dal momento che è frequente l'uso di un tipo personalizzato (o di una macro-variabile del precompilatore) denominato bool, lo standard ha inserito il tipo logico con il nome _Bool, allo scopo di evitare conflitti con il codice esistente.

Il tipo _Bool può contenere solo i valori zero e uno; pertanto, la conversione di un numero di tipo diverso in un tipo _Bool avviene traducendo qualunque valore diverso da zero con il numero uno (Vero), mentre lo zero mantiene il suo valore invariato (Falso).

Lo standard non stabilisce come deve essere rappresentato in memoria il tipo _Bool, anche se si tratta molto probabilmente di un byte intero che viene a essere sacrificato per lo scopo. Data la particolarità di questo tipo, non è detto che si possa utilizzare un puntatore per raggiungere l'area di memoria corrispondente.

Comprendendo il motivo per il quale questo tipo ha ricevuto un nome così particolare, diventa evidente che se lo si vuole utilizzare convenga creare una macro-variabile o un tipo derivato. D'altra parte, lo stesso file stdbool.h prescrive la definizione della macro-variabile bool.

In conclusione, se si desidera utilizzare un tipo di dati booleano, conviene fare riferimento alla macro-variabile bool, la quale potrebbe anche essere ridefinita localmente nel proprio programma, se quello che si vuole non è conforme alle previsioni dello standard o delle librerie del proprio compilatore.

580.2   Tipo «void»

Il tipo void rappresenta un'eccezione tra i tipi di dati usati nel linguaggio, in quanto rappresenta formalmente un valore inesistente. Ma il suo utilizzo va visto caso per caso. La situazione più frequente di utilizzo del tipo void riguarda le funzioni, quando non devono restituire alcun valore: in tal caso si dichiara che sono di tipo void.

void procedura (int x)
{
    ...
    return;
}

L'esempio mostra una funzione che, non dovendo restituire alcun valore, viene dichiarata di tipo void. Come si vede, l'istruzione return va usata, in questo caso, senza l'indicazione di un valore.

Quando una funzione non richiede parametri, si deve indicare esplicitamente questo fatto con la parola chiave void:

int funzione (void)
{
    ...
}

In questo esempio, la funzione restituisce un valore intero, ma non fa uso di alcun parametro.

Il cast di tipo void può servire per annullare il risultato di un'espressione, quando ciò che interessa della stessa sono solo i suoi «effetti collaterali». In altri termini, quando un'espressione esegue qualche tipo di operazione, ma complessivamente si vuole scartare il risultato che viene generato, si può usare un cast di tipo void. Per esempio, quando si vuole usare una funzione, la quale restituirebbe un valore, del quale non si vuole fare alcun uso, si può indicare nella chiamata un cast al tipo void, anche se di norma ciò non è necessario:

...
    (void) mia_funzione (...);
...

È possibile definire un puntatore generico al tipo void, sapendo che questo è convertibile in tutti gli altri tipi di puntatore, con un cast appropriato e che è sempre possibile fare anche l'inverso:

    ...
    void *p;
    ...
    p = (void *) &a;
    ...

Il puntatore nullo può essere definito, sia come un valore intero pari a zero, sia come tale valore tradotto in un puntatore di tipo void *:

    ...
    int NULL = 0;
    ...
    ...
    void *NULL = (void *) 0;
    ...

Si osservi che un puntatore di tipo void * non può essere incrementato o decrementato, perché idealmente fa riferimento a un'unità di memoria di dimensione nulla. Pertanto, per usare un puntatore del genere, quando si vuole scandire la memoria, prima va convertito nel formato più appropriato.

580.3   Tipo «size_t»

Secondo lo standard il tipo size_t è definito nel file stddef.h, ma in pratica, dal momento che viene usato dall'operatore sizeof, potrebbe essere incorporato direttamente nel compilatore, tra i tipi fondamentali. A ogni modo si tratta normalmente di un tipo equivalente a un unsigned long int, destinato però a contenere la dimensione di qualcosa, intesa come intervallo tra due indirizzi (tra due puntatori), ma espressa come valore assoluto.

#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
    double a = 1.1;
    double b = 2.2;
    char * A = (char *) &a;
    char * B = (char *) &b;
    size_t s = abs (A - B);
    printf ("distanza: %d\n", s);
    return 0;
}

L'esempio mostra la dichiarazione di due variabili e di due puntatori alle variabili. Tuttavia, i puntatori sono di tipo char *, in modo che la sottrazione tra i due dia la distanza in byte. Volendo, per non fare riferimento a un tipo particolare di puntatore, si potrebbe usare il tipo void, ottenendo lo stesso risultato:

#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
    double a = 1.1;
    double b = 2.2;
    void * A = (void *) &a;
    void * B = (void *) &b;
    size_t s = abs (A - B);
    printf ("distanza: %d\n", s);
    return 0;
}

Va osservato che il risultato mostrato dall'esecuzione dell'esempio compilato, dipende dal compilatore. In pratica, è il compilatore che decide come collocare in memoria le variabili; se si presume che siano adiacenti, si dovrebbe ottenere una distanza di otto byte.

580.4   Tipo «ptrdiff_t»

Per rappresentare la differenza tra due indirizzi, tenendo conto del segno, si usa il tipo ptrdiff_t, definito anch'esso nel file stddef.h. Molto probabilmente si tratta di un tipo equivalente a un long int. Viene ripreso l'esempio già mostrato, senza calcolare il valore assoluto della differenza tra indirizzi:

#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
    double a = 1.1;
    double b = 2.2;
    void * A = (void *) &a;
    void * B = (void *) &b;
    ptrdiff_t s = (A - B);
    printf ("differenza: %d\n", s);
    return 0;
}

580.5   Tipo «va_list»

Il tipo va_list è definito dallo standard nella libreria stdarg.h, allo scopo di agevolare la scansione degli argomenti variabili, passati alle funzioni. Lo standard è vago sul significato che deve avere il tipo va_list, ma in pratica dovrebbe trattarsi di un puntatore al tipo char.(1) Tuttavia il suo utilizzo rimane relegato alla scansione degli argomenti variabili, come descritto nella sezione 578.3. Viene comunque riportata qui la copia di un esempio che ne mostra l'uso.

#include <stdio.h>
#include <stdarg.h>

void f (int w,...)
{
    long double x;      // Dichiara le variabili che servono
    long long int y;    // a contenere gli argomenti per i
    int z;              // quali mancano i parametri formali.

    va_list ap;         // Dichiara il puntatore agli argomenti.

    va_start (ap, w);   // Posiziona il puntatore dopo la fine di «w».

    x = va_arg (ap, long double);       // Estrae l'argomento successivo
                                        // portando avanti il puntatore
                                        // di conseguenza.

    printf ("w = %d; ", w);     // Mostra il valore del primo parametro.
    printf ("x = %Lf; ", x);    // Mostra il valore dell'argomento successivo.

    y = va_arg (ap, long long int);     // Estrapola e mostra
    printf ("y = %lld; ", y);           // il terzo argomento.

    z = va_arg (ap, int);               // Estrapola e mostra
    printf ("z = %d\n", z);             // il quarto argomento.

    va_end (ap);                        // Conclude la scansione.

    return;
}

int main (int argc, char *argv[])
{
    f (10, (long double)12.34, (long long int)13, 14);
    return 0;
}

580.6   Tipo «wchar_t»

Per rappresentare un carattere esteso, ovvero un carattere dell'insieme universale, non è sufficiente il tipo char e per questo esiste invece il tipo wchar_t (wide character type), definito nel file stddef.h.

Il tipo wchar_t è un intero, usato generalmente senza segno, di rango sufficiente a rappresentare tutti i caratteri che si intende di poter ammettere. È da osservare che per rappresentare l'insieme completo dei caratteri già definiti sono necessari anche più di 32 bit.

Il tipo wchar_t si usa sostanzialmente come il tipo char, anche per ciò che riguarda gli array e le stringhe (che per essere tali devono essere terminate con il carattere nullo), ma si tratta sempre di una gestione interna, perché la rappresentazione richiede invece una trasformazione nella forma prevista dalla configurazione locale (capitolo 581).

580.7   Tipo «wint_t»

Molte delle funzioni standard che in qualche modo hanno a che fare con un carattere singolo (perché ne ricevono il valore come argomento o perché restituiscono il valore di un carattere), lo fanno trattando il carattere come un tipo int, ovvero, trattando il carattere senza segno e promuovendolo al rango di un intero normale. Questo sistema permette di distinguere tra tutti i caratteri dell'insieme ridotto e un valore ulteriore, rappresentato dalla macro-variabile EOF, usata per rappresentare un errore in base al contesto.

Nella gestione dei caratteri estesi ci sono funzioni analoghe che svolgono lo stesso tipo di adattamento, ma in tal caso il valore del carattere viene gestito in qualità di wint_t, il quale può rappresentare tutti i caratteri che sono ammessi dal tipo wchar_t, con l'aggiunta del valore corrispondente a WEOF (diverso da tutti gli altri).

Il tipo wint_t e la macro-variabile WEOF sono definiti nel file wchar.h. Il tipo wint_t è, evidentemente, un intero di rango tale da consentire la rappresentazione di tutti i valori necessari.

580.8   Tipo «time_t»

Diverse funzioni dichiarate nel file time.h fanno riferimento al tipo time_t che rappresenta la quantità di unità di tempo trascorsa a partire da un'epoca di riferimento.

Frequentemente si tratta di un valore numerico intero che rappresenta la quantità di secondi trascorsi dall'epoca di riferimento (nei sistemi Unix è di norma l'ora zero del 1 gennaio 1970); inoltre, in un elaboratore che gestisca correttamente i fusi orari, è normale che questo valore sia riferito al tempo universale coordinato.

580.9   Tipo «struct tm»

La libreria standard, nel file time.h, 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:

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.
};

580.10   Tipo «FILE»

Il tipo FILE rappresenta una variabile strutturata con tutte le informazioni necessarie a individuare un flusso di file aperto. Di norma vengono usati puntatori di tipo FILE * per tutte le operazioni di accesso relative a flussi di file aperti, tanto che nel gergo comune si confondono le cose e tali puntatori sono chiamati generalmente stream.

580.11   Tipo «fpos_t»

Alcune funzioni individuano la posizione di accesso ai file attraverso un insieme di dati. In quei casi, per rappresentare tale insieme di dati si usano variabili strutturate di tipo fpos_t.


1) È improbabile che sia utilizzato un tipo void *, perché non sarebbe possibile scandire la memoria, salvo convertirlo ogni volta in un formato char *.


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_tipi_di_dati_speciali_di_uso_comune.htm

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

Valid ISO-HTML!

CSS validator!

Gjlg Metamotore e Web Directory