Guida Completa a GCC: GNU Compiler Collection

Indice

  1. Introduzione a GCC
  2. Installazione di GCC
  3. Verifica dell’installazione
  4. Compilazione Base con GCC
  5. Compilazione per Diversi Linguaggi
  6. Le Fasi della Compilazione
  7. Opzioni Fondamentali di GCC
  8. Opzioni di Ottimizzazione
  9. Opzioni di Debug e Warning
  10. Opzioni Avanzate
  11. Gestione delle Librerie
  12. Compilazione di Progetti Multi-file
  13. Utilizzi Avanzati
  14. Esempi Pratici Completi
  15. Troubleshooting Comune

1. Introduzione a GCC

1.1 Cos’è GCC?

GCC (GNU Compiler Collection) è una suite di compilatori sviluppata dal progetto GNU e distribuita con licenza GPL. Originariamente creato come compilatore C (GNU C Compiler), GCC si è evoluto diventando una collezione di compilatori per diversi linguaggi di programmazione.

1.2 Linguaggi Supportati

GCC supporta nativamente i seguenti linguaggi:

1.3 Caratteristiche Principali

1.4 Architettura di GCC

GCC opera attraverso diverse fasi:

  1. Preprocessing: espansione macro, inclusione file header
  2. Compilation: traduzione in linguaggio assembly
  3. Assembly: conversione in codice oggetto
  4. Linking: collegamento con librerie per creare l’eseguibile

2. Installazione di GCC

2.1 Installazione su Ubuntu/Linux

2.1.1 Verifica Presenza di GCC

Prima di installare, verificare se GCC è già presente nel sistema:

gcc --version

Se non installato, si procede con l’installazione.

2.1.2 Installazione Standard

Su distribuzioni Debian/Ubuntu utilizzare il package manager apt:

# Aggiornare i repository
sudo apt update

# Installare GCC
sudo apt install gcc

# Per installare anche g++ (compilatore C++)
sudo apt install g++

# Per installare l'intera suite build-essential (consigliato)
sudo apt install build-essential

Il pacchetto build-essential include:

2.1.3 Installazione su Fedora/CentOS/RHEL

# Per Fedora
sudo dnf install gcc gcc-c++

# Per CentOS/RHEL
sudo yum install gcc gcc-c++

# Per installare l'intero Development Tools group
sudo yum groupinstall "Development Tools"

2.1.4 Installazione su Arch Linux

sudo pacman -S gcc

2.1.5 Installazione di Versioni Specifiche

Per installare versioni multiple di GCC:

# Installare GCC versione specifica (esempio: GCC 11)
sudo apt install gcc-11 g++-11

# Configurare alternative per gestire versioni multiple
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 110
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 110

# Selezionare la versione da utilizzare
sudo update-alternatives --config gcc
sudo update-alternatives --config g++

2.2 Installazione su macOS

2.2.1 Installazione tramite Xcode Command Line Tools (Raccomandato)

Questo metodo installa Clang (compilatore compatibile con GCC) che viene invocato attraverso il comando gcc:

# Installare Command Line Tools
xcode-select --install

Apparirà una finestra di dialogo per confermare l’installazione. Questo metodo è il più semplice e fornisce un ambiente di sviluppo completo.

Nota importante: Su macOS, il comando gcc è tipicamente un alias per clang, non il vero GCC GNU. Clang è compatibile al 99% con GCC per la maggior parte degli usi.

2.2.2 Installazione del Vero GCC tramite Homebrew

Per installare il vero GCC GNU:

# Installare Homebrew se non presente
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Installare GCC
brew install gcc

# Verificare l'installazione
gcc-13 --version  # Il numero può variare in base alla versione installata

Con Homebrew, il vero GCC sarà disponibile come gcc-13, gcc-12, etc. per evitare conflitti con Clang.

2.2.3 Configurazione del PATH

Se necessario, aggiungere GCC al PATH modificando ~/.zshrc (o ~/.bash_profile per bash):

# Aggiungere al file di configurazione shell
export PATH="/usr/local/bin:$PATH"

# Ricaricare la configurazione
source ~/.zshrc

2.3 Installazione su Windows

2.3.1 Installazione tramite MinGW-w64 (Metodo Raccomandato)

MinGW-w64 (Minimalist GNU for Windows) è una porta di GCC per Windows.

Passo 1: Scaricare MinGW-w64

Visitare il sito ufficiale: https://www.mingw-w64.org/downloads/

Opzioni principali:

Passo 2: Installazione con MSYS2 (metodo consigliato)

  1. Scaricare MSYS2 da: https://www.msys2.org/

  2. Eseguire l’installer msys2-x86_64-[data].exe

  3. Seguire le istruzioni mantenendo il percorso predefinito: C:\msys64

  4. Completare l’installazione e aprire il terminale MSYS2

  5. Aggiornare i pacchetti:

pacman -Syu
  1. Chiudere e riaprire MSYS2, poi aggiornare nuovamente:
pacman -Su
  1. Installare la toolchain GCC:
# Per architettura 64-bit
pacman -S mingw-w64-x86_64-gcc

# Per architettura 32-bit
pacman -S mingw-w64-i686-gcc

# Per installare l'intera suite di sviluppo
pacman -S --needed base-devel mingw-w64-x86_64-toolchain

Passo 3: Configurare le Variabili d’Ambiente

  1. Aprire “Modifica variabili d’ambiente di sistema” dal menu Start
  2. Cliccare su “Variabili d’ambiente”
  3. Nella sezione “Variabili di sistema”, selezionare “Path” e cliccare “Modifica”
  4. Aggiungere il percorso: C:\msys64\mingw64\bin (per 64-bit)
  5. Cliccare OK per salvare

Passo 4: Verificare l’installazione

Aprire un nuovo Prompt dei comandi (cmd) o PowerShell:

gcc --version
g++ --version

2.3.2 Installazione tramite WinLibs (Alternativa)

  1. Visitare: https://winlibs.com/
  2. Scaricare la versione desiderata (UCRT o MSVCRT runtime)
  3. Estrarre l’archivio in una directory (es: C:\mingw64)
  4. Aggiungere C:\mingw64\bin alle variabili d’ambiente PATH
  5. Verificare l’installazione aprendo cmd e digitando gcc --version

2.3.3 Installazione tramite WSL (Windows Subsystem for Linux)

Un’alternativa moderna è utilizzare WSL per avere un ambiente Linux completo su Windows:

# In PowerShell con privilegi amministrativi
wsl --install -d Ubuntu

# Dopo il riavvio e la configurazione di Ubuntu
sudo apt update
sudo apt install build-essential

2.3.4 Installazione tramite Cygwin

  1. Scaricare Cygwin da: https://www.cygwin.com/
  2. Eseguire setup-x86_64.exe
  3. Durante l’installazione, nel package selector, cercare e selezionare:
  4. Completare l’installazione
  5. GCC sarà disponibile nel terminale Cygwin

