Domanda

Per essere veramente conformi agli standard, tutte le funzioni in C (ad eccezione di main) devono avere un prototipo, anche se vengono utilizzate solo dopo la loro definizione nella stessa unità di traduzione?

È stato utile?

Soluzione

Dipende da cosa intendi per "veramente conforme agli standard". Tuttavia, la risposta breve è "è una buona idea assicurarsi che tutte le funzioni abbiano un prototipo nell'ambito prima di essere utilizzate".

Una risposta più qualificata osserva che se la funzione accetta argomenti variabili (in particolare la famiglia di funzioni printf () ), un prototipo deve essere nell'ambito per essere rigorosamente conforme agli standard. Questo vale per C89 (da ANSI) e C90 (da ISO; lo stesso di C89 ad eccezione della numerazione delle sezioni). A parte le funzioni "varargs", tuttavia, le funzioni che restituiscono un int non devono essere dichiarate e le funzioni che restituiscono qualcosa di diverso da un int necessitano di una dichiarazione che mostri il tipo restituito ma non è necessario il prototipo per l'elenco degli argomenti.

Si noti, tuttavia, che se la funzione accetta argomenti che sono soggetti a "normali promozioni" in assenza di prototipi (ad esempio, una funzione che accetta un char o short - entrambi convertiti in int ; più seriamente, forse, una funzione che accetta un float anziché un double ), quindi è necessario un prototipo. Lo standard era lassista al riguardo per consentire la compilazione del vecchio codice C con compilatori conformi standard; il codice più vecchio non è stato scritto per preoccuparsi di garantire che le funzioni fossero dichiarate prima dell'uso e, per definizione, il codice più vecchio non utilizzava i prototipi poiché non erano disponibili in C fino a quando non esisteva uno standard.

C99 non consente "int implicito" ... ciò significa sia casi dispari come " static a; " (un int di default) sia dichiarazioni di funzioni implicite. Questi sono menzionati (insieme ad altri 50 importanti cambiamenti) nella prefazione ISO / IEC 9899: 1999, che confronta quello standard con le versioni precedenti:

  
      
  • rimuovi int
    implicito   ...
  •   
  • rimuove la dichiarazione di funzione implicita
  •   

In ISO / IEC 9899: 1990, §6.3.2.2 Chiamate di funzione dichiarate:

  

Se l'espressione che precede l'elenco degli argomenti tra parentesi in una chiamata di funzione è costituita   solo di un identificatore e se non è visibile alcuna dichiarazione per questo identificatore, l'identificatore è implicitamente   dichiarata esattamente come se, nel blocco più interno contenente la chiamata di funzione, la dichiarazione:

extern int identifier();
     

apparso. 38

     

38 Cioè, un identificatore con ambito di blocco ha dichiarato di avere un collegamento esterno con la funzione di tipo senza   informazioni sui parametri e restituzione di un int . Se in realtà non è definito come avente tipo "funzione   restituendo int ", il comportamento non è definito.

Questo paragrafo manca nello standard del 1999. Non ho (ancora) monitorato il cambiamento di verbosità che consente static a; in C90 e non lo consente (che richiede static int a; ) in C99.

Nota che se una funzione è statica, può essere definita prima di essere utilizzata e non deve essere preceduta da una dichiarazione. GCC può essere persuaso a diventare più umido se una funzione non statica è definita senza una dichiarazione che la precede ( -Wmissing-prototypes ).

Altri suggerimenti

Un prototipo è una dichiarazione di funzione che specifica i tipi di parametri della funzione.

Pre-ANSI C (il linguaggio descritto dalla prima edizione del 1978 di Kernighan & R; The C Programming Language ") non aveva prototipi; non è stato possibile per una dichiarazione di funzione descrivere il numero o i tipi dei parametri. Stava al chiamante passare il numero e il tipo di argomenti corretti.

ANSI C ha introdotto "prototipi", dichiarazioni che specificano i tipi di parametri (una funzionalità presa in prestito dai primi C ++).

A partire da C89 / C90 (gli standard ANSI e ISO descrivono la stessa lingua), è legale chiamare una funzione senza dichiarazione visibile; viene fornita una dichiarazione implicita. Se la dichiarazione implicita è incompatibile con la definizione effettiva (ad esempio, chiamando sqrt (" foo ") , il comportamento non è definito. Né questa dichiarazione implicita né una dichiarazione non prototipo possono essere compatibili con un funzione variadic, quindi ogni chiamata a una funzione variadic (come printf o scanf ) deve avere un prototipo visibile.

C99 ha lasciato cadere dichiarazioni implicite. Qualsiasi chiamata a una funzione senza una dichiarazione visibile è una violazione del vincolo, che richiede una diagnostica del compilatore. Ma quella dichiarazione non è ancora richiesta per essere un prototipo; può essere una dichiarazione vecchio stile che non specifica i tipi di parametro.

C11 non ha apportato modifiche significative in questo settore.

Quindi, anche a partire dallo standard ISO C del 2011, dichiarazioni e definizioni di funzioni vecchio stile (che sono state "obsolete" dal 1989) sono ancora consentite nel codice conforme.

Per tutte le versioni di C risalenti al 1989, per motivi di stile, ci sono pochissime ragioni per non usare i prototipi per tutte le funzioni. Le dichiarazioni e le definizioni vecchio stile vengono conservate solo per evitare la rottura del vecchio codice.

No, le funzioni non hanno sempre bisogno di un prototipo. L'unico requisito è che una funzione sia "dichiarata" prima di usarlo. Esistono due modi per dichiarare una funzione: scrivere un prototipo o scrivere la funzione stessa (chiamata "definizione". "Una definizione è sempre una dichiarazione, ma non tutte le dichiarazioni sono definizioni.

Sì, ogni funzione deve avere un prototipo, ma quel prototipo può apparire in una dichiarazione separata o come parte della definizione della funzione. Le definizioni delle funzioni scritte in C89 e versioni successive hanno naturalmente dei prototipi, ma se scrivi cose nel classico stile K & amp; R, quindi:

main (argc, argv)

  int argc;
  char **argv;

{
  ...
}

quindi la definizione della funzione non ha prototipo. Se scrivi lo stile ANSI C (C89), quindi:

main (int argc, char **argv) { ... }

quindi la definizione della funzione ha un prototipo.

Un buon consiglio quando si scrivono nuove funzioni è quello di scriverle capovolte con principale in basso, quindi quando si cambia idea sugli argomenti della funzione o sul tipo di ritorno non è necessario correggere anche il prototipo. Riparare costantemente i prototipi e gestire tutti gli avvisi del compilatore quando non sono aggiornati diventa davvero noioso.

Una volta che le funzioni funzionano in modo uniforme, spostare il codice in un modulo ben definito e inserire i prototipi in un file .h con lo stesso nome. Fa risparmiare tempo serio. Il più grande aiuto per la produttività che ho trovato in 5 anni.

Per quanto ne so (in ANSI C89 / ISO C90), no. Non sono sicuro di C99; tuttavia, mi aspetto lo stesso.

Nota personale: scrivo prototipi di funzioni solo quando ...

  1. Devo (quando A () chiama B () e B () chiama A ()) o
  2. Sto esportando la funzione; altrimenti, sembra superfluo.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top