Poiché la funzione effettiva prevede argomenti di (int, float)
, non (int, double)
, ciò comporterà un comportamento non definito. Anche se la tua funzione è prevista a (int, double)
ma era un numero intero, oppure dici che l'hai chiamato con function1(0, 0);
anziché function(0, 0.0);
, il tuo programma avrebbe comunque un comportamento non definito. Fortunatamente GCC 5.2.1 le comunicazioni che la dichiarazione e la definizione di function1
sono contrastanti:
% gcc test.c
test.c:9:5: error: conflicting types for ‘function1’
int function1(int x, float y) {
^
test.c:9:1: note: an argument type that has a default promotion can’t
match an empty parameter name list declaration
int function1(int x, float y) {
^
test.c:1:5: note: previous declaration of ‘function1’ was here
int function1();
^
test.c:12:5: error: conflicting types for ‘function2’
int function2(int x, float y) {
^
test.c:12:1: note: an argument type that has a default promotion can’t
match an empty parameter name list declaration
int function2(int x, float y) {
^
test.c:2:5: note: previous declaration of ‘function2’ was here
int function2();
^
e le uscite del compilatore con codice di errore, mentre il mio tcc
compila felicemente, nessuna diagnostica, niente. Produce solo il codice rotto. Lo stesso vale, ovviamente, se si avesse la dichiarazione in un file di intestazione e la definizione in una diversa unità di compilazione che non includesse quella dichiarazione.
Ora, qualora il compilatore non rilevare questo caso, in fase di esecuzione, nulla potrebbe accadere, come previsto per comportamento indefinito.
Ad esempio, supponiamo che gli argomenti siano stati passati in pila; sui processori a 32 bit int
e float
potrebbero essere contenuti in 4 byte, mentre double
potrebbe essere di 8 byte; la chiamata di funzione sarebbe quindi spingere x
come int
e y
come double
anche se era float
- in totale il chiamante avrebbe spinto 12 byte e il chiamato si aspetta solo 8.
In un altro caso, si supponga che si definirebbe la funzione con 2 numeri interi. Il codice chiamante dovrebbe quindi caricarli in registri interi, ma il chiamante si aspetterebbe un doppio in un registro a virgola mobile. Il registro a virgola mobile potrebbe contenere un valore trap che, una volta eseguito, potrebbe uccidere il programma.
Qual è peggio, il programma potrebbe ora si comportano come previsto , contenente quindi un heisenbug che potrebbe causare problemi quando si ricompilare il codice con una nuova versione di compilatore, o la porta ad un'altra piattaforma.
Le tue prime due righe dichiarano funzioni con una firma diversa (nessun parametro) a quelle che hai dichiarato in seguito. Quindi le prime due linee non sono necessarie. Le dichiarazioni di funzione descrivono il nome, il numero e il tipo di parametri e il tipo di reso. Due funzioni possono avere lo stesso nome ma diversi parametri. Non possono differire solo nel tipo di ritorno. – BryanT
@BryanT Questo non è corretto (sarebbe comunque corretto in C++). In C, le parentesi vuote nella dichiarazione della funzione significano che può richiedere un numero qualsiasi di argomenti di qualsiasi tipo. Se vuoi esplicitamente zero argomenti, usa '(void)'; vedi una delle firme standard di 'main':' int main (void) {...} '. – szczurcio
Sono d'accordo. Stavo pensando al C++. Ma non è ancora meglio dichiararli esattamente come saranno definiti? – BryanT