3. Verifica dell’installazione

Dopo l’installazione, verificare che GCC funzioni correttamente.

3.1 Verifica Versione

# Verificare versione GCC
gcc --version

# Verificare versione G++
g++ --version

# Verificare altri compilatori se installati
gfortran --version

3.2 Test di Compilazione

Creare un file di test hello.c:

#include <stdio.h>

int main() {
    printf("GCC funziona correttamente!\n");
    return 0;
}

Compilare ed eseguire:

# Compilazione
gcc hello.c -o hello

# Esecuzione (Linux/macOS)
./hello

# Esecuzione (Windows)
hello.exe

Se l’output è “GCC funziona correttamente!”, l’installazione è riuscita.

3.3 Informazioni Dettagliate sul Sistema

Per visualizzare informazioni dettagliate sulla configurazione di GCC:

# Visualizzare la configurazione di compilazione di GCC
gcc -v

# Mostrare le directory di ricerca degli include
gcc -E -x c - -v < /dev/null  # Linux/macOS
echo | gcc -E -x c - -v        # Windows

# Mostrare le directory di ricerca delle librerie
gcc -print-search-dirs

# Visualizzare i target supportati
gcc -dumpmachine

4. Compilazione Base con GCC

4.1 Sintassi Generale

La sintassi base di GCC è:

gcc [opzioni] file_sorgente -o file_output

4.2 Compilazione Semplice

Esempio C base

// programma.c
#include <stdio.h>

int main() {
    printf("Hello, World!\n");
    return 0;
}

Compilazione:

# Compilazione base
gcc programma.c

# Questo crea un eseguibile chiamato 'a.out' su Linux/macOS
# o 'a.exe' su Windows

Specificare il Nome dell’Output

# Usare l'opzione -o per specificare il nome
gcc programma.c -o programma

# Eseguire
./programma  # Linux/macOS
programma.exe  # Windows

4.3 Processo Completo in Un Comando

In un singolo comando, GCC esegue:

  1. Preprocessing
  2. Compilation
  3. Assembly
  4. Linking

4.4 Compilazione C++

Per file C++ usare g++:

// programma.cpp
#include <iostream>

int main() {
    std::cout << "Hello, C++!" << std::endl;
    return 0;
}

Compilazione:

g++ programma.cpp -o programma

Nota: Anche se tecnicamente si può usare gcc per compilare C++, g++ è preferibile perché linka automaticamente le librerie standard C++.


5. Compilazione per Diversi Linguaggi

5.1 Compilazione C

# File singolo
gcc programma.c -o programma

# Con standard specifico
gcc -std=c99 programma.c -o programma    # Standard C99
gcc -std=c11 programma.c -o programma    # Standard C11
gcc -std=c17 programma.c -o programma    # Standard C17 (più recente)

Standard C supportati:

5.2 Compilazione C++

# File singolo C++
g++ programma.cpp -o programma

# Con standard specifico
g++ -std=c++98 programma.cpp -o programma   # C++98
g++ -std=c++11 programma.cpp -o programma   # C++11
g++ -std=c++14 programma.cpp -o programma   # C++14
g++ -std=c++17 programma.cpp -o programma   # C++17
g++ -std=c++20 programma.cpp -o programma   # C++20
g++ -std=c++23 programma.cpp -o programma   # C++23 (sperimentale)

Standard C++ supportati:

5.3 Compilazione Fortran

# Installare gfortran se non presente
sudo apt install gfortran  # Ubuntu/Debian

# Compilazione
gfortran programma.f90 -o programma
gfortran programma.f -o programma      # Fortran 77 (fixed format)
gfortran programma.f90 -o programma    # Fortran 90+ (free format)

5.4 Compilazione Objective-C

# Objective-C (principalmente su macOS)
gcc -lobjc programma.m -o programma

# Objective-C++
g++ -lobjc programma.mm -o programma

5.5 Compilazione Ada (GNAT)

# Compilare con GNAT
gnatmake programma.adb

# Oppure
gcc -c programma.adb
gnatbind programma
gnatlink programma

5.6 Compilazione Go (GCCGo)

# Con gccgo
gccgo programma.go -o programma

# Nota: è più comune usare il compilatore Go standard (gc)

5.7 Compilazione D

# Con gdc
gdc programma.d -o programma

6. Le Fasi della Compilazione

GCC permette di controllare individualmente le quattro fasi della compilazione.

6.1 Preprocessing (-E)

Il preprocessing espande le macro e include i file header.

# Solo preprocessing, output su stdout
gcc -E programma.c

# Salvare il risultato in un file
gcc -E programma.c -o programma.i

Cosa fa il preprocessor:

Esempio:

// test.c
#define PI 3.14159
#include <stdio.h>

int main() {
    double r = PI * 2.0;
    return 0;
}
gcc -E test.c -o test.i
# Il file test.i conterrà tutto stdio.h espanso e PI sostituito con 3.14159

6.2 Compilation (-S)

Genera codice assembly dal codice sorgente preprocessato.

# Genera file assembly (.s)
gcc -S programma.c

# Output sarà programma.s

Il file .s contiene istruzioni assembly specifiche per l’architettura target.

Esempio di output assembly (estratto):

main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $0, %eax
    popq    %rbp
    ret

6.3 Assembly (-c)

Converte il codice assembly in codice oggetto (binario non linkato).

# Genera file oggetto (.o)
gcc -c programma.c

# Output sarà programma.o

Il file .o (.obj su Windows) contiene codice macchina ma non è ancora eseguibile perché mancano i collegamenti alle librerie.

6.4 Linking (default)

Collega i file oggetto con le librerie necessarie per creare l’eseguibile finale.

# Linking di file oggetto
gcc programma.o -o programma

# Linking di più file oggetto
gcc file1.o file2.o file3.o -o programma

6.5 Pipeline Completa Manuale

Eseguire tutte le fasi manualmente:

# 1. Preprocessing
gcc -E programma.c -o programma.i

# 2. Compilation
gcc -S programma.i -o programma.s

# 3. Assembly
gcc -c programma.s -o programma.o

# 4. Linking
gcc programma.o -o programma

# Eseguire
./programma

Questa separazione è utile per:


7. Opzioni Fondamentali di GCC

7.1 Opzioni di Output

-o <file>

Specifica il nome del file di output.

gcc programma.c -o mio_programma

-c

Compila o assembla il codice sorgente ma non linka. Produce file oggetto .o.

gcc -c file1.c  # Produce file1.o
gcc -c file2.c  # Produce file2.o
gcc file1.o file2.o -o programma  # Link finale

-S

Compila ma non assembla. Produce file assembly .s.

gcc -S programma.c  # Produce programma.s

-E

Solo preprocessing. Output su stdout o file specificato.

gcc -E programma.c -o programma.i

