GNU Compiler Collection

linuxtutorialTutorial Linux – Il GCC (GNU Compiler Collection) merita la palma di compilatore più diffuso in ambiente Linux, grazie alla sua versatilità, infatti può compilare programmi scritti in C, C++, Objective-C, Fortran, Java, e Ada.
Uno dei pregi indiscussi di GCC è concedere al programmatore un grande controllo su tutte le fasi del processo di compilazione:

precompilazione
compilazione
assemblaggio
linking.

GCC si può scaricare all’indirizzo http://gcc.gnu.org/. E’ giunto alla release 4.4.3.
In questo breve articolo ci soffermeremo sulla compilazione in C. Il processo può essere interrotto dopo ogni passaggio per esaminare il risultato parziale. GCC comprende anche sottolinguaggi del C, come ANSI C, o traditional (Kernighan e Ritchie) C. Si può controllare quante e quali informazioni di debug includere nel file binario risultante. Come la maggior parte dei compilatori, GCC ottimizza il codice.

Il comando gcc esegue il compilatore C. Per utilizzarlo si indichi il nome del file sorgente C e si usi l’opzione -o per specificare il nome del file di output. gcc precompilerà, compilerà, assemblerà e linkerà il programma, generando un eseguibile, spesso chiamato un binario. Questa la sintassi più semplice:

gcc infile.c [-o outfile]

infile.c è un file con codice sorgente C e -o  che specifica di salvare con nome outfile il risultato della compilazione. In tutto il libro i caratteri [ ] indicano che l’argomento incluso è opzionale.

Il seguente esempio usa gcc per creare il programma hello partendo da file sorgente hello.c. Per prima cosa il codice sorgente:
/*
* hello.c – canonical hello world program
*/
#include <stdio.h>
int main(int argc, char *argv[])
{
printf(“Hello, Linux programming world!\n”);
return 0;
}

Ora, per compilare e lanciare il programma, basta digitare quanto segue:
$ gcc hello.c -o hello

Se l’operazione prosegue senza intoppi gcc ritorna al prompt della shell. Esso compila e collega (link) il file con il codice sorgente hello.c (gcc hello.c), creando un binario chiamato hello, come specificato dall’uso dell’argomento -o hello.

Se si lancia il programma, ecco l’output che si avrà:

$ ./hello
Hello, Linux programming world!

Il comando per eseguire il programma hello include esplicitamente la directory corrente, indicata da un . , questo perché avere la directory corrente nel path è un rischio per la sicurezza. Per cui, invece di avere una variabile $PATH del tipogcc_lg/bin:/usr/bin:/usr/local/bin:., se ne dovrebbe avere una del tipo: bin:/usr/bin:/usr/local/bin in modo che un cracker non possa posizionare un eseguibile pericoloso nella directory corrente con nome uguale a quello di un normale comando che si voglia lanciare.

Con GCC (e con qualunque compilatore C), la fase di precompilazione gestisce costrutti quali #include <stdio.h> oppure macro come #define. Una volta che sono state trattate queste, comincia la normale compilazione.

GCC si basa sull’estensione del file per determinare di che tipo file di codice sorgente si tratti, quindi che tipo di linguaggio di programmazione è stato usato.

COMPILARE CODICI SORGENTE MULTIPLI
La gran parte dei progetti non banali consistono di più file di codice sorgente. Ciascun file deve essere compilato diventando un file oggetto (object code file) prima del passaggio finale di unione (link). Per far questo, si passi a gcc il nome di ciascun file sorgente che deve compilare. GCC penserà al resto. gcc andrà lanciato in un modo simile a questo:
$ gcc file1.c file2.c file3.c -o progname

gcc creerà i file file1.o, file2.o e file3.o e quindi li linkerà tutti assieme per generare il file progname. In alternativa si può usare l’opzione -c di gcc sui singoli file, creando un file oggetto da ciascuno. In un secondo tempo si linkeranno i file oggetto per creare l’eseguibile. In questo caso il precedente singolo comando diventa:
$ gcc -c file1.c
$ gcc -c file2.c
$ gcc -c file3.c
$ gcc file1.o file2.o file3.o -o progname

Una ragione per procedere così è quella di evitare di ricompilare i file che non sono cambiati. Un’altra ragione per compilare separatamente i file sorgente è quella di evitare compilazioni troppo lunghe. Compilare file multipli con un singolo comando gcc può durare a lungo se uno dei sorgenti è molto grande.

Diamo un’occhiata a un esempio che crea un file binario eseguibile da più file di codice sorgente. Il programma di esempio, chiamato newhello, comprende un file sorgente scritto in C, main.c (listato 29-1); un file header, msg.h (listato 29-2); e un secondo file sorgente in C, msg.c (listato 29-3)

LISTATO 29-1
Programma principale newhello
/*
* main.c driver program
*/
#include <stdio.h>
#include “msg.h”
int main(int argc, char *argv[])
{
char msg_hi[] = { “Hi there, programmer!” };
char msg_bye[] = { “Goodbye, programmer!” };
printf(“%s\n”, msg_hi);
prmsg(msg_bye);
return 0;
}

LISTATO 29-2
File Header con funzioni ausiliarie
/*
* msg.h – header for msg.c
*/
#ifndef MSG_H_
#define MSG_H_
void prmsg(char *msg);
#endif /* MSG_H_ */

LISTATO 29-3
Definizione delle funzioni audiliarie di newhello
/*
* msg.c – function declared in msg.h
*/
#include <stdio.h>
#include “msg.h”
void prmsg(char *msg)
{
printf(“%s\n”, msg);
}

Il comando per compilare questi programmi per creare newhello è:
$ gcc msg.c main.c -o newhello

Il comando gcc trova il file header msg.h nella directory corrente e, automaticamente lo include durante la fase di precompilazione. La locazione del file stdio.h è nota al comando gcc, che provvederà a includerlo. Si possono impostare ulteriori cartelle di ricerca per questi file (chiamati file di include), utilizzando l’opzione -I di gcc.

Per creare singolarmente i file oggetto si può usare il seguente comando:
$ gcc -c msg.c
$ gcc -c main.c

Quindi si potrà creare newhello dai file oggetto come segue:
$ gcc msg.o main.o -o newhello

Quando si eseguirà il programma si otterrà il seguente output:
$ ./newhello
Hi there, programmer!
Goodbye, programmer!

Prima di creare il file binario newhell, gcc crea il file oggetto per ciascun file sorgente.

Come detto in precedenza, -o file specifica a GCC di mettere il risultato in un file di nome file, indipendentemente da che tipo di file abbia processato. I nomi assegnati di default per un file con nome file.suffix, se non specificata l’opzione -o, sono: a.out per l’eseguibile, file.o per un file oggetto e file.s per il file assembly. L’output della fase di precompilazione andrà su stdout.

About the Author