Visualizzazione post con etichetta c. Mostra tutti i post
Visualizzazione post con etichetta c. Mostra tutti i post

giovedì 3 marzo 2011

C: printf e specificatori di formato

Gli specificatori di formato permettono alla funzione printf di stampare il contenuto delle variabili; tutti gli specificatori sono costituiti da un carattere % seguito da uno o più caratteri alfabetici.

char %c
short, int, long %d
short, int, long %i
unsigned short, unsigned int, unsigned long %u
float %f
double %lf
float, double (notazione scientifica) %e
float, double [usa il più breve tra %f e %e] %g
short, int, long (formato ottale) %o
short, int, long (formato esadecimale) %x
puntatore %p
sequenza di caratteri terminata da un '\0' %s
unsigned long long [64-bit] %llu
long long [64-bit] %ll
long double [64-bit] %Lf
unsigned short [8-bit] %hu

Vedi anche come modificare il numero di cifre decimali stampate da printf per un float.

C: modificare il numero di cifre decimali stampate da printf per un float

Nell'invocazione di una printf è possibile definire il numero di cifre decimali che verranno stampate per il valore associato ad una variabile di tipo float.
  printf("%.3f\n",f1); //stampa 3 cifre decimali
printf("%.10f\n",f1); //stampa 10 cifre decimali

Di default la funzione stampa 6 cifre decimali per ogni float.
Quanto descritto vale anche per i tipi double.

lunedì 13 dicembre 2010

C: leggere dati da file scartando testo e commenti

Si supponga di avere un file simile a questo
 #commento1 
valore 1
valore 2
# commento2
valore 3
# commento 3
# commento 4
valore 4
valore 5
valore 6

Si supponga di voler estrapolare da esso i valori escludendo sia il testo superfluo che i commenti.

Il seguente codice descrive un possbile modo per farlo.
 #include <stdio.h> 

int main ( void )
{
static const char filename[] = "file.txt";

FILE *file = fopen ( filename, "r" );

if ( file != NULL )
{
/* here, 4096 is the maximum line size */
char line [ 4096 ];
int i;

/* read a line */
while ( fgets ( line, sizeof(line), file ) != NULL )
{
if(line[0]!='#')
{
sscanf( line, "valore %d\n", &i );
printf("%d\n",i);
}
}
fclose ( file );

}
else
{
perror ( filename );
}

return 0;
}

L'esecuzione del programma descritto sul file di input precedentemente mostrato conduce alla seguente esecuzione
 1
2
3
4
5
6

Il file viene letto una riga alla volta e le righe di commento vengono scartate (riconosciute tramite il carattere '#' iniziale). Se non si tratta di commento, il valore numerico viene estrapolato dalla stringa di testo.

IMPORTANTE: fgets non legge sempre una riga, legge tanti byte quanti sono specificati dal secondo argomento, in presenza di un simbolo di "a capo" interrompe la lettura anche se il numero di byte specificato non è stato raggiunto; quando questo accade si può dire che una riga è stata letta ma per ovvie ragioni è necessario essere certi che la lunghezza in caratteri di una riga non superi quella specificata come secondo argomento di fgets.

martedì 9 novembre 2010

C: stampare un long double con printf

Lo specificatore di formato che permette di stampare il contenuto di una variabile di tipo long double è %Lf

La seguente riga, per esempio, stampa sullo standard output il risultato della funzione powl che, in questo caso, restituisce 2 elevato a 64 tramite un long double:
  printf("%Lf\n",powl(2.0,64.0)); 

sabato 23 gennaio 2010

C/C++: scoprire quanta memoria occupa una variabile

A volte potrebbe essere utile conoscere la quantità di memoria occupata da una variabile in uno dei nostri programmi C o C++.
La risposta a questa necessità è sizeof, funzione che può essere utilizzata come da esempio:

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

int main()
{
printf("%d\n",sizeof(int));

return 0;
}

In questo modo possiamo sapere quanti byte occupa una qualsiasi variabile di tipo intero sulla nostra macchina: infatti, la dimensione delle variabile è influenzata sia dall'architettura hardware su cui stiamo lavorando che dal sistema operativo che utilizziamo.
Ad esempio, le variabili di tipo "long" occupano 4 byte su architetture a 32 bit, mentre occupano 8 byte se utilizziamo un processore con registri a 64 bit insieme ad un sistema operativo anch'esso a 64 bit (se usiamo un s.o. a 32 bit su un architettura a 64, è come se lavorassimo su un architettura a 32 bit).

La funzione sizeof risulta molto utile anche per allocare memoria dinamica in modo indipendente dalla particolare architettura, quindi per scrivere codice generico. Eccone un esempio:

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

int main()
{
int *p;
p = (int*)malloc(sizeof(int));

return 0;
}

Infatti, la funzione malloc richiede come argomento il numero di byte che la variabile puntata occuperà, spesso senza sizeof non sarebbe possibile allocare in modo corretto memoria dinamica, ad esempio, quando allochiamo spazio per un puntarore ad una struttura dati complessa.

martedì 15 dicembre 2009

C: Math.h e undefined reference to...

Imbattersi in un errore come il seguente è particolarmente pericoloso se non si sa di cosa si tratta, in questo caso la soluzione, banale, sarebbe ardua da identificare.

    /tmp/cctraBp0.o: In function `main': 
run.c:(.text+0x34): undefined reference to `sqrt'
collect2: ld returned 1 exit status
In questo esempio ho compilato un programma molto semplice:

 #include <stdio.h> 