7.2 Opzioni di Standard del Linguaggio

-std=<standard>

Specifica lo standard del linguaggio da utilizzare.

# C
gcc -std=c99 programma.c -o programma
gcc -std=c11 programma.c -o programma

# C++
g++ -std=c++11 programma.cpp -o programma
g++ -std=c++17 programma.cpp -o programma

-ansi

Equivale a -std=c90 per C o -std=c++98 per C++.

gcc -ansi programma.c -o programma

-pedantic

Emette tutti i warning richiesti dallo standard ISO.

gcc -std=c11 -pedantic programma.c -o programma

-pedantic-errors

Come -pedantic, ma emette errori invece di warning.

gcc -std=c11 -pedantic-errors programma.c -o programma

7.3 Opzioni di Include e Define

-I<directory>

Aggiunge una directory al percorso di ricerca degli header file.

gcc -I/path/to/headers programma.c -o programma
gcc -I./include -I./lib/include programma.c -o programma

Esempio struttura progetto:

progetto/
├── src/
│   └── main.c
├── include/
│   └── mylib.h
└── lib/
    └── mylib.c

Compilazione:

gcc -I./include src/main.c lib/mylib.c -o programma

-D<macro>[=valore]

Definisce una macro come se fosse scritta con #define.

# Definire macro booleana
gcc -DDEBUG programma.c -o programma

# Definire macro con valore
gcc -DVERSION=2 -DMAX_SIZE=1024 programma.c -o programma

Esempio di utilizzo:

#include <stdio.h>

int main() {
    #ifdef DEBUG
        printf("Modalità debug attiva\n");
    #endif
    
    #ifdef VERSION
        printf("Versione: %d\n", VERSION);
    #endif
    
    return 0;
}

Compilazione:

gcc -DDEBUG -DVERSION=2 programma.c -o programma

-U<macro>

Rimuove una definizione di macro predefinita.

gcc -U__STRICT_ANSI__ programma.c -o programma

7.4 Opzioni di Librerie

-l<libreria>

Specifica una libreria da linkare. GCC cerca lib<libreria>.a o lib<libreria>.so.

# Linkare libreria matematica (libm)
gcc programma.c -o programma -lm

# Linkare più librerie
gcc programma.c -o programma -lm -lpthread -lssl -lcrypto

Esempio con matematica:

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

int main() {
    double result = sqrt(16.0);
    printf("Radice di 16 = %.2f\n", result);
    return 0;
}

Compilazione:

gcc -o programma programma.c -lm

Nota: L’ordine è importante! Le librerie dipendenti devono essere specificate dopo quelle che le usano.

-L<directory>

Aggiunge una directory al percorso di ricerca delle librerie.

gcc -L/usr/local/lib -L./lib programma.c -o programma -lmylib

-static

Linka le librerie staticamente invece che dinamicamente.

gcc -static programma.c -o programma

-shared

Crea una libreria condivisa (shared library).

gcc -shared -fPIC mylib.c -o libmylib.so

7.5 Opzioni di Architettura

-m32 / -m64

Genera codice per architettura a 32 o 64 bit.

# 32-bit
gcc -m32 programma.c -o programma32

# 64-bit
gcc -m64 programma.c -o programma64

-march=<arch>

Ottimizza per un’architettura CPU specifica.

# Architettura nativa (quella del sistema corrente)
gcc -march=native programma.c -o programma

# Specifiche architetture
gcc -march=x86-64 programma.c -o programma
gcc -march=armv7-a programma.c -o programma

-mtune=<cpu>

Ottimizza per una specifica CPU mantenendo compatibilità.

gcc -mtune=intel programma.c -o programma
gcc -mtune=generic programma.c -o programma

7.6 Opzioni Varie Fondamentali

-v

Modalità verbose: mostra tutti i comandi eseguiti da GCC.

gcc -v programma.c -o programma

-pipe

Usa pipe invece di file temporanei durante la compilazione (più veloce).

gcc -pipe programma.c -o programma

-save-temps

Conserva i file intermedi (.i, .s, .o).

gcc -save-temps programma.c -o programma
# Crea: programma.i, programma.s, programma.o, programma

–help

Mostra le opzioni disponibili.

gcc --help
gcc --help=optimizers  # Help sulle opzioni di ottimizzazione
gcc --help=warnings    # Help sui warning

8. Opzioni di Ottimizzazione

Le ottimizzazioni migliorano le prestazioni del codice generato a scapito di tempi di compilazione più lunghi.

8.1 Livelli di Ottimizzazione

-O0 (default)

Nessuna ottimizzazione. Compilazione più rapida, debug più facile.

gcc -O0 programma.c -o programma
# Equivalente a non specificare alcuna opzione -O

-O1 o -O

Ottimizzazioni base. Riduce dimensione e tempo di esecuzione senza aumentare molto il tempo di compilazione.

gcc -O1 programma.c -o programma
# oppure
gcc -O programma.c -o programma

Attiva ottimizzazioni come:

-O2

Ottimizzazioni più aggressive (raccomandato per produzione).

gcc -O2 programma.c -o programma

Include tutte le ottimizzazioni di -O1 più:

È il livello raccomandato per la maggior parte delle applicazioni in produzione.

-O3

Massime ottimizzazioni. Può aumentare significativamente la dimensione del codice.

gcc -O3 programma.c -o programma

Include tutte le ottimizzazioni di -O2 più:

Attenzione: può occasionalmente introdurre bug in codice mal scritto.

-Os

Ottimizza per dimensione del codice.

gcc -Os programma.c -o programma

Utile per sistemi embedded o quando la dimensione del codice è critica.

-Ofast

Massime prestazioni ignorando la conformità stretta agli standard.

gcc -Ofast programma.c -o programma

Include -O3 più ottimizzazioni che possono violare la conformità IEEE/ISO:

-Og

Ottimizzazioni compatibili con il debugging.

gcc -Og -g programma.c -o programma

Mantiene buone prestazioni senza compromettere l’esperienza di debug.

8.2 Ottimizzazioni Specifiche

-finline-functions

Inlining aggressivo delle funzioni.

gcc -O2 -finline-functions programma.c -o programma

-funroll-loops

Srotola i loop per ridurre overhead delle iterazioni.

gcc -O2 -funroll-loops programma.c -o programma

-fomit-frame-pointer

Omette il frame pointer quando non necessario, liberando un registro.

gcc -O2 -fomit-frame-pointer programma.c -o programma

-ftree-vectorize

Abilita la vectorizzazione automatica (inclusa in -O3).

gcc -O2 -ftree-vectorize programma.c -o programma

-ffast-math

Ottimizzazioni matematiche aggressive che possono violare IEEE 754.

gcc -O2 -ffast-math programma.c -o programma

Attenzione: può causare comportamenti numerici imprevisti.

8.3 Link Time Optimization (LTO)

Ottimizzazione durante il linking attraverso tutto il programma.

