[C] gcc non compila
Salve, sto appena imparando il C. Ho scritto un programma in questo linguaggio ma il compilatore non me lo compila, restituendomi un messaggio che non riesco a capire:
Il programma è questo:
Sapete spiegarmi dove sta l'errore?
gcc -o energia.exe energia.c energia.c:6:1: warning: return type defaults to ‘int’ [-Wimplicit-int] main(){ ^~~~ /tmp/ccOSaN4b.o: nella funzione "main": energia.c:(.text+0xe7): riferimento non definito a "pow" energia.c:(.text+0x135): riferimento non definito a "pow" collect2: error: ld returned 1 exit status
Il programma è questo:
#include <stdio.h> #include <math.h> #define G 9.81 /* Questo programma calcola i valori dell'energia cinetica T = 1/2mv^2 e dell'energia potenziale U = mhg in funzione del tempo di un punto materiale di massa m soggetto a una gravità. Le grandezze sono quelle del sistema metrico internazionale. */ main(){ double m; /* massa del punto materiale */ double v0; /* velocità iniziale del punto materiale */ double h0; /* quota iniziale */ double t; /* tempo */ double U, T; /* energia potenziale e cinetica */ double v; /* velocità in funzione del tempo */ double h; /* quota in funzione del tempo */ printf ("Inserisci la massa del punto materiale espressa in kg: "); scanf ("%lf", &m); printf ("\n Inserisci la velocità iniziale espressa in m/s: "); scanf ("%lf", &v0); printf ("\n Inserisci la quota iniziale in m: "); scanf ("%lf", &h0); /* calcola v e h */ v = v0 - G*t; h = h0 + v0*t + G / 2. * pow(t, 2.0); /* calcola i valori di T e U */ T = m / 2.* pow(v, 2.0); U = m*G*h; printf ("\n L'energia cinetica è %f J", T); printf ("\n L'energia potenziale è %f J", U); }
Sapete spiegarmi dove sta l'errore?
Risposte
La compilazione va a buon fine, ma il linker non trova il riferimento alla funzione pow().
Quando usi funzioni dall'header con GCC, devi specificare esplicitamente la libreria "m" (per "math") passando come argomento "-lm" (elle minuscola seguita dalla libreria, in questo caso "m").
Quando usi funzioni dall'header
gcc -o energia.exe -lm energia.c
gcc -o energia.exe -lm energia.c energia.c:6:1: warning: return type defaults to ‘int’ [-Wimplicit-int] main(){ ^~~~ /tmp/ccRJFsHc.o: nella funzione "main": energia.c:(.text+0xe7): riferimento non definito a "pow" energia.c:(.text+0x135): riferimento non definito a "pow" collect2: error: ld returned 1 exit status
purtroppo mi restituisce lo stesso messaggio

Questo è uno scherzo che fa [inline]gcc[/inline]. Prova con
cioè mettendo [inline]-lm[/inline] dopo il nome del file sorgente
gcc -o energia.exe energia.c -lm
cioè mettendo [inline]-lm[/inline] dopo il nome del file sorgente
Tra parentesi, meglio andare sul sicuro e usare una compilazione più puntigliosa: usa
finché stai iniziando.
gcc -o energia.exe -Wall -Wextra -Werror -pedantic energia.c -lm
finché stai iniziando.
E poi ovviamente manca l'int prima del main... Ho l'impressione che tu stia imparando C su un libro pre-standard (cioè pubblicato prima del 1989).
"Raptorista":
Prova [...] mettendo [inline]-lm[/inline] dopo il nome del file sorgente
Grazie della correzione, dimenticavo che l'ordine degli argomenti è importante in questo caso.
"Raptorista":
Tra parentesi, meglio andare sul sicuro e usare una compilazione più puntigliosa: usa
gcc -o energia.exe -Wall -Wextra -Werror -pedantic energia.c -lm
finché stai iniziando.
Aggiungerei anche [inline]-std=c11[/inline], dato che di default GCC usa estensioni fuori standard.
"vict85":
E poi ovviamente manca l'int prima del main... Ho l'impressione che tu stia imparando C su un libro pre-standard (cioè pubblicato prima del 1989).
Il libro è del 2006.
Sono riuscito a creare il file .exe scrivendo -lm alla fine. Grazie mille per l'aiuto.
Vorrei sapere a che servono le opzioni -Wall -Wextra -Werror -pedantic
Quelle opzioni servono a dire al compilatore di segnalare tutti i warning, anche quelli extra, segnalarteli come errori e di essere pedante nel seguire lo standard. In pratica ti aiutano a trovare errori nel tuo codice che altrimenti non vedresti. Per esempio, ce n'è uno che io vedo e che quelle flag fanno saltare fuori.
"UeCiccio":
[quote="vict85"]E poi ovviamente manca l'int prima del main... Ho l'impressione che tu stia imparando C su un libro pre-standard (cioè pubblicato prima del 1989).
Il libro è del 2006.
Sono riuscito a creare il file .exe scrivendo -lm alla fine. Grazie mille per l'aiuto.
Vorrei sapere a che servono le opzioni -Wall -Wextra -Werror -pedantic[/quote]
Il punto è che quel codice sembra vecchio e ritengo che sia colpa del libro. Le variabili possono ormai essere definite in qualsiasi punto del codice e ci sono reali vantaggi a definirle solo quanto servono. Inoltre è utile sempre inizializzare le variabili a qualche valore quando le definisci. Questi due piccoli accorgimenti rendono il codice più leggibile, più facile da mantenere ed eliminano il rischio di alcuni errori insidiosi.
Inoltre, a mio avviso, la funzione [inline]pow[/inline] non andrebbe insegnata. Infatti, non ha alcun senso usare [inline]pow[/inline] per calcolare potenze basse, calcolabili con uno o due prodotti. E spesso viene usata all'interno di cicli, anche se sarebbe possibile calcolare la nuova potenza a partire dalla vecchia con una singola moltiplicazione.
"vict85":
Il punto è che quel codice sembra vecchio e ritengo che sia colpa del libro. Le variabili possono ormai essere definite in qualsiasi punto del codice e ci sono reali vantaggi a definirle solo quanto servono. Inoltre è utile sempre inizializzare le variabili a qualche valore quando le definisci. Questi due piccoli accorgimenti rendono il codice più leggibile, più facile da mantenere ed eliminano il rischio di alcuni errori insidiosi.
Inoltre, a mio avviso, la funzione [inline]pow[/inline] non andrebbe insegnata. Infatti, non ha alcun senso usare [inline]pow[/inline] per calcolare potenze basse, calcolabili con uno o due prodotti. E spesso viene usata all'interno di cicli, anche se sarebbe possibile calcolare la nuova potenza a partire dalla vecchia con una singola moltiplicazione.
Se ho capito bene dovrei scrivere il programma in questa maniera:
#include <stdio.h> #include <math.h> #define G 9.81 // Questo programma calcola i valori dell'energia cinetica K = 1/2mv^2 e dell'energia potenziale U = mhg in funzione del tempo di un punto materiale di massa m soggetto a una gravità. Le grandezze sono quelle del sistema metrico internazionale. */ int main(){ double m = 0; // massa del punto materiale double v0 = 0; // velocità iniziale del punto materiale double h0 = 0; // quota iniziale double t = 0; // tempo printf (" Inserisci la massa del punto materiale espressa in kg: "); scanf ("%lf", &m); printf ("\n Inserisci la velocità iniziale espressa in m/s (verso positivo verso l'alto) : "); scanf ("%lf", &v0); printf ("\n Inserisci la quota iniziale in m: "); scanf ("%lf", &h0); printf ("\n Inserisci l'istante di tempo in s nel quale vuoi conoscere i valori dell'energia cinetica e dell'energia potenziale: "); scanf ("%lf", &t); //calcola v e h double v = 0; // velocità in funzione del tempo double h = 0; // quota in funzione del tempo v = v0 - G*t; h = h0 + v0*t - G/2. * t*t; /* calcola i valori di K e U */ double U = 0, K = 0; // energia potenziale e cinetica K = m/2. * v*v; U = m*G*h; printf ("\n L'energia cinetica all'istante t = %f s è %f J", t, K); printf ("\n L'energia potenziale all'istante t = %f s è %f J \n", t, U); }
"UeCiccio":
Se ho capito bene dovrei scrivere il programma in questa maniera:
Non proprio. Stai inizializzando le variabili a zero, per poi sovrascriverle immediatamente con il valore che vuoi assegnare loro.
//calcola v e h double v = 0; // velocità in funzione del tempo double h = 0; // quota in funzione del tempo v = v0 - G*t; h = h0 + v0*t - G/2. * t*t;
Invece dovresti direttamente inizializzarle con quel valore.
//calcola v e h double v = v0 - G*t; // velocità in funzione del tempo double h = h0 + v0*t - G/2. * t*t; // quota in funzione del tempo
Fanno eccezione le varibili che chiedi all'utente. Quelle potresti forse lasciarle non inizializzate.
"UeCiccio":
Se ho capito bene dovrei scrivere il programma in questa maniera:
Più o meno. Intendevo come dice Claudio. Risulta comunque importante anche controllare ciò che ricevi come input, anche se non è essenziale con i programmi giocattolo che fai ora. Ho messo la lettura delle variabili in funzioni a parte e controllato che la massa sia non negativa:
#include <stdio.h> #include <math.h> #define G ( 9.81 ) double leggi_massa( ); double leggi_velocita_iniziale( ); double leggi_quota_iniziale( ); double leggi_tempo( ); void clear_stdin( ); /* * Questo programma calcola i valori dell'energia cinetica K = 1/2mv^2 e dell'energia potenziale * U = mhg in funzione del tempo di un punto materiale di massa m soggetto a una gravita'. Le * grandezze sono quelle del sistema metrico internazionale. */ int main( ) { // massa del punto materiale double const m = leggi_massa( ); printf( "\n" ); // velocita' iniziale del punto materiale double const v0 = leggi_velocita_iniziale( ); printf( "\n" ); // quota iniziale double const h0 = leggi_quota_iniziale( ); printf( "\n" ); // tempo double const t = leggi_tempo( ); printf( "\n" ); // calcola v e h // velocità in funzione del tempo double const v = v0 - G * t; // quota in funzione del tempo double const h = h0 + t * ( v0 - 0.5 * G * t ); // calcola i valori di K e U // energia cinetica double const K = 0.5 * m * v * v; // energia potenziale double const U = m * G * h; printf( "L'energia cinetica all'istante t = %g s e' %g J\n", t, K ); printf( "L'energia potenziale all'istante t = %g s e' %g J \n", t, U ); } double leggi_massa( ) { double m; printf( "Inserisci la massa del punto materiale espressa in kg: " ); if ( scanf( "%lf", &m ) == 0 || m < 0 ) { clear_stdin( ); printf( "Errore nella lettura della massa. Riprovare.\n" ); return leggi_massa( ); } return m; } double leggi_velocita_iniziale( ) { double v; printf( "Inserisci la velocita' iniziale espressa in m/s (verso positivo verso l'alto) : " ); if ( scanf( "%lf", &v ) == 0 ) { clear_stdin( ); printf( "Errore nella lettura della velocita' iniziale. Riprovare.\n" ); return leggi_velocita_iniziale( ); } return v; } double leggi_quota_iniziale( ) { double h; printf( "Inserisci la quota iniziale in m: " ); if ( scanf( "%lf", &h ) == 0 ) { clear_stdin( ); printf( "Errore nella lettura della quota iniziale. Riprovare.\n" ); return leggi_quota_iniziale( ); } return h; } double leggi_tempo( ) { double t; printf( "Inserisci l'istante di tempo in s nel quale vuoi conoscere i valori dell'energia " "cinetica e dell'energia potenziale: " ); if ( scanf( "%lf", &t ) == 0 ) { clear_stdin( ); printf( "Errore nella lettura del tempo. Riprovare.\n" ); return leggi_tempo( ); } return t; } void clear_stdin( ) { int c; while ( ( c = getchar( ) ) != '\n' && c != EOF ) { } }
La funzione [inline]clear_stdin[/inline] serve a gestire il caso in cui inserisci qualcosa che [inline]scanf[/inline] non sa tradurre in double e rimane bloccato. Alcuni libri ti consigliano [inline]fflush(stdin)[/inline] per farlo ma non è un modo portabile per farlo. Lo standard dice infatti che è un undefined behaviour (ovvero il compilatore può generare il codice che preferisce, dall'eliminare la chiamata alla funzione a farti la stessa cosa che ho scritto in [inline]clear_stdin[/inline]). Quindi non usare mai [inline]fflush[/inline] con [inline]stdin[/inline].
Un piccolo commento a parte: i caratteri accentati non sono visualizzati bene nella console. Quindi non scriverli nei printf.
Essenzialmente qual è la differenza tra lo scrivere [inline]#define G (9.81)[/inline] e [inline]double G = 9.81[/inline]?
"UeCiccio":
Essenzialmente qual è la differenza tra lo scrivere [inline]#define G (9.81)[/inline] e [inline]double G = 9.81[/inline]?
Il secondo definisce una variabile di nome [inline]G[/inline] e gli assegna il valore [inline]9.81[/inline] . Se la variabile è costante e il compilatore capisce che può eliminarla, lo fa. Ovvero, non è detto che nell'esecuzione del codice a [inline]G[/inline] sia assegnata una qualche posizione in memoria.
Per quanto riguarda [inline]#define[/inline] è un po' più difficile da spiegare perché dovresti comprendere come viene compilato il codice. In pratica, prima che il compilatore inizi a interpretare il codice, fa alcune operazioni preliminari. Durante queste operazioni, esegue le istruzioni del preprocessore (oltre a eliminari spazi inutili e commenti).
Vediamo cosa fanno le istruzioni principali:
- [*:db0haisf] #include "filename" copia l'intero contenuto di [inline]filename[/inline] all'interno del file che contiene l'istruzione.[/*:m:db0haisf]
[*:db0haisf] #define nome valore definisce una regola di sostituzione, ovvero dice al compilatore di sostituire ogni ripetizione di [inline]nome[/inline] con [inline]valore[/inline][/*:m:db0haisf]
[*:db0haisf] #if cond1 codice1 #elseif cond2 codice2 #else codice3 #endif serve a selezionare il codice giusto a seconda di alcune condizioni. Le altre opzioni sono eliminate.[/*:m:db0haisf]
[*:db0haisf] #pragma serve a dare istruzioni al compilatore. Le istruzioni possibili dipendono dal compilatore anche se alcune come [inline]once[/inline] sono disponibili su quasi tutti i sistemi. [/*:m:db0haisf]
[*:db0haisf] #error blocca la compilazione con un errore. [/*:m:db0haisf][/list:u:db0haisf]
https://en.wikipedia.org/wiki/C_preprocessor
In aggiunta a quanto detto da vict, nel caso specifico che hai riportato, la differenza principale è che una variabile definita come [inline]double G = 9.81[/inline] [senza [inline]const[/inline]] può cambiare valore nel corso del programma, mentre ciò che è definito via [inline]#define[/inline] è immutabile.
Sebbene l'uso di [inline]#define[/inline] sia una pratica molto diffusa, soprattutto in C, in generale mi sento di consigliarti di dichiarare variabili vere e di dichiararle costanti, cioè come [inline]const double G = 9.81[/inline], anziché usare [inline]#define[/inline]. La differenza non sarà apprezzabile nel caso di piccoli programmi come questo, ma è una pratica migliore e quindi tanto vale farci l'abitudine.
Una spiegazione dettagliata si trova, ad esempio, nel libro di Meyers Effective C++, capitolo 1, sezione 2.
Sebbene l'uso di [inline]#define[/inline] sia una pratica molto diffusa, soprattutto in C, in generale mi sento di consigliarti di dichiarare variabili vere e di dichiararle costanti, cioè come [inline]const double G = 9.81[/inline], anziché usare [inline]#define[/inline]. La differenza non sarà apprezzabile nel caso di piccoli programmi come questo, ma è una pratica migliore e quindi tanto vale farci l'abitudine.
Una spiegazione dettagliata si trova, ad esempio, nel libro di Meyers Effective C++, capitolo 1, sezione 2.
"Raptorista":
In aggiunta a quanto detto da vict, nel caso specifico che hai riportato, la differenza principale è che una variabile definita come [inline]double G = 9.81[/inline] [senza [inline]const[/inline]] può cambiare valore nel corso del programma, mentre ciò che è definito via [inline]#define[/inline] è immutabile.
Sebbene l'uso di [inline]#define[/inline] sia una pratica molto diffusa, soprattutto in C, in generale mi sento di consigliarti di dichiarare variabili vere e di dichiararle costanti, cioè come [inline]const double G = 9.81[/inline], anziché usare [inline]#define[/inline]. La differenza non sarà apprezzabile nel caso di piccoli programmi come questo, ma è una pratica migliore e quindi tanto vale farci l'abitudine.
Una spiegazione dettagliata si trova, ad esempio, nel libro di Meyers Effective C++, capitolo 1, sezione 2.
Non sono sicuro, ma penso che esista una qualche differenza nel modo in cui il C++ e il C gestiscono le variabili [inline]const[/inline]. Insomma, sicuramente il C++ ha voluto eliminare sin dall'inizio la necessità dei define[nota]Per esempio ora ci sono constexpr e template.[/nota], ma forse alcune cose non sono valide in C. È possibile però che questo non sia più vero nel 2019: ho imparato il C++ e il C nel 2003; molte cose sono cambiate da allora.
Che io sappia, C e C++ interpreteranno il seguente codice in modo diverso:
int const A = 3; int b[A];
e in C potrebbe non compilare.
Da questa pagina: https://en.cppreference.com/w/c/language/const
In altre parole, in C ci sono maggiori limitazioni nei posti in cui una variabile dichiarata costante possa essere utilizzata. In queste circostanze l'uso del preprocessore può essere l'unica alternativa.
Notes
C adopted the const qualifier from C++, but unlike in C++, expressions of const-qualified type in C are not constant expressions; they may not be used as case labels or to initialize static and thread storage duration objects, enumerators, or bit field sizes. When they are used as array sizes, the resulting arrays are VLAs.
In altre parole, in C ci sono maggiori limitazioni nei posti in cui una variabile dichiarata costante possa essere utilizzata. In queste circostanze l'uso del preprocessore può essere l'unica alternativa.