Risultati da 1 a 4 di 4

Discussione: [C++] domande niubbe!

  1. #1
    Il Puppies L'avatar di Zen-Zen
    Data Registrazione
    01-06-05
    Località
    Sotto al satellite.
    Messaggi
    761

    Predefinito [C++] domande niubbe!

    Ciao a tutti, apro questo topic per poter fare domande niubbe di varia natura sul C++ che mi sto accingendo a studiare!

    inizio con questo:

    il mio programma è composto da 3 file:

    - funzione.h è il file header dove dichiaro le funzioni.

    - funzione.cpp è il file dove implemento le funzioni dichiarate in funzione.h

    - test.cpp è il file dove c'è il main e faccio il test delle funzioni

    ora la mia domanda è: se sia in funzione.cpp che test.cpp all'inizio devo scrivere "#include <cmath>", è corretto farlo? ci sono problemi di duplicazione? perchè il compilatore compila e il programma viene eseguito mentre se lo tolgo in funzione.cpp ho errore in compilazione...non è strano? pensavo che se sia cmath che funzione.h sono inclusi in test.cpp non fosse necessario includerlo di nuovo per eseguire il codice di funzione.cpp...

    e poi l'ordine corretto con cui inserire le istruzioni "#include" quale sarebbe? prima includo quelle della libreria standard e poi i miei header oppure il contrario?

    ok, per ora è tutto...grazie a chi risponderà

  2. #2
    Lo Zio L'avatar di Edward Gein
    Data Registrazione
    18-11-01
    Località
    Steuerland
    Messaggi
    2,837

    Predefinito Re: [C++] domande niubbe!

    Ok, cercherò di non essere tecnico e fare un esempio facile. Volevo fare una rappresentazione tramite grafi, ma cominciamo ragionando al contrario per spiegarti il funzionamento di un compilatore C/C++. Hai un albero, e il tuo file .cpp è il tronco. Da questo tronco partono un certo numero di rami, e ogni ramo è un file che viene importato nel .cpp con la direttiva #include. A sua volta, da ognuno di questi rami possono partire altri piccoli rami (sempre associati ai file importati con include).

    Il compilatore lavora sul tronco. Nella fase di pre-processing, tutti i rami vengono seguiti e processati (semplificando di molto: immagina che ogni volta che vedi "#include <file.h>" il contenuto di file.h venga copia/incollato al posto di quella linea). Dopo aver finito di processare tutto, il parser vero e proprio entra in azione, il codice viene tradotto, e alla fine ti ritrovi con un file oggetto.

    Vediamo se me la cavo ancora con l'ascii art:
    Codice:
    a1.h     a2.h
     |         |
     |         |
     +-> b.h <-+      d.h
          |            |
          |            |
          +--> c.h <---+
                |
                |
             file.cpp
    In questo esempio, il compilatore parte da file.cpp, e ad un certo punto trova la direttiva #include "c.h". Quindi sostituisce la riga #include "c.h" con il contenuto del file c.h. Il file c.h a sua volta contiene due direttive: #include b.h e #include d.h, che vengono rimpiazzate dal contenuto dei file b.h e c.h rispettivamente. E così via.

    Prendiamo il tuo caso particolare:
    Codice:
    cmath        funzione.h     cmath        funzione.h
      |              |            |              |
      |              |            |              |
      +------+-------+            +------+-------+
             |                           |
             |                           |
        funzione.cpp                  test.cpp
    Per ogni "albero", l'ordine di inclusione è da sinistra verso destra, il livello di inclusione è dall'alto verso il basso.
    Il compilatore processa funzione.cpp, nella cui implementazione definisci le funzioni dichiarate in funzione.h e sicuramente usi funzioni matematiche presenti nella libreria math. Una volta processato funzione.cpp e prodotto il file oggetto, il compilatore si "scorda" di quello che ha già visto.
    Poi passa a processare test.cpp e ancora una volta include cmath e funzione.h, e genera un file oggetto.
    Il linker si occupa di mettere insieme questi due file oggetto. In particolare, nell'file test.obj ci sarà uno o più simboli con dei nomi uguali a quelli dichiarati in funzione.h, ma senza l'implementazione. Ci penserà il linker a risolvere il problema aggiungendo il corpo dei simboli che si trova in funzione.obj.

    Compilatore e linker operano dunque separatamente.
    Se tu rimuovi la direttiva #include <cmath> da funzione.cpp, il compilatore (che idealmente opera su un file .cpp alla volta) troverà dei simboli che non riconosce, ad esempio sinf() o cosf(). Da lì procede in 2 modi: o ti dice "non trovo la dichiarazione di sinf()" e interrompe la compilazione, o gli assegna una dichiarazione implicita (un compilatore c assume che le funzioni non dichiarate restituiscano int). Nel primo caso hai un errore in fase di compilazione, nel secondo in fase di link (perché in realtà sinf() restituisce un float, e dunque il simbolo all'interno di test.obj non è lo stesso simbolo che il compilatore ha inserito in funzione.cpp).

    Spero di essere stato chiaro.

  3. #3
    Il Puppies L'avatar di Zen-Zen
    Data Registrazione
    01-06-05
    Località
    Sotto al satellite.
    Messaggi
    761

    Predefinito Re: [C++] domande niubbe!

    innanzitutto ti ringrazio per la spiegazione!

    è chiaro, però mi rimangono ancora dei dubbi:

    1) se ogni file .cpp e i relativi file header importati vengono compilati a sè, non capisco a cosa serva inserire all'inizio dei file.h le direttive #ifndef file_h, #define file_h, ecc

    2) l'ordine di inclusione hai detto è da sinistra a destra, però, da quello che ho capito, di fatto l'ho decido io in base a quale direttiva #include metto prima...allora la mia domanda è: è sempre meglio includere prima i file che appartengono alla libreria standard e poi i miei file, oppure non fa differenza?

  4. #4
    Lo Zio L'avatar di Edward Gein
    Data Registrazione
    18-11-01
    Località
    Steuerland
    Messaggi
    2,837

    Predefinito Re: [C++] domande niubbe!

    1) Le direttive #ifdef servono perché a volte un singolo file .h è a sua volta incluso in altri file.h. Ad esempio, hai 2 file "B.h" e "C.h" che dichiarano le classi B e C come sottoclassi di A, dichiarata in un "A.h". Se includi entrambi nello stesso .cpp, hai bisogno della include guard in A.h.

    Nel mondo ideale, il subset di header in comune tra le varie unità di compilazione è ridotto al minimo possibile, cioè sarebbe bello avere in comune solo header che vengono modificati raramente o mai (tipo di librerie standard). Cambiare un header comporta la ricompilazione di tutte le unità che lo includono. Ma d'altronde si sa, viviamo in una valle di lacrime.

    2) Di norma, io includo prima gli header delle librerie standard, poi i file relativi al progetto.

Permessi di Scrittura

  • Tu non puoi inviare nuove discussioni
  • Tu non puoi inviare risposte
  • Tu non puoi inviare allegati
  • Tu non puoi modificare i tuoi messaggi
  • Il codice BB è Attivato
  • Le faccine sono Attivato
  • Il codice [IMG] è Attivato
  • Il codice HTML è Disattivato