# Compilazione con LTO
gcc -flto -O2 file1.c file2.c -o programma

# Per progetti grandi, compilare separatamente
gcc -flto -O2 -c file1.c
gcc -flto -O2 -c file2.c
gcc -flto -O2 file1.o file2.o -o programma

LTO permette ottimizzazioni cross-file che non sarebbero possibili altrimenti.

8.4 Profile Guided Optimization (PGO)

Ottimizzazione basata su dati di profilazione reali.

# Passo 1: Compilare con instrumentazione
gcc -O2 -fprofile-generate programma.c -o programma

# Passo 2: Eseguire il programma con dati rappresentativi
./programma < dati_test.txt

# Passo 3: Ricompilare usando i dati raccolti
gcc -O2 -fprofile-use programma.c -o programma_ottimizzato

# Cleanup
rm -f *.gcda

8.5 Confronto Prestazioni

Esempio di benchmark:

// test_performance.c
#include <stdio.h>
#include <time.h>

#define SIZE 10000000

int main() {
    clock_t start = clock();
    
    long long sum = 0;
    for (int i = 0; i < SIZE; i++) {
        sum += i * i;
    }
    
    clock_t end = clock();
    double time_spent = (double)(end - start) / CLOCKS_PER_SEC;
    
    printf("Somma: %lld\n", sum);
    printf("Tempo: %.6f secondi\n", time_spent);
    
    return 0;
}

Compilare con diversi livelli:

gcc -O0 test_performance.c -o test_O0
gcc -O1 test_performance.c -o test_O1
gcc -O2 test_performance.c -o test_O2
gcc -O3 test_performance.c -o test_O3

# Testare
time ./test_O0
time ./test_O1
time ./test_O2
time ./test_O3

9. Opzioni di Debug e Warning

9.1 Opzioni di Debug

-g

Genera informazioni di debug per il debugger (gdb).

gcc -g programma.c -o programma

Livelli di debug:

gcc -g3 programma.c -o programma

-ggdb

Ottimizza le informazioni di debug per GDB.

gcc -ggdb3 programma.c -o programma

-gdwarf-<version>

Specifica la versione del formato DWARF per il debug.

gcc -g -gdwarf-4 programma.c -o programma

Debug con Ottimizzazione

Combinare debug e ottimizzazione:

# Debug-friendly optimization
gcc -Og -g programma.c -o programma

# O anche O2 con debug (esperienza di debug degradata)
gcc -O2 -g programma.c -o programma

9.2 Opzioni di Warning

I warning aiutano a identificare potenziali problemi nel codice.

-Wall

Abilita un set completo di warning comuni.

gcc -Wall programma.c -o programma

Include warning per:

-Wextra (o -W)

Warning addizionali non inclusi in -Wall.

gcc -Wall -Wextra programma.c -o programma

Include:

-Werror

Tratta tutti i warning come errori (ferma la compilazione).

gcc -Wall -Wextra -Werror programma.c -o programma

Utile per garantire codice senza warning.

-Werror=<warning>

Tratta uno specifico warning come errore.

gcc -Wall -Werror=return-type programma.c -o programma

-Wpedantic

Warning per codice non conforme allo standard.

gcc -std=c11 -Wpedantic programma.c -o programma

Warning Specifici Utili

# Warning su variabili non inizializzate
gcc -Wuninitialized programma.c -o programma

# Warning su conversioni implicite
gcc -Wconversion programma.c -o programma

# Warning su shadowing di variabili
gcc -Wshadow programma.c -o programma

# Warning su funzioni deprecate
gcc -Wdeprecated-declarations programma.c -o programma

# Warning su formato printf/scanf errato
gcc -Wformat programma.c -o programma

# Warning combinati (configurazione rigorosa)
gcc -Wall -Wextra -Wpedantic -Wshadow -Wconversion \
    -Wuninitialized programma.c -o programma

-Wno-<warning>

Disabilita uno specifico warning.

# Disabilitare warning su variabili inutilizzate
gcc -Wall -Wno-unused-variable programma.c -o programma

-w

Disabilita tutti i warning (sconsigliato).

gcc -w programma.c -o programma

9.3 Sanitizers (Strumenti di Runtime)

I sanitizers rilevano errori a runtime.

-fsanitize=address (AddressSanitizer)

Rileva errori di memoria: buffer overflow, use-after-free, memory leaks.

gcc -fsanitize=address -g programma.c -o programma

Esempio:

// bug.c
#include <stdlib.h>

int main() {
    int *array = malloc(10 * sizeof(int));
    array[10] = 42;  // Buffer overflow!
    free(array);
    return 0;
}
gcc -fsanitize=address -g bug.c -o bug
./bug
# Output mostra il buffer overflow con stack trace

-fsanitize=undefined (UndefinedBehaviorSanitizer)

Rileva undefined behavior: overflow aritmetico, null pointer dereference, ecc.

gcc -fsanitize=undefined -g programma.c -o programma

-fsanitize=thread (ThreadSanitizer)

Rileva data race in programmi multi-thread.

gcc -fsanitize=thread -g programma.c -o programma -lpthread

-fsanitize=leak (LeakSanitizer)

Rileva memory leak.

gcc -fsanitize=leak -g programma.c -o programma

Combinare Sanitizers

# Address + Undefined Behavior
gcc -fsanitize=address,undefined -g programma.c -o programma

Nota: ThreadSanitizer non può essere combinato con AddressSanitizer.

9.4 Static Analysis

-fanalyzer

Analisi statica del codice (GCC 10+).

gcc -fanalyzer programma.c -o programma

Rileva:


10. Opzioni Avanzate

10.1 Preprocessore Avanzato

-M, -MM

Genera regole di dipendenza per Make.

# Include dipendenze di sistema
gcc -M programma.c

# Esclude dipendenze di sistema
gcc -MM programma.c

Output:

programma.o: programma.c myheader.h

-MD, -MMD

Come -M e -MM ma compila anche il file.

gcc -MMD -c programma.c
# Crea programma.o e programma.d (file dipendenze)

-MF <file>

Specifica il file di output per le dipendenze.

gcc -MMD -MF programma.dep -c programma.c

-include <file>

Include automaticamente un file header prima della compilazione.

gcc -include config.h programma.c -o programma

-imacros <file>

Include le macro da un file senza includerne il contenuto.

gcc -imacros macros.h programma.c -o programma

10.2 Generazione di Codice

-fPIC / -fpic

Genera Position Independent Code per librerie condivise.

gcc -fPIC -c mylib.c -o mylib.o
gcc -shared mylib.o -o libmylib.so

-fPIC è preferito per compatibilità, -fpic può essere più efficiente ma ha limitazioni.

-fno-common

Ogni variabile globale non inizializzata viene allocata nel segmento BSS.

gcc -fno-common programma.c -o programma

Utile per rilevare multiple definizioni.

