Home ] Su ] Novità ] Hardware ] Software ] Windows ] Sicurezza ] Linguaggi ] Cerca ] Contatta ] Guestbook ] Mappa ] Info su... ]

Le FAQ sul linguaggio C


La versione originale di questa FAQ è sempre disponibile all'indirizzo http://www.programmazione.it.
Nel suddetto indirizzo potete trovare materiale più aggiornato rispetto al contenuto della FAQ sul linguaggio C di questo sito.
Prima di continuare a leggere le seguenti sezioni della FAQ sul linguaggio C, vi invitiamo a prendere visione del disclaimer, dei copyright e delle note di revisione, situate in questa pagina.


FAQ del gruppo it.comp.lang.c

a cura di Marco Coppo - versione 1.00

 

Sommario
  1. Introduzione
  2. Per cominciare
  3. Come faccio a:
  4. Altre domande

 

1.1 – Il gruppo di discussione it.comp.lang.c

È un gruppo non moderato (cioè non c’è nessuno che filtra i vostri messaggi prima che essi raggiungano il newsgroup), in cui si tratta del linguaggio di programmazione C, nei termini indicati dal manifesto:

/* it.comp.lang.c Il linguaggio di programmazione C */

Il gruppo tratterà del linguaggio C, ideato da Brian W. Kernighan e Dennis M. Ritchie.

Il gruppo tratterà di qualsiasi dialetto del linguaggio C, in modo particolare del vecchio K&R e di quello specificato dall'istituto americano ANSI come standard "ANSI X3.159-1989" e ratificato dall'istituto internazionale ISO come standard "ISO/IEC 9899:1990".

Argomenti compresi:

  • Il linguaggio in generale;
  • Gli algoritmi;
  • Le ottimizzazioni;
  • La portabilità;
  • I sistemi di sviluppo.

Il gruppo non si intende moderato né collegato a mailing-list bidirezionali

ATTENZIONE: Questo manifesto non è più considerato al passo coi tempi per diverse ragioni, tra le quali il nuovo standard e la nascita di gruppi di discussione specifici per vari tipi di piattaforma. E’ in corso la discussione sul suo aggiornamento, nel frattempo, pur rimanendo questo il manifesto ufficiale, si prega di non postare su questo gruppo domande riguardanti caratteristiche di uno specifico ambiente, sebbene implementabili in C, quando sia presente un newsgroup apposito.

Il documento che state leggendo contiene le FAQ (Frequently Asked Questions – domande frequentemente poste) ufficiali del newsgroup it.comp.lang.c.
Se avete bisogno di risposte riguardanti gli argomenti che sono contemplati nel manifesto del gruppo, ma che non vengono trattate in questo documento, potete spedirle al newsgroup. Quando mandate messaggi siete tenuti a seguire tutte le regole del "galateo" informatico sulla rete, di cui trovate descrizione nella risposta 1.2.

Torna all'indice

1.2 – Il galateo in rete : la Netiquette

Il newsgroup è un servizio che ci viene offerto e sta a noi fare in modo di non degradarne la qualità. Se il servizio è efficiente, tutti (e in primo luogo noi stessi) ne traiamo benefici.
Per questo è stata definita la “Netiquette”, ovvero una serie di regole di comportamento da tenere durante l’uso di servizi di rete, nel presente caso dei newsgroup. La versione completa della Netiquette è reperibile in forma completa all'indirizzo:

http://www.fau.edu/rinaldi/netiquette.html e tradotte in italiano all'indirizzo http://www.inferentia.it/netiquette.

Prima di cominciare a postare messaggi sul newsgroup è bene conoscere almeno alcune regole basilari della Netiquette, riportate qui di seguito in forma ridotta e adattata.

LA NETIQUETTE

