2015-02-02 16 views
22

Sapendo che questa chiamata:Perché questo puntatore funziona senza avvertimenti o errori?

pow(4); 

genererà questo messaggio di errore:

error: too few arguments to function ‘pow’ 

sto imparando puntatori a funzioni e ho avuto sorpreso quando visto questo codice qui sotto a lavorare. Ma perché?

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

void aux(double (*function)(), double n, double x); 

int main(void) 
{ 
    aux(pow, 4, 2); 

    aux(sqrt, 4, 0); 

    return 0; 
} 

void aux(double (*function)(double), double n, double x) 
{ 
    if(x == 0) 
     printf("\nsqrt(%.2f, %.2f): %f\n", n, x, (*function)(n)); 
    else 
     printf("\npow(%.2f, %.2f): %f\n", n, x, (*function)(n)); 
} 

ho compilato utilizzando:

gcc -Wall -Wextra -pedantic -Wconversion -o test test.c -lm 

Il risultato è:

pow(4.00, 2.00): 16.000000 

sqrt(4.00, 0.00): 2.000000 

se cambio il terzo parametro della prima chiamata di aux a 3, il risultato cambia a:

pow(4.00, 3.00): 64.000000 

sqrt(4.00, 0.00): 2.000000 

E ancora una domanda. Qual è il modo corretto per dichiarare e utilizzare i puntatori alle funzioni in questo caso?

+0

@SouravGhosh: Non è questo il problema. Un '-lm mancante causerebbe un errore del linker. –

+0

@KeithThompson Giusto signore. Colpa mia. –

+0

@SouravGhosh Non c'è bisogno di scusarsi. Basta eliminare i commenti errati e fastidiosi e andare avanti. –

risposta

37

questo:

void aux(double (*function)(), double n, double x); 

utilizza una dichiarazione di non-prototipo di vecchio stile per function. Le parentesi vuote () significano che la funzione prende un numero fisso e non specificato e uno o più tipi di argomenti.

C consente comunque questo tipo di dichiarazione per compatibilità con le versioni precedenti. I prototipi (dichiarazioni di funzioni che specificano i tipi dei parametri) sono stati introdotti da ANSI C nel 1989. Prima non era possibile specificare i tipi di parametro in una dichiarazione di funzione ei compilatori non potevano verificare se una chiamata ha superato il numero corretto e tipo (i) di argomenti.

Tali dichiarazioni sono "obsolete", il che significa che il supporto per esse potrebbe essere rimosso da un futuro standard C (ma in più di 20 anni il comitato non è riuscito a rimuoverle). Chiamare una funzione con il numero sbagliato di tipi di argomenti non verrà necessariamente diagnosticata dal compilatore e il comportamento non è definito.

Le regole per la compatibilità di tipi di funzione sono un po 'complicate quando uno ha un prototipo e l'altro no. Questi tipi:

double(double)   /* function with one double parameter 
          returning double */ 
double(double, double) /* function with two double parameters 
          returning double */ 

non sono compatibili tra di loro, ma sono entrambi compatibile con questo tipo:

double() /* function with a fixed but unspecified number of parameters 
       returning double */ 

che è ciò che rende possibile avere chiamate errate senza una diagnosi da il compilatore.

Per evitare questo problema, utilizzare sempre prototipi:

void aux(double (*function)(double, double), double n, double x); 

Non solo si ottiene una migliore Diagnostics dal compilatore, non c'è bisogno di preoccuparsi per le regole di compatibilità contorte per le funzioni non-creazione del prototipo (che, se sei curioso, sono specificati in N1570 6.7.6.3 paragrafo 16).

+2

Ti stai ancora chiedendo come solo un argomento abbia come risultato il calcolo di 'pow' o' sqrt'? – haccks

+0

@haccks: comportamento non definito. –

+6

@haccks: puoi meravigliarti per sempre. Il comportamento è ** non definito **. In linea di principio, potrebbe cambiare la volta successiva in cui eseguirai il tuo programma. Non puoi prevederlo, e puoi solo spiegarlo smontando il binario per esaminare il codice effettivamente generato dal compilatore. Non si può presumere che il compilatore genererà lo stesso codice la prossima volta che compila il programma. –

5

una coppia vuota di parentesi () dice al compilatore C che la funzione può essere chiamata con qualsiasi numero di parametri, ma che la funzione stessa ha un numero specifico di parametri per suo prototipo. Quindi se lo si utilizza con un puntatore a funzione e il numero e i rispettivi tipi di parametri passati corrispondono al prototipo della funzione punta a punta, tutto funzionerà. Se si affama l'elenco dei parametri o si usano valori con caratteri diversi, tuttavia ... beh, questo è un comportamento indefinito.

8

Poiché è stato specificato il prototipo di aux() prima di main e function non ha alcun tipo di argomento specificato. Imparare la differenza:

void f(); /* Accepts any number of arguments thanks to K&R C */ 
void g(void); /* No arguments accepted */ 
void h(int); /* Only one integer argument accepted */ 

Se si dichiara aux() prototipo come:

void aux(double (*function)(double), double n, double x); 

GCC inizia a lamentarsi.

9

vostro prototipo di function aux() ...

void aux(double (*function)(), double n, double x); 

... specifica il primo argomento ad essere un puntatore a una funzione che restituisce double e accettando gli argomenti non specificati. Ciò impedisce a GCC di emettere avvisi su tipi non corrispondenti per le chiamate a tale funzione in main().

Tuttavia, la funzione della funzione aux() fornisce un tipo più specifico per il suo primo parametro, uno che è incompatibile con gli argomenti effettivi che si stanno passando. La chiamata di tali funzioni tramite il puntatore ha semantica indefinita. Praticamente potrebbe succedere di tutto, compreso che il comportamento sembra essere quello che volevi. Non puoi fare affidamento su nulla su quel comportamento.

Problemi correlati