-fstack-protector

Protezione contro buffer overflow dello stack.

# Protezione base
gcc -fstack-protector programma.c -o programma

# Protezione per tutte le funzioni
gcc -fstack-protector-all programma.c -o programma

# Protezione forte (bilanciamento tra -all e base)
gcc -fstack-protector-strong programma.c -o programma

-D_FORTIFY_SOURCE

Ulteriore protezione contro buffer overflow (richiede -O1 o superiore).

gcc -O2 -D_FORTIFY_SOURCE=2 programma.c -o programma

10.3 Opzioni di Linking

-Wl,<opzioni>

Passa opzioni direttamente al linker.

# Specificare rpath
gcc programma.c -o programma -Wl,-rpath,/usr/local/lib

# Settare soname per libreria condivisa
gcc -shared -Wl,-soname,libmylib.so.1 mylib.o -o libmylib.so.1.0

# Strippare simboli di debug al linking
gcc programma.c -o programma -Wl,--strip-all

-nostdlib

Non linka la libreria standard C.

gcc -nostdlib -nostartfiles programma.c -o programma

Usato per kernel, bootloader, o embedded systems.

-static-libgcc / -static-libstdc++

Linka staticamente le librerie GCC/G++.

gcc -static-libgcc programma.c -o programma
g++ -static-libstdc++ programma.cpp -o programma

-rdynamic

Aggiunge tutti i simboli alla tabella dei simboli dinamici.

gcc -rdynamic programma.c -o programma -ldl

Utile per backtrace e plugin dinamici.

10.4 Caratteristiche del Processore

-msse, -msse2, -msse3, -msse4

Abilita istruzioni SSE specifiche.

gcc -msse4.2 programma.c -o programma

-mavx, -mavx2, -mavx512f

Abilita istruzioni AVX.

gcc -mavx2 programma.c -o programma

-mfpu=<fpu>

Specifica l’unità floating-point (ARM).

gcc -mfpu=neon programma.c -o programma

10.5 Preprocessore Condizionale e Macro

Macro Predefinite Utili

GCC definisce automaticamente diverse macro:

#include <stdio.h>

int main() {
    printf("GCC Version: %d.%d.%d\n", 
           __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
    
    printf("Standard C: %ld\n", __STDC_VERSION__);
    
    #ifdef __linux__
        printf("Sistema: Linux\n");
    #elif defined(__APPLE__)
        printf("Sistema: macOS\n");
    #elif defined(_WIN32)
        printf("Sistema: Windows\n");
    #endif
    
    printf("File: %s\n", __FILE__);
    printf("Linea: %d\n", __LINE__);
    printf("Data compilazione: %s\n", __DATE__);
    printf("Ora compilazione: %s\n", __TIME__);
    
    return 0;
}

Compilare:

gcc info.c -o info
./info

10.6 Attributi e Estensioni GCC

attribute((constructor)) / attribute((destructor))

Funzioni eseguite prima/dopo main.

#include <stdio.h>

__attribute__((constructor))
void before_main() {
    printf("Eseguito prima di main()\n");
}

__attribute__((destructor))
void after_main() {
    printf("Eseguito dopo main()\n");
}

int main() {
    printf("Dentro main()\n");
    return 0;
}

attribute((packed))

Strutture senza padding.

struct __attribute__((packed)) PackedStruct {
    char a;
    int b;
    char c;
};

attribute((aligned(N)))

Allineamento specifico.

int var __attribute__((aligned(16)));

attribute((unused))

Sopprime warning per variabili/parametri non usati.

void func(int x __attribute__((unused))) {
    // x non usato, ma nessun warning
}

11. Gestione delle Librerie

11.1 Librerie Statiche (.a)

Creazione di Libreria Statica

# File sorgenti
# mymath.c
// mymath.c
int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}
// mymath.h
#ifndef MYMATH_H
#define MYMATH_H

int add(int a, int b);
int subtract(int a, int b);

#endif

Creazione della libreria:

# Compilare in file oggetto
gcc -c mymath.c -o mymath.o

# Creare archivio statico
ar rcs libmymath.a mymath.o

# Verificare contenuto
ar -t libmymath.a

Uso della Libreria Statica

// main.c
#include <stdio.h>
#include "mymath.h"

int main() {
    printf("5 + 3 = %d\n", add(5, 3));
    printf("5 - 3 = %d\n", subtract(5, 3));
    return 0;
}

Compilazione:

# Metodo 1: Specificare la libreria
gcc main.c -L. -lmymath -o programma

# Metodo 2: Includere direttamente
gcc main.c libmymath.a -o programma

# Eseguire
./programma

11.2 Librerie Condivise/Dinamiche (.so, .dylib, .dll)

Creazione di Libreria Condivisa (Linux)

# Compilare con Position Independent Code
gcc -fPIC -c mymath.c -o mymath.o

# Creare libreria condivisa
gcc -shared mymath.o -o libmymath.so

# Con soname (versioning)
gcc -shared -Wl,-soname,libmymath.so.1 mymath.o -o libmymath.so.1.0.0

# Creare symlink
ln -s libmymath.so.1.0.0 libmymath.so.1
ln -s libmymath.so.1 libmymath.so

Creazione su macOS

gcc -fPIC -c mymath.c -o mymath.o
gcc -dynamiclib mymath.o -o libmymath.dylib

Creazione su Windows (MinGW)

gcc -shared mymath.c -o mymath.dll

Uso della Libreria Condivisa

# Compilare
gcc main.c -L. -lmymath -o programma

# Eseguire (Linux) - se non trova la libreria
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
./programma

# O installare in directory sistema
sudo cp libmymath.so /usr/local/lib/
sudo ldconfig

Su macOS:

export DYLD_LIBRARY_PATH=.:$DYLD_LIBRARY_PATH
./programma

Su Windows: assicurarsi che mymath.dll sia nella stessa directory dell’eseguibile o in una directory nel PATH.

Rpath - Embedded Library Path

# Compilare con rpath embedded
gcc main.c -L. -lmymath -Wl,-rpath,'$ORIGIN' -o programma

# O specificare percorso assoluto
gcc main.c -L. -lmymath -Wl,-rpath,/usr/local/lib -o programma

$ORIGIN significa “directory dell’eseguibile”.

11.3 Librerie Comuni

Libreria Matematica (libm)

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

int main() {
    printf("sqrt(16) = %.2f\n", sqrt(16.0));
    printf("sin(0) = %.2f\n", sin(0.0));
    printf("pow(2, 8) = %.0f\n", pow(2.0, 8.0));
    return 0;
}

Compilazione:

gcc programma.c -o programma -lm

Libreria Thread POSIX (libpthread)

#include <stdio.h>
#include <pthread.h>

void* thread_function(void* arg) {
    printf("Thread in esecuzione\n");
    return NULL;
}

int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    pthread_join(thread, NULL);
    return 0;
}