È responsabilità degli utenti il rispettare i regolamenti e le procedure delle altre reti/sistemi. Ricordate, il fatto che un utente possa compiere una determinata azione non implica necessariamente che debba compierla.
L'uso della rete è un privilegio, non un diritto, che potrebbe essere revocato temporaneamente o permanentemente in qualunque momento vi rendiate protagonisti di una condotta scorretta.

  1. Secondo l'ordinamento degli Stati Uniti, non è legale "usare qualsiasi macchina telefax, computer, o altri dispositivi per inviare pubblicità non richiesta" a qualunque "dispositivo dotato della capacità di trascrivere su carta testo o immagini (o entrambi) da un segnale elettronico ricevuto su una normale linea telefonica".
    Su Internet, questo tipo di attività è chiamata "spamming" e viene ritenuto tale il posting sul newsgroup it.comp.lang.c di messaggi pubblicitari o non riconducibili a nessuno degli argomenti elencati nel manifesto.
    Anche in Italia è possibile segnalare le violazione della netiquette secondo le definizioni della RFC 1855 e degli allegati che chiunque registri un dominio .it è tenuto a controfirmare.
    Per informazioni su come difendervi dallo spamming potete rivolgervi al gruppo it.news.net-abuse
  2. Non assumete mai che i vostri messaggi siano strettamente privati o che non possano essere letti da altri che dal destinatario indicato. Infatti una volta ricevuto un messaggio questo diventa di proprietà del destinatario che può in teoria usarlo a suo piacimento. Non inviate mai nulla che non vorreste sentire ripetuto nei notiziari serali.
  3. Non inviate lo stesso messaggio a più gruppi contemporaneamente (crossposting, spawn).
  4. È vietato in questo newsgroup l'invio di file binari (tutto ciò che non è testo: foto, programmi compilati, etc...) in quanto è dedicato esclusivamente alla conversazione.
  5. Scrivete paragrafi e messaggi corti ed essenziali... la sinteticità è la virtù dei colti.
  6. Quando citate (quote) un'altra persona, togliete tutto quanto non è direttamente applicabile alla vostra risposta. Non lasciate che il vostro newsreader includa automaticamente l'intero corpo del messaggio a cui state rispondendo. Riducete al minimo le citazioni, lo stretto necessario per dare un contesto alla vostra risposta.
    Nessuno ama leggere lunghi messaggi riportati in citazione per la terza o quarta volta, semplicemente per poi trovare una risposta di una riga: "Anch'io."
  7. Settate il rientro automatico dei messaggi al massimo a circa 70 caratteri per evitare problemi di visualizzazione che li rendono molto fastidiosi da leggere e ne compromettono in parte la comprensione
  8. È anche consigliabile sostituire le lettere accentate con l'uso di apostrofi, infatti non tutti i newsreader visualizzano i caratteri accentati allo stesso modo. è ==> e'
  9. Per gli utenti di Outlook Express: per evitare il dannoso spezzarsi in più rami di un thread e la fastidiosa visione di subject deturpati da una infinità di "R:", si invitano cortesemente i possessori del suddetto newsreader a porre rimedio scaricando la patch appositamente creata e disponibile ai seguente indirizzi:
  10. Servitevi di un linguaggio che sia il meno aggressivo e maleducato possibile, ed evitate di offendere il responsabile di un errore o chi ha semplicemente espresso una opinione personale che non condividete.
    Non scendete allo stesso livello di chi nei suoi messaggi, a torto o a ragione, inizia a insultare e cercate di non cadere nelle trappole dei provocatori.
  11. Non postate messaggi riservati ad una sola persona (esistono le mail). Non postate messaggi di prova (esiste it.test).
    Non postate messaggi pubblicitari (esiste it.annunci.commerciali).
  12. Focalizzate bene l'argomento del messaggio e includete sempre un testo pertinente nel campo soggetto (subject) del messaggio, in modo che gli altri utenti possano localizzarlo velocemente.
    I messaggi contenenti subject generici come "AIUTO" vengono spesso tralasciati da persone che non hanno tempo da perdere
  13. Se volete potete includere la vostra firma in fondo ai messaggi. La firma dovrebbe contenere le vostre informazioni personali che ritenete opportuno rendere pubbliche ( indirizzi e-mail, link web personali, saluti, ecc... ).
    Non dovrebbe comunque superare le quattro righe di lunghezza.
  14. Scrivete con le lettere maiuscole solo per sottolineare un punto importante o per distinguere un titolo o un sottotitolo dal resto del testo. Scrivere in maiuscole intere parole che non sono titoli viene generalmente definito URLARE!
  15. *Asterischi* intorno ad una parola posso aiutare a evidenziare un termine.
  16. Non spedite mai catene di Sant'Antonio (chain letters) su Internet. Lo spedirlo è ritenuto spamming potrebbe causarvi la perdita dell'accesso.
  17. Identificate tutte le citazioni, i riferimenti e le fonti delle informazioni che divulgate e rispettate i copyright e gli eventuali accordi per la divulgazione di qualsiasi informazione.
  18. E' considerato "estrema maleducazione" l'inoltrare comunicazioni personali e/o private a mailing list o su Usenet senza l'esplicita autorizzazione dell'autore.
  19. Fate attenzione al sarcasmo e all'umorismo. Senza la comunicazione facciale e l'intonazione della voce, la vostra battuta di spirito potrebbe essere intesa come una critica. Quando volete esprimere qualche particolare inflessione, usate le emoticons. Per esempio: :-) = faccia che sorride (implica una battuta)
  20. Gli acronimi possono essere usati per abbreviare quando possibile; tuttavia messaggi pieni di acronimi possono confondere il lettore. Esempi:
    • RTFF = Read The Fucking FAQ
    • IMHO = In My Humble/Honest Opinion (a mio modesto parere)
    • FYI = For Your Information (per vostra informazione)
    • BTW = By The Way (per inciso...)
    • Flame = critica (spesso cattiva)