#include <math.h>

int main()
{
double x = 2.0;

printf("Hello (%lf).\n", sqrt(x));

return 0;
}
Qui viene utilizzata una funzione della libreria matematica, sqrt, e viene opportunamente incluso l'header math.h, ma la libreria matematica del C appartiene ad una speciale categoria di librerie, quella delle librerie dinamiche o condivise (shared libraries).
Quando si usa una di queste librerie non è sufficiente includerla nel codice, si rende necessario utilizzare delle opzioni di compilazione. Per compilare correttamente questo programma senza ottenere errori dal linker (ld), dobbiamo aggiungere l'opzione -lm, in questo modo:
 gcc run.c -o run -lm 
In questo modo si otterrà un eseguibile perfettamente funzionante senza errori di linking.

ATTENZIONE: quando si usano versioni recenti di gcc (sicuramente dalla versione 4.3.1 in poi) l'utilizzo dell'opzione -lm è necessario soltanto se si utilizzano le funzioni matematiche insieme a variabili. Se invocassimo sqrt con una costante come argomento, il gcc sostituisce l'invocazione della funzione con il suo risultato ed il codice corrispondente alla invocazione della funzione matematica non viene generato. Per questo non c'è bisogno di aggiungere l'opzione del linker che permette il caricamento dinamico della libreria a run-time. Il seguente codice, ad esempio:

 #include <stdio.h> 
#include <math.h>

int main()
{
printf("Hello (%lf).\n", sqrt(2.0));

return 0;
}
può essere compilato correttamente con il seguente comando:
 gcc run.c -o run 

senza che il linker restituisca alcun errore.

lunedì 9 novembre 2009

C: calcolare il tempo di esecuzione di una funzione

Vediamo un metodo semplice per calcolare il tempo di esecuzione di una funzione C.
In realtà questo metodo può essere utilizzato per calcolare il tempo trascorso tra due istruzioni qualsiasi di un nostro programma.
Innanzitutto abbiamo bisogno di includere nel nostro sorgente l'header time.h che ci permette di utilizzare il tipo di dato clock_t e la macro CLOCKS_PER_SEC, nonché la funzione clock(). Un semplicissimo esempio:

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

int main()
{
clock_t t1 = clock();
clock_t t2 = clock();

double time_sec =
(double)(t2-t1)/(double)(CLOCKS_PER_SEC);

printf("Time (sec): %lf\n",time_sec);

return 0;
}

A questo punto possiamo determinare il tempo necessario ad eseguire qualsiasi sequenza di istruzioni o intere funzioni semplicemente inserendo prima e dopo tali sequenze una istruzione del tipo
    clock_t t1 = clock(); 
Supponiamo di voler misurare il tempo necessario per eseguire un for consistente in 2'000'000'000 cicli vuoti, il codice necessario sarebbe il seguente:

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

int main()
{
int i;

clock_t t1 = clock();

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

clock_t t2 = clock();

double time_sec =
(double)(t2-t1)/(double)(CLOCKS_PER_SEC);

printf("Time (sec): %lf\n",time_sec);

return 0;
}

Naturalmente il tempo necessario ad eseguire un insieme di istruzioni non è deterministico, cioè non sarà sempre lo stesso ad ogni esecuzione. Ogni volta che lanciamo la nostra applicazione possiamo ottenere valori differenti per il tempo di esecuzione.

NOTA: se vogliamo determinare il tempo complessivo necessario ad eseguire un intero programma, la shell di Linux ci mette a disposizione un comando molto utile e molto più semplice da utilizzare, si chiama time e può essere utilizzato così:
    time ./nome_programma 

giovedì 5 novembre 2009

C: compilare un semplice programma

Come compilare un semplice programma C in poche semplici mosse.

Innanzitutto abbiamo bisogno del semplice programma C, quindi ecco una versione personalizzata del solito "Hello World!".
 #include <stdio.h> 

int main()
{
printf("Hello Max!");

return 0;
}
Potete scrivere questo codice (o fare copia e incolla dello stesso) all'interno di un file chiamato hello.c, ad esempio. Il nome del file non è importante potete chiamare i vostri file come volete, purché siano nomi validi per il vostro sistema operativo.
Adesso vediamo come compilare tale programma con il compilatore gcc.
E' sufficiente digitare da riga di comando:
 gcc hello.c -o hello 
L'opzione -o permette di specificare il nome del file eseguibile che vogliamo generare, in questo caso hello. Se non viene specificato un nome tramite l'opzione -o gcc assegna un nome standard all'eseguibile: a.out.
Il gioco è fatto, adesso abbiamo a disposizione il nostro programma compilato che si chiama hello e si trova nella stessa cartella di hello.c, eseguendolo si otterrà in output sul terminale la stringa di testo che abbiamo chiesto di stampare tramite l'istruzione printf.
In GNU/Linux per eseguire il programma appena compilato è necessario digitare la seguente istruzione in un terminale e premere invio:
 ./hello 
Perché "semplice programma C"? Primo perché è costituito da un unica istruzione, secondo perché il programma qui presentato è costituito anche da un solo file. Di solito i programmi dei veri programmatori sono compilati a partire da diversi file sorgente (in alcuni casi centinaia o migliaia di file), ma in questi casi è un po' più complessa la procedura di compilazione tanto che a volte si ricorre ad un ottimo strumento, chiamato makefile, che permette di compilare grandi progetti in modo semplice, veloce ed efficiente. Spero di poter mostrare il funzionamento di un makefile in un prossimo post.