Compilazione:

gcc programma.c -o programma -lpthread

OpenSSL

#include <openssl/md5.h>
#include <stdio.h>
#include <string.h>

int main() {
    unsigned char digest[MD5_DIGEST_LENGTH];
    char string[] = "hello";
    
    MD5((unsigned char*)string, strlen(string), digest);
    
    printf("MD5 hash: ");
    for(int i = 0; i < MD5_DIGEST_LENGTH; i++)
        printf("%02x", digest[i]);
    printf("\n");
    
    return 0;
}

Compilazione:

gcc programma.c -o programma -lssl -lcrypto

12. Compilazione di Progetti Multi-file

12.1 Struttura Progetto Tipica

progetto/
├── include/
│   ├── util.h
│   └── calc.h
├── src/
│   ├── main.c
│   ├── util.c
│   └── calc.c
├── lib/
│   └── (librerie esterne)
├── obj/
│   └── (file oggetto temporanei)
└── Makefile

12.2 File di Esempio

include/calc.h

#ifndef CALC_H
#define CALC_H

int add(int a, int b);
int multiply(int a, int b);

#endif

include/util.h

#ifndef UTIL_H
#define UTIL_H

void print_result(const char* operation, int result);

#endif

src/calc.c

#include "calc.h"

int add(int a, int b) {
    return a + b;
}

int multiply(int a, int b) {
    return a * b;
}

src/util.c

#include <stdio.h>
#include "util.h"

void print_result(const char* operation, int result) {
    printf("%s = %d\n", operation, result);
}

src/main.c

#include <stdio.h>
#include "calc.h"
#include "util.h"

int main() {
    int a = 10, b = 5;
    
    int sum = add(a, b);
    print_result("10 + 5", sum);
    
    int product = multiply(a, b);
    print_result("10 * 5", product);
    
    return 0;
}

12.3 Compilazione Manuale

Metodo 1: Tutto in Un Comando

gcc -I./include src/main.c src/calc.c src/util.c -o programma

Metodo 2: Compilazione Separata (Raccomandato)

# Creare directory per oggetti
mkdir -p obj

# Compilare ogni file in oggetto
gcc -I./include -c src/main.c -o obj/main.o
gcc -I./include -c src/calc.c -o obj/calc.o
gcc -I./include -c src/util.c -o obj/util.o

# Linkare tutti gli oggetti
gcc obj/main.o obj/calc.o obj/util.o -o programma

# Eseguire
./programma

Vantaggi:

12.4 Makefile Base

Makefile

# Compilatore e flag
CC = gcc
CFLAGS = -Wall -Wextra -I./include -O2 -g
LDFLAGS = 

# Directory
SRC_DIR = src
OBJ_DIR = obj
INC_DIR = include