Torna all'indice

1.3 – Breve storia del linguaggio C

Il C nasce all’inizio degli anni settanta come linguaggio per la programmazione del sistema operativo Unix, anch’esso agli albori, nei Bell Laboratories, ad opera di Dennis M. Ritchie.
E’ il risultato dell’evoluzione del linguaggio B, creato da Thompson negli stessi Bell Laboratories, essenzialmente derivato dal linguaggio BCPL sviluppato da Martin Richards nella metà degli anni sessanta e adattato ad un PDP–7.
Essi erano entrambi linguaggi typeless, resi inadeguati dall’avvento del PDP-11 il quale introduceva un’architettura della memoria basata su bytes invece che su celle.
Nel 1971 Ritchie aggiunge il tipo carattere al linguaggio B e ne riscrive il compilatore per il PDP-11, chiama questa prima versione NB ovvero New B.
Il passo successivo è importante e riguarda una diversa e più efficiente implementazione dei puntatori che giustifica il cambio di nome e dà vita al C nel 1972. L’aggiunta del preprocessore per includere files e dichiarare costanti e macros, seppur senza argomenti, oltre ad altre migliorie rendono il linguaggio potente a sufficienza da permettere la riscrittura del kernel di Unix per il PDP-11.
Successivamente viene sviluppata la prima libreria, contenente le funzioni per l’I/O, quella che poi diverrà stdio.h.
Nel periodo tra il 1973 e il 1980 il linguaggio cresce, guadagnando strutture, long, unsigned, union e tipi enumerativi. Nel 1978 viene pubblicato il “white book” ovvero “The C programming language” ad opera di B.Kernighan e D.Ritchie, la prima pubblicazione completa descrivente il linguaggio.
L’attenzione degli sviluppatori si sposta quindi sulla portabilità e sia il C che Unix vengono portati su più piattaforme, rendendo il linguaggio ormai così diffuso da richiedere una standardizzazione.
Nel 1983 l’ANSI crea il comitato X3J11 con lo scopo di produrre un C standard. Sei anni dopo lo standard ANSI C è definito, ed accettato dall’ISO come ISO/IEC 9899-1990.

Per saperne di più consultate: http://cm.bell-labs.com/cm/cs/who/dmr/chist.html (in inglese)

Torna all'indice

2.1 - Che compilatore mi serve per iniziare e dove lo posso trovare gratis?

Il compilatore è specifico per ogni piattaforma, quindi se usi:

DOS/WIN puoi scegliere tra:

  • - DJGPP : E’ il porting per DOS dell’ottimo compilatore C/C++ GNU (Gnu is Not Unix) della Free Software Foundation
    www.delorie.com/djgpp/
  • - Turbo C : E’ possibile scaricare la versione 2 dalla sezione Museum della Inprise (ex Borland)
    www.borland.com (Richiede registrazione)

WIN32 puoi scegliere tra:

UNIX/LINUX : normalmente è già installato e comunque fa parte del pacchetto del sistema operativo. Linux usa gcc della GNU, altri sistemi possono usare cc, per saperne di più guarda nelle pagine man: (es; man gcc o man cc).

Torna all'indice

2.2 - Quale libro mi conviene utilizzare per imparare il C?

Il libro ufficiale del newsgroup è quello del creatore del linguaggio C (Ritchie) :

  • Brian Kernighan, Dennis Ritchie “Il linguaggio C” - seconda edizione

Altri testi consigliati da frequentatori del newsgroup:

  • Leendert Ammeraal "Linguaggio C: teoria e applicazioni" - Tecniche nuove
    "L’ ho trovato molto interessante" – Marco Porcelli
  • Al Kelley - Ira Pohl "C : Didattica e programmazione" - Addison Wesley
    "Un bel libro" – Marco Iannaccone
  • P.Aitken, B.L.Jones "Programmare in C" - Ed. Apogeo
    Antonino Sabetta