# File
SOURCES = $(wildcard $(SRC_DIR)/*.c)
OBJECTS = $(SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
TARGET = programma

# Regola principale
all: $(TARGET)

# Linking
$(TARGET): $(OBJECTS)
    $(CC) $(OBJECTS) -o $@ $(LDFLAGS)

# Compilazione
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
    $(CC) $(CFLAGS) -c $< -o $@

# Creare directory oggetti
$(OBJ_DIR):
    mkdir -p $(OBJ_DIR)

# Pulizia
clean:
    rm -rf $(OBJ_DIR) $(TARGET)

# Rebuild completo
rebuild: clean all

# Regole di debug
debug: CFLAGS += -DDEBUG -g3
debug: all

# Regole di release
release: CFLAGS += -O3 -DNDEBUG
release: all

.PHONY: all clean rebuild debug release

Uso:

# Compilare
make

# Compilare versione debug
make debug

# Compilare versione release ottimizzata
make release

# Pulire
make clean

# Rebuild completo
make rebuild

12.5 Makefile Avanzato con Dipendenze Automatiche

CC = gcc
CFLAGS = -Wall -Wextra -I./include -MMD -MP
LDFLAGS = -lm

SRC_DIR = src
OBJ_DIR = obj
INC_DIR = include

SOURCES = $(wildcard $(SRC_DIR)/*.c)
OBJECTS = $(SOURCES:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
DEPENDS = $(OBJECTS:.o=.d)
TARGET = programma

all: $(TARGET)

$(TARGET): $(OBJECTS)
    $(CC) $(OBJECTS) -o $@ $(LDFLAGS)

$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR)
    $(CC) $(CFLAGS) -c $< -o $@

$(OBJ_DIR):
    mkdir -p $(OBJ_DIR)

clean:
    rm -rf $(OBJ_DIR) $(TARGET)

-include $(DEPENDS)

.PHONY: all clean

L’opzione -MMD -MP genera automaticamente le dipendenze dagli header file.


13. Utilizzi Avanzati

13.1 Cross-Compilation

Compilare per una piattaforma diversa da quella di sviluppo.

Installare Toolchain Cross-Compilation

# Per ARM su Ubuntu
sudo apt install gcc-arm-linux-gnueabihf

# Per Windows su Linux (MinGW)
sudo apt install mingw-w64

# Per architetture multiple
sudo apt install gcc-multilib g++-multilib

Esempio Cross-Compilation

# Compilare per ARM
arm-linux-gnueabihf-gcc programma.c -o programma_arm

# Compilare per Windows da Linux
x86_64-w64-mingw32-gcc programma.c -o programma.exe

# Compilare per 32-bit su sistema 64-bit
gcc -m32 programma.c -o programma32

Variabili d’Ambiente per Cross-Compilation

# Impostare compilatore e flag
export CC=arm-linux-gnueabihf-gcc
export CXX=arm-linux-gnueabihf-g++
export CFLAGS="-march=armv7-a"

# Poi usare normalmente
$CC programma.c -o programma

13.2 Preprocessore Personalizzato

Header Guards Automatici

// myheader.h
#ifndef MYHEADER_H
#define MYHEADER_H

// Contenuto header

#endif /* MYHEADER_H */

Oppure usare #pragma once (non standard ma ampiamente supportato):

// myheader.h
#pragma once

// Contenuto header

Compilazione Condizionale Multi-piattaforma

#include <stdio.h>

int main() {
    #ifdef _WIN32
        printf("Windows\n");
    #elif defined(__linux__)
        printf("Linux\n");
    #elif defined(__APPLE__) && defined(__MACH__)
        printf("macOS\n");
    #else
        printf("Sistema sconosciuto\n");
    #endif
    
    return 0;
}

Debug vs Release

#include <stdio.h>

#ifdef DEBUG
    #define LOG(msg) printf("[DEBUG] %s:%d - %s\n", __FILE__, __LINE__, msg)
#else
    #define LOG(msg)
#endif

int main() {
    LOG("Inizio programma");
    printf("Hello, World!\n");
    LOG("Fine programma");
    return 0;
}

Compilazione:

# Versione debug
gcc -DDEBUG programma.c -o programma_debug

# Versione release
gcc programma.c -o programma_release

13.3 Inline Assembly

GCC permette di inserire codice assembly direttamente nel C.

Sintassi Basic

#include <stdio.h>

int main() {
    int input = 5;
    int output;
    
    // Inline assembly AT&T syntax (Linux)
    asm("movl %1, %%eax\n\t"
        "addl $10, %%eax\n\t"
        "movl %%eax, %0"
        : "=r" (output)
        : "r" (input)
        : "%eax");
    
    printf("Input: %d, Output: %d\n", input, output);
    return 0;
}

Sintassi Intel

int main() {
    int a = 10, b = 20, sum;
    
    asm volatile(
        ".intel_syntax noprefix\n\t"
        "mov eax, %1\n\t"
        "add eax, %2\n\t"
        "mov %0, eax\n\t"
        ".att_syntax prefix"
        : "=r" (sum)
        : "r" (a), "r" (b)
        : "eax"
    );
    
    printf("Somma: %d\n", sum);
    return 0;
}

13.4 Utilizzo di Plugin GCC

GCC supporta plugin per estenderne le funzionalità.

Scrivere un Plugin Semplice

// myplugin.c
#include "gcc-plugin.h"
#include "plugin-version.h"

int plugin_is_GPL_compatible;

int plugin_init(struct plugin_name_args *plugin_info,
                struct plugin_gcc_version *version) {
    printf("Plugin caricato!\n");
    return 0;
}

Compilazione del plugin:

gcc -I$(gcc -print-file-name=plugin)/include \
    -fPIC -shared myplugin.c -o myplugin.so

Uso del plugin:

gcc -fplugin=./myplugin.so programma.c -o programma

13.5 Coverage del Codice

Misurare quale codice viene eseguito durante i test.

# Compilare con coverage
gcc -fprofile-arcs -ftest-coverage programma.c -o programma

# Eseguire il programma
./programma

# Generare report (lcov)
gcov programma.c

# Visualizzare
cat programma.c.gcov

Output mostra numero di esecuzioni per ogni linea.

Per report HTML:

# Installare lcov
sudo apt install lcov

# Generare dati
lcov --capture --directory . --output-file coverage.info

# Generare HTML
genhtml coverage.info --output-directory coverage_html

# Aprire nel browser
firefox coverage_html/index.html

14. Esempi Pratici Completi

14.1 Programma con Libreria Matematica

// scientific_calc.c
#include <stdio.h>
#include <math.h>

#define PI 3.14159265358979323846

int main() {
    double angle = 45.0;
    double radians = angle * PI / 180.0;
    
    printf("Angolo: %.2f gradi\n", angle);
    printf("Seno: %.4f\n", sin(radians));
    printf("Coseno: %.4f\n", cos(radians));
    printf("Tangente: %.4f\n", tan(radians));
    
    double x = 2.0;
    printf("\nEsponenziale e^%.1f = %.4f\n", x, exp(x));
    printf("Logaritmo naturale ln(%.1f) = %.4f\n", x, log(x));
    printf("Radice quadrata sqrt(%.1f) = %.4f\n", x, sqrt(x));
    
    return 0;
}

Compilazione:

gcc scientific_calc.c -o scientific_calc -lm -Wall -O2
./scientific_calc

14.2 Programma Multi-Thread

// parallel_sum.c
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

#define NUM_THREADS 4
#define ARRAY_SIZE 1000000

typedef struct {
    int *array;
    int start;
    int end;
    long long partial_sum;
} ThreadData;

void* compute_partial_sum(void* arg) {
    ThreadData* data = (ThreadData*)arg;
    data->partial_sum = 0;
    
    for (int i = data->start; i < data->end; i++) {
        data->partial_sum += data->array[i];
    }
    
    return NULL;
}

int main() {
    int *array = malloc(ARRAY_SIZE * sizeof(int));
    pthread_t threads[NUM_THREADS];
    ThreadData thread_data[NUM_THREADS];
    
    // Inizializzare array
    for (int i = 0; i < ARRAY_SIZE; i++) {
        array[i] = i + 1;
    }
    
    // Dividere lavoro tra thread
    int chunk_size = ARRAY_SIZE / NUM_THREADS;
    
    for (int i = 0; i < NUM_THREADS; i++) {
        thread_data[i].array = array;
        thread_data[i].start = i * chunk_size;
        thread_data[i].end = (i == NUM_THREADS - 1) ? 
                             ARRAY_SIZE : (i + 1) * chunk_size;
        
        pthread_create(&threads[i], NULL, 
                      compute_partial_sum, &thread_data[i]);
    }
    
    // Attendere completamento
    long long total_sum = 0;
    for (int i = 0; i < NUM_THREADS; i++) {
        pthread_join(threads[i], NULL);
        total_sum += thread_data[i].partial_sum;
    }
    
    printf("Somma totale: %lld\n", total_sum);
    printf("Formula: %lld\n", (long long)ARRAY_SIZE * (ARRAY_SIZE + 1) / 2);
    
    free(array);
    return 0;
}

Compilazione:

gcc parallel_sum.c -o parallel_sum -lpthread -Wall -O2
./parallel_sum

14.3 Gestione File con Error Handling

// file_manager.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define BUFFER_SIZE 1024

int copy_file(const char* source, const char* dest) {
    FILE *src = fopen(source, "rb");
    if (src == NULL) {
        fprintf(stderr, "Errore apertura %s: %s\n", 
                source, strerror(errno));
        return -1;
    }
    
    FILE *dst = fopen(dest, "wb");
    if (dst == NULL) {
        fprintf(stderr, "Errore apertura %s: %s\n", 
                dest, strerror(errno));
        fclose(src);
        return -1;
    }
    
    char buffer[BUFFER_SIZE];
    size_t bytes;
    
    while ((bytes = fread(buffer, 1, BUFFER_SIZE, src)) > 0) {
        if (fwrite(buffer, 1, bytes, dst) != bytes) {
            fprintf(stderr, "Errore scrittura: %s\n", strerror(errno));
            fclose(src);
            fclose(dst);
            return -1;
        }
    }
    
    if (ferror(src)) {
        fprintf(stderr, "Errore lettura: %s\n", strerror(errno));
        fclose(src);
        fclose(dst);
        return -1;
    }
    
    fclose(src);
    fclose(dst);
    return 0;
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "Uso: %s <sorgente> <destinazione>\n", argv[0]);
        return EXIT_FAILURE;
    }
    
    printf("Copiando %s -> %s...\n", argv[1], argv[2]);
    
    if (copy_file(argv[1], argv[2]) == 0) {
        printf("Copia completata con successo!\n");
        return EXIT_SUCCESS;
    } else {
        fprintf(stderr, "Copia fallita!\n");
        return EXIT_FAILURE;
    }
}

Compilazione:

gcc file_manager.c -o file_manager -Wall -Wextra -O2

# Test
echo "Test content" > test.txt
./file_manager test.txt test_copy.txt
cat test_copy.txt

14.4 Programma con Gestione Argomenti (getopt)

// cmdline_tool.c
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>

void print_usage(const char* program_name) {
    printf("Uso: %s [opzioni]\n", program_name);
    printf("Opzioni:\n");
    printf("  -h, --help          Mostra questo help\n");
    printf("  -v, --verbose       Modalità verbose\n");
    printf("  -o, --output FILE   File di output\n");
    printf("  -n, --number NUM    Numero di iterazioni\n");
}

int main(int argc, char *argv[]) {
    int verbose = 0;
    char *output_file = NULL;
    int iterations = 10;
    
    static struct option long_options[] = {
        {"help",    no_argument,       0, 'h'},
        {"verbose", no_argument,       0, 'v'},
        {"output",  required_argument, 0, 'o'},
        {"number",  required_argument, 0, 'n'},
        {0, 0, 0, 0}
    };
    
    int opt;
    while ((opt = getopt_long(argc, argv, "hvo:n:", 
                              long_options, NULL)) != -1) {
        switch (opt) {
            case 'h':
                print_usage(argv[0]);
                return EXIT_SUCCESS;
            case 'v':
                verbose = 1;
                break;
            case 'o':
                output_file = optarg;
                break;
            case 'n':
                iterations = atoi(optarg);
                break;
            default:
                print_usage(argv[0]);
                return EXIT_FAILURE;
        }
    }
    
    if (verbose) {
        printf("Modalità verbose attiva\n");
        printf("Iterazioni: %d\n", iterations);
        if (output_file)
            printf("Output file: %s\n", output_file);
    }
    
    // Logica principale del programma
    for (int i = 0; i < iterations; i++) {
        if (verbose)
            printf("Iterazione %d/%d\n", i + 1, iterations);
    }
    
    printf("Completato!\n");
    return EXIT_SUCCESS;
}

Compilazione e uso:

gcc cmdline_tool.c -o cmdline_tool -Wall

# Test varie opzioni
./cmdline_tool --help
./cmdline_tool -v -n 5
./cmdline_tool --verbose --output result.txt --number 3

15. Troubleshooting Comune

15.1 Errori di Compilazione

Undefined reference to ‘funzione’

Problema: Funzione dichiarata ma non implementata o libreria non linkata.

Soluzione:

# Se manca implementazione, aggiungere il file .c
gcc main.c funzione.c -o programma

# Se manca libreria (es. math)
gcc programma.c -o programma -lm

# Se libreria in directory custom
gcc programma.c -L/path/to/lib -lmylib -o programma

Cannot find -l<library>

Problema: Libreria non trovata.

Soluzione:

# Installare la libreria di sviluppo
sudo apt install lib<nome>-dev

# O specificare percorso
gcc programma.c -L/usr/local/lib -lmylib -o programma

# Verificare se libreria esiste
ldconfig -p | grep <library>

Header file not found

Problema: File header non trovato.

Soluzione:

# Specificare directory include
gcc -I/path/to/headers programma.c -o programma

# Per progetti complessi
gcc -I./include -I/usr/local/include programma.c -o programma

15.2 Errori Runtime

Segmentation Fault

Debug:

# Compilare con debug symbols
gcc -g programma.c -o programma

# Usare GDB
gdb ./programma
(gdb) run
(gdb) backtrace
(gdb) quit

# Oppure usare AddressSanitizer
gcc -fsanitize=address -g programma.c -o programma
./programma

Memory Leaks

Debug:

# Usare Valgrind
sudo apt install valgrind
valgrind --leak-check=full ./programma

# O usare LeakSanitizer
gcc -fsanitize=leak -g programma.c -o programma
./programma

Undefined Behavior

Debug:

# UndefinedBehaviorSanitizer
gcc -fsanitize=undefined -g programma.c -o programma
./programma

15.3 Problemi di Linking Librerie Condivise

error while loading shared libraries

Problema: Libreria condivisa non trovata a runtime.

Soluzione:

# Metodo 1: Aggiungere a LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH

# Metodo 2: Configurare ldconfig
sudo echo "/path/to/lib" > /etc/ld.so.conf.d/mylib.conf
sudo ldconfig

# Metodo 3: Usare rpath
gcc programma.c -L. -lmylib -Wl,-rpath,'$ORIGIN/lib' -o programma

15.4 Warning Comuni

Implicit declaration of function

Problema: Funzione usata senza prototipo.

Soluzione:

// Aggiungere #include appropriato
#include <string.h>  // Per strlen, strcpy, etc.
#include <stdlib.h>  // Per malloc, free, etc.
#include <math.h>    // Per sqrt, sin, cos, etc.

Comparison between signed and unsigned

Problema: Confronto tra signed e unsigned.

Soluzione:

// Cast esplicito
size_t size = strlen(str);
for (int i = 0; i < (int)size; i++) { ... }

// O usare tipo corretto
for (size_t i = 0; i < size; i++) { ... }

15.5 Ottimizzazione e Performance

Codice troppo lento

Debug:

# Compilare con profiling
gcc -pg -O2 programma.c -o programma

# Eseguire
./programma

# Analizzare con gprof
gprof programma gmon.out > analysis.txt
less analysis.txt

Eseguibile troppo grande

Soluzione:

# Strippare simboli di debug
strip programma

# O compilare senza debug
gcc -Os programma.c -o programma

# Link Time Optimization
gcc -flto -Os programma.c -o programma

Conclusioni

Questa guida ha coperto GCC dalle basi all’utilizzo avanzato:

  1. Installazione su tutti i principali sistemi operativi
  2. Compilazione base per diversi linguaggi
  3. Opzioni fondamentali per controllo del processo di compilazione
  4. Ottimizzazione per massimizzare le prestazioni
  5. Debug e testing con sanitizers e strumenti di analisi
  6. Gestione librerie statiche e condivise
  7. Progetti multi-file con Makefile
  8. Utilizzi avanzati inclusi cross-compilation, inline assembly, coverage

GCC è uno strumento estremamente potente e flessibile. La padronanza delle sue opzioni e caratteristiche permette di:

Risorse Aggiuntive

Comandi Quick Reference

# Compilazione base
gcc programma.c -o programma

# Con warning e ottimizzazione
gcc -Wall -Wextra -O2 programma.c -o programma

# Debug completo
gcc -g -Wall -Wextra -fsanitize=address,undefined programma.c -o programma

# Release ottimizzata
gcc -O3 -DNDEBUG -Wall -Wextra programma.c -o programma

# Libreria condivisa
gcc -fPIC -shared mylib.c -o libmylib.so

# Multi-file
gcc -I./include src/*.c -o programma -lm

# Cross-compilation
arm-linux-gnueabihf-gcc programma.c -o programma_arm

Buon coding!