Tutorials online (in italiano) scaricabili gratuitamente, segnalati dai frequentatori del newsgroup:

  • Introduzione alla programmazione in C. – consultabile online o scaricabile dal sito FTP (in formato txt)
    "Tutorial molto adatto per chi è alle prime armi, molto chiaro" – Stefano Ferrari
  • Tricky C – consultabile online, da cui è anche possibile scaricare l’intero documento in formato html
Torna all'indice

3.1 - Come faccio a cancellare il contenuto dello schermo?

Il C standard non possiede nessuna funzione che faccia ciò. Bisogna perciò ricorrere alle estensioni di ogni specifico compilatore. Ecco alcune soluzioni:

sotto DOS: (Utilizzando i compilatori Turbo C/C++ della Borland o il DJGPP) Si usa la funzione:

  • void clrscr(void) definita in conio.h. Esempio : clrscr();

sotto UNIX/LINUX : (Utilizzando le librerie ncurses) Si usa la funzione:

  • int clear (void) definita in curses.h. Esempio : clear ();

Per un esempio più completo vedere la risposta alla domanda 3.2.

In alternativa si fa una chiamata al sistema operativo con la funzione system() definita in stdlib.h:

  • sotto DOS : system("cls");
  • sotto UNIX/LINUX : system("clear");
Torna all'indice

3.2 - Come faccio a posizionare il cursore in un punto preciso del video?

Anche in questo caso lo standard C non prevede una funzione adatta. Ancora una volta bisogna rivolgersi alle estensioni dello specifico compilatore:

sotto DOS: (Utilizzando i compilatori Turbo C/C++ della Borland o il DJGPP)

  • void gotoxy(int riga, int colonna) definita in conio.h. Esempio : gotoxy(5,8);

sotto UNIX/LINUX : (Utilizzando le librerie ncurses) Si usa la funzione:

  • int move (int riga, int colonna) definita in curses.h. Esempio : move(5,8);

Segue un esempio più completo che dimostra l’uso di alcune funzioni delle librerie ncurses:

#include <curses.h>
#include <unistd.h>
int main(void)
{
    int i;
    initscr();        /* inizializza le curses, vedi man ncurses */
    clear();          /* cancella lo schermo */
    move(12,37);      /* muove il cursore */
    addstr("test");   /* scrive 'test' */
    refresh();        /* esegue il refresh dello schermo */
    sleep(1);         /* aspetta un secondo */
    for (i = 0; i < 20; i++)
        {
        move(i,i);
        addstr(".");
        refresh();
        sleep(1);
        }
    endwin();         /* usa endwin() prima di uscire dal programma */
    return 0;
}

E’ anche possibile posizionare il cursore e scrivere un carattere sul video in un "colpo solo", utilizzando la funzione:

  • int mvaddch(int riga, int colonna, char carattere); nell’esempio sopra: mvaddch(i, i, '.');

N.B.: Ricordatevi di linkare la libreria perché non viene fatto automaticamente! (consultare man curses per dettagli).

Torna all'indice

3.3 - Come faccio a convertire una stringa in una variabile numerica?

Si usano le funzioni della libreria stdlib.h, precisamente:

  • int atoi(const char *s) converte una stringa in un intero. Es.: intnum = atoi(stringa1);
  • double atof(const char *s) converte una stringa in un float. Es.: floatnum = atof(stringa2);
  • long int atol(const char *s) converte una stringa in un long. Es.: longnum = atol(stringa3);

Queste funzioni sono però piuttosto spartane, nel senso che interrompono la lettura della stringa al primo errore incontrato senza nessuna segnalazione. Nella stessa libreria ci sono funzioni più sofisticate quali:

  • double strtod(const char * nptr, char ** endptr);
  • long int strtol(const char * nptr, char ** endptr, int base);
  • unsigned long int strtoul(const char * nptr, char ** endptr, int base);

Esse ritornano un puntatore all’elemento dell’array in cui la funzione ha interrotto l’acquisizione dati, utile per controllarne il contenuto e capire l’errore. Altro vantaggio di queste ultime è che possono interpretare la stringa secondo le convenzioni del C, per esempio 10, 0xA e 012 vengono tradotte nello stesso numero. Esempio:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main(void)
{
    long valore;
    char **corr, *el[] = { "2147483647", "4294967296", NULL };
    for (corr = el; *corr; corr++)
        {
        errno = 0;
        valore = strtol(*corr, NULL, 0);
        printf("Stringa: %s -> valore acquisito: %ld ", *corr, valore);
        printf("Errore: %s\n", (errno) ? "Overflow" : "Nessuno");
        }
    return 0;
}

Se invece lo scopo è acquisire dati numerici diversi contenuti nella stessa stringa in un formato predefinito si può utilizzare:

int sscanf(const char *s, char *format, ...) definita in stdio.h. Esempio:

char stringa[DIM]="1.55566 3 96.82"; /* un float un intero un float separati da spazio */
sscanf(stringa, “%f%d%f”, &primofloat, &intero, &secondofloat);
Torna all'indice

3.4 - Come faccio a convertire una variabile numerica in una stringa?

Si utilizza la funzione :

  • int sprintf(char *s, const char * format, ...) definita in stdio.h. Esempio:
    sprintf(stringa1,”%d”,numerointero);  /* NB stringa1 è array di char, numerointero è int */
    sprintf(stringa2,”%f”,numdec);        /* NB stringa2 è array di char, numdec è float o double */
    

Il metodo sopra descritto è standard, e quindi preferibile perché portabile su qualunque architettura, ma non è l’unico.

Esistono estensioni specifiche dei vari compilatori che permettono di ottenere lo stesso risultato. Per facilitare la ricerca nell’help del proprio compilatore si segnalano, a titolo d’esempio, le funzioni itoa(), ultoa() per l’ambiente DOS/Windows.

Torna all'indice

3.5 - Come faccio a generare un numero (pseudo) casuale?

Il C dispone della funzione:

int rand(void) definita in stdlib.h

per la generazione di numeri pseudocasuali nell’intervallo compreso tra 0 e RAND_MAX (costante definita in stdlib.h).

Essi sono valori ottenuti per via algoritmica, per cui in realtà casuali non lo sono affatto, difatti se non si inizializza l’algoritmo con un opportuno “seme” la sequenza di valori risulta essere sempre la stessa. La funzione utilizzata allo scopo è:

void srand(int seme) definita in stdlib.h

Perché sia efficace, il seme deve poter variare. Si usa perciò chiamarla con il valore restituito dalla funzione:

time_t time(time_t *timer) definita in time.h

Essa ritorna il numero di secondi trascorsi dal 1° gennaio 1970 ed è perciò differente ogni volta che la si chiama. Vediamo ad esempio come generare un intero compreso tra 0 e 9:

#include <stdlib.h>
#include <time.h>
int main(void)
    {
    int numerocasuale ;
    srand(time(NULL)) ;
    numerocasuale = (int)(10.0*rand()/(RAND_MAX+1.0));
    ……
    }

Note:
Il cast a intero permette di eliminare la parte decimale.
Si potrebbe anche utilizzare direttamente l’operatore modulo ( Es numerocasuale=rand()%10 ) ma si perderebbe gran parte della “casualità”. Utilizzando questa tecnica con modulo 2, infatti, si ottiene una sequenza quasi perfetta di 0 e 1 alternati! La funzione rand() è comunque considerata un generatore molto primitivo di numeri casuali, per algoritmi migliori si consiglia:
http://131.113.34.4/matumoto/emt.html

Torna all'indice

3.6 - Compilare un progetto costituito da più sorgenti

Quando il codice sorgente comincia a diventare lungo, perde in leggibilità, è difficile da mantenere , la compilazione richiede parecchio tempo e qualche volta potrebbe anche fallire a causa di memoria insufficiente. E’ giunto il momento di suddividere il progetto in più sorgenti e avvalersi dei benefici della compilazione separata. Ma come? Tutto sta in 3 fasi:

  1. Suddivisione del codice in più file (sorgenti e headers)
  2. Compilazione di ogni singolo file sorgente
  3. Fase di linking dei moduli oggetto
  1. - Il codice sorgente va suddiviso in files contenenti ognuno un gruppo omogeneo di definizioni di funzioni (per omogeneo si intende che è meglio raggruppare le funzioni che svolgono compiti simili in un unico file – esempio un set di funzioni per eseguire operazioni tra matrici). Uno di questi conterrà la funzione main().
    All’inizio di ogni file devono essere inclusi i file header corrispondenti alle dichiarazioni delle funzioni utilizzate all’interno del file.
    - Un file header serve a garantire la coerenza delle dichiarazioni in tutti i moduli dell'applicazione. Quindi ci si mettono, appunto, dichiarazioni di funzioni e strutture dati ma mai definizioni, cioè niente che allochi, implicitamente o esplicitamente, memoria.
  2. Con l’aggiunta dei necessari file header, ogni file può essere compilato indipendentemente dagli altri. A differenza di quanto accade durante la “compilazione” di un progetto a sorgente unico, qui non viene invocato il linker e quindi il “prodotto dell’operazione è un file “oggetto” che ha normalmente estensioni .o o .obj.
    Se a causa di debug o aggiornamento, è necessario modificare la definizione di una funzione, non è necessario ricompilare tutti i sorgenti, ma solo quello oggetto di modifiche e poi invocare nuovamente il linker.
  3. Ultima fase, quella di linking. Viene spesso automatizzata attraverso strumenti quali make e varianti specifiche di alcuni compilatori. Nel caso di make, esso legge le informazioni che sono contenute nel makefile, il quale contiene le “regole” con le quali compilare e linkare i vari moduli. Make controlla le date dei file di sorgente e oggetto e se sono diverse provvede a ricompilare quello specifico file e a collegare tra loro i vari moduli.
Torna all'indice

3.7 - Svuotare il buffer di tastiera

Durante l’inserimento di caratteri da tastiera, questi vengono depositati in un apposito buffer, fino a quando non viene premuto il tasto <Invio>. A questo punto le funzioni atte all’acquisizione dati da tastiera, (che in C viene vista come un qualunque file chiamato stdin – standard input -) come scanf(), leggono il buffer e inseriscono i dati letti, nella/e variabili definite dall’utente, fintanto che trovano nel buffer dati consistenti con il formato specificato. Il resto dei dati viene lasciato nel buffer, cosicché alla prossima chiamata di scanf() essi saranno i primi ad essere letti. Questo può essere buono perché evita di perdere dati ma, se non si considera tale meccanismo, si rischia di perdere ore a cercare di capire come mai il nostro programma non funziona come dovrebbe.
Un metodo per svuotare il buffer potrebbe essere quello di leggerlo fino a quando non si incontra il carattere ‘\n’, ma se l’input è bufferizzato a più livelli potrebbe non funzionare. E’ comunque il metodo più portabile:

while ( getchar() != '\n' );

L’uso della funzione standard fflush() applicata al file stdin è invece da evitare in quanto l’ISO C non definisce un comportamento preciso per questa funzione quando applicata ai file di input.
Alcuni compilatori potrebbero implementare fflush() in modo che abbia lo stesso comportamento su tutti i file (input e output), ma l’uso di questa caratteristica fa perdere portabilità al programma.

Torna all'indice

3.8 - Dichiarare un vettore con numero di elementi arbitrario

L’obbligo di dichiarare a “compile-time” la dimensione di un array o di una matrice può essere restrittivo (quando non si conosce con certezza la dimensione massima) e inefficiente (perché obbliga a sovradimensionare la struttura dati e quindi ad allocare più memoria di quanta non ne serva in realtà).

Fortunatamente il C fornisce le funzioni:

void *malloc(size_t size) e void free(void *ptr) definite nella libreria stdlib.h

Che, rispettivamente, compiono queste operazioni:

  • malloc() alloca una certa quantità di memoria definibile a “run-time” e ritorna un puntatore alla prima “unità di memoria” allocata. NB si noti che “unità di memoria” ha la dimensione del tipo di dato che deve contenere.
  • free () dealloca la memoria precedentemente allocata con malloc(), liberandola quindi per altri usi.

Esempio : (allocazione di un array di n elementi di tipo T dove T può essere char, int, float, double…)

T *p;
p = malloc( n * sizeof( T ) ); /* Ritorna p che può essere usato per accedere all’array */
p[ i ] = v;                    /* Per assegnare l'elemento i-esimo */
var = p[ i ];                  /* Per leggere l'elemento i-esimo. NB: v e var devono essere tipo T */

Se il tipo T è un tipo complesso, ad esempio una struct, si deve anche accedere al membro che interessa assegnare o dal quale interessa leggere:

p[ i ].membro = v;    /* Per assegnare l'elemento i-esimo */
var = p[ i ].membro;  /* Per leggere l'elemento i-esimo. NB v e var devono essere di tipo opportuno */

In alternativa è possibile utilizzare l'operatore di dereferenza. Ad esempio, ecco due metodi per scorrere tutto l'array:

  • Con indicizzazione
    int i;
    
    for ( i = 0; i < n; i++ )
        p[ i ] = i;
    /* Tanto per assegnare qualcosa ;-) */
    
  • Con operatore di dereferenza
    int *t = p;
    
    p[ n - 1 ] = '\0';    /* Valore di guardia */
    while ( *p )
        *p++ = i;
    

Nei due casi sopra l’array è stato allocato per contenere interi (T è un int, p = malloc( sizeof (int )) )

Quando l'array p non serve bisogna deallocarlo con: free( p );

Esempio: (allocazione dinamica di una matrice di dimensioni dim1xdim2)

char **mat = NULL;
int i = 0;

/*    ALLOCAZIONE     */

if ( !(mat = malloc( dim1 * sizeof(char *) )) )
   fprintf( stderr,"Errore allocazione matrice\n" );
else
   for ( i = 0; i < dim1; i++ )
       if ( !( mat[i] = malloc( dim2 ) ) )
          fprintf( stderr,"Errore allocazione matrice\n" );

/*    DEALLOCAZIONE     */

for ( i = 0; i < dim1; i++ )
    free( mat[i] );

free( mat );

Dove dim1 e dim2 sono le variabili contenenti le dimensioni della matrice, quindi settabili a runtime.

ATTENZIONE: malloc() opera direttamente sulla memoria, il suo uso è pertanto allo stesso tempo potente e pericoloso. Ecco alcuni errori comuni da evitare:

  1. Mai perdere il riferimento al puntatore per il quale è stata allocata la memoria. Es.:
    T *p;
    p = malloc( n * sizeof( T ) );
    p = pippo;   /* Errore! Non è più possibile deallocare p! */
    
  2. Ricordarsi di deallocare sempre la memoria allocata, quando non è più necessaria! Pensate a una funzione che gestisca matrici di notevole dimensione, in cui vi siate dimenticati di liberare la memoria al termine dell’operazione. Chiamate questa funzione un po’ di volte… e la memoria libera finirà -> sistema bloccato!
Torna all'indice

3.9 - Acquisire da file una stringa intera, terminata da \n

Un problema comune che si presenta durante l’acquisizione di dati da un file di testo è quello di caricare da esso un’ intera stringa contenente spazi. L’uso di fscanf() a questo scopo è sconsigliato, a meno che non si conosca esattamente il formato delle righe del file di input, perché considera lo spazio nella stringa come un delimitatore di formato. Perciò, avendo un file contenente la seguente stringa:

questa è una stringa

tentando di leggerlo in questa maniera:

char stringa[40];
fscanf(nomefile,”%s”,stringa);
printf(“%s”,stringa);

otterremo in output solamente:

questa

Inoltre fscanf() non controlla (di default) la dimensione dell’input, per cui se la prima parola fosse (nell’esempio sopra) di più di 40 caratteri (in tedesco potrebbe succedere !-) la fscanf() scriverebbe tranquillamente i caratteri in eccesso su aree di memoria non libere, causando cose poco piacevoli! Per ovviare a questo inconveniente bisogna inserire nella stringa di formato lo specificatore di ampiezza massima del campo, così:

fscanf(nomefile,”%39s”,stringa); /*  NB il 40° carattere è il ‘\0’  */

La risposta a questi problemi sta nell’uso della funzione:

char *fgets(char *s, int n,FILE *fp); definita in stdio.h. Esempio: fgets(stringa,40,filepoint);

Essa acquisisce dal file puntato da fp al massimo n-1 caratteri (altrimenti si ferma quando trova il carattere newline ‘\n’) che scrive nella stringa s, a cui aggiunge il carattere ‘\0’. Se l’operazione ha avuto successo, essa ritorna il puntatore alla stringa, altrimenti un puntatore nullo. Da notare che anche il carattere newline ‘\n’ viene incluso. Per rimuoverlo si può fare:

char *c;
…
  if ((c = strchr(stringa, '\n'))) *c = '\0';

Per riassumere, la funzione fgets() è più sicura e permette l’input da files privi di formato omogeneo; mentre fscanf() è da preferire quando il file ha formato fisso e magari contenente diversi tipi di dato, che possono, con essa, essere automaticamente caricati nelle opportune variabili.

Torna all'indice

4.1 - E’ possibile ottenere il sorgente dal codice macchina ?

Tutte le istruzioni macchina devono passare dalla CPU per essere eseguite, e questo comporta che debbano essere caricate in RAM. Qui esse sono accessibili e quindi possono essere lette e tradotte in linguaggio assembler con relativa facilità.
E’ quindi possibile ottenere il listato assembler del programma; questa fase si chiama deassemblazione e viene fatta con un disassemblatore. Dal momento che l’assembler si pone allo stesso livello del linguaggio macchina il codice prodotto è fedele.
Altro discorso è trasformare operazioni assembly in istruzioni di un linguaggio ad alto livello come il C. Qui entrerebbe in gioco un fantomatico “decompilatore” che, con la tecnica del “reverse engineering” cercherebbe di interpretare gruppi di operazioni assembly e ricavarne equivalenti costrutti in un linguaggio ad alto livello.
Strumenti del genere sono stati creati, ma, a causa delle ottimizzazioni del compilatore è praticamente impossibile ottenere un sorgente anche solo simile all’originale. Inoltre, cosa non da poco, si perdono tutti i nomi delle variabili e delle funzioni, cosicché il sorgente decompilato avrà per variabili nomi del tipo v1,v2… e f1,f2… per le funzioni. Inoltre, il codice sarà privo di commenti, incrementando la difficoltà di lettura. Non è detto, infine, che compilandolo di nuovo, si ottenga lo stesso eseguibile!
In sintesi, dal punto di vista pratico, è per ora quasi impossibile lavorare con il risultato di una decompilazione, rendendo l’assembler l’unico linguaggio adatto ad analizzare e modificare un programma di cui non si possiede il sorgente.

Torna all'indice

4.2 - Il nome di un vettore è equivalente al puntatore al suo primo elemento?

Un array ed un puntatore sono due tipi di dati diversi. Il primo è un tipo aggregato che permette di memorizzare lo stesso tipo di dato uno dopo l'altro in memoria ed individua questa memoria (che viene allocata automaticamente (vedi risposta 3.8) o staticamente a seconda di come viene definito l'array) con un unico identificatore e consente l'accesso ai suoi elementi tramite indice (con l'operatore []).

Di contro, un puntatore è una variabile semplice atta a contenere un indirizzo. Un puntatore si può far puntare ad un'area di memoria allocata dinamicamente ed adatta a contenere il tipo di dato che si vuole gestire attraverso il puntatore. Il puntatore stesso è tipizzato in quanto soggetto alla cosiddetta aritmetica dei puntatori che consente di passare da un elemento all'altro in memoria (ammesso che sia stato allocato spazio per più di un elemento) utilizzando semplicemente l'operatore ++ o comunque un operatore di somma. A differenza dell'array, il puntatore può essere riassegnato per farlo puntare in qualche altro posto.

La confusione che a volte si sente tra array e puntatori deriva dal fatto che in un'espressione il nome dell'array viene convertito automaticamente nel puntatore al suo primo elemento (ad esempio passandolo come parametro ad una funzione). Ci sono solo tre eccezioni a questa regola:

  1. L'operatore sizeof().
  2. L'operatore & (address of).
  3. Le stringhe costanti utilizzate per inizializzare un array.

In questi tre casi l'array non viene convertito al puntatore al suo primo elemento.

Torna all'indice


Redatto da Marco Coppo grazie alla collaborazione dei partecipanti al newsgroup it.comp.lang.c – © Febbraio 2000

 



Disclaimer

SourceNet non riconosce nessun tipo di garanzia per il contenuto di tutta la sezione dedicata alle FAQ sul linguaggio C pubblicata su questo sito (SourceNet Italia).
Tutto il contenuto della FAQ sul linguaggio C è fornito "così come è", senza alcuna garanzia di qualsiasi tipo, sia espressa che implicita, ivi incluse, senza limitazioni, le garanzie implicite di commerciabilità o idoneità per uno scopo particolare ovvero quelle che escludano la violazione di diritti altrui. L'intero rischio derivante dall'uso o dalle prestazioni del contenuto di tutta la sezione della FAQ sul linguaggio C rimane a carico dell'utente.


Copyright

Il contenuto dell'intera FAQ sul linguaggio C situata in questo sito è di proprietà di Marco Coppo.
Non viene concessa a nessuno la possibilità di poter copiare il contenuto dell'intera FAQ sul linguaggio C senza l'autorizzazione di Marco Coppo. Per contattare l'autore contattate il webmaster di http://www.programmazione.it.


Note sulla revisione

La versione di questa FAQ è stata aggiornata il 14/7/2000.


Pagine FAQ sulla programmazione

 

Torna ad inizio pagina

[ Le FAQ sul linguaggio C ] Le FAQ sul linguaggio C++ ] Le FAQ sul linguaggio HTML ] Le FAQ sui Modem ] Le FAQ sul linguaggio Java ] Le FAQ su Outlook Express e sul prefisso R: ] Le FAQ su Outlook Express 5 ] Le FAQ sul linguaggio Perl ] Le FAQ sui dischi rigidi ] Le FAQ sui modem Conexant ] Le FAQ sul linguaggio C++ ] Le FAQ su GeForce ] La FAQ su Hamster ]

Ultimo aggiornamento : 17/01/2009.   
Home ] Su ] Novità ] Hardware ] Software ] Windows ] Sicurezza ] Linguaggi ] Cerca ] Contatta ] Guestbook ] Mappa ] Info su... ]

Copyright © 1997-2070, Joseph Parrello. Tutti i diritti sono riservati.

Siete il visitatore n. Contatore Sito
Bpath Contatore
dal 17 gennaio 2009.