2009-08-21 5 views
6

Apparentemente (almeno secondo gcc -std=c99) C99 non supporta l'overloading delle funzioni. La ragione per non supportare alcune nuove funzionalità in C è in genere la retrocompatibilità, ma in questo caso non riesco a pensare a un singolo caso in cui l'overloading delle funzioni possa compromettere la compatibilità con le versioni precedenti. Qual è il ragionamento alla base di questa caratteristica di base?C'è un motivo per cui C99 non supporta l'overloading delle funzioni?

+2

Se si desidera C con sovraccarico di funzione, si potrebbe essere interessati a guardare C++. – SingleNegationElimination

+0

@William, ecco cosa ho trovato nel documento C rationale: "La pratica esistente di omettere i parametri finali in una chiamata se è noto che i parametri non verranno utilizzati è stata costantemente scoraggiata. Poiché l'omissione di tali parametri crea una disequivalenza tra la chiamata e la dichiarazione, il comportamento in questi casi è indefinito e un programma massimamente portatile eviterà questo utilizzo. Quindi un'implementazione è libera di implementare un meccanismo di chiamata di funzioni per elenchi di argomenti fissi che fallirebbero (forse fatalmente) se il numero o tipo di argomenti dovevano essere forniti. " –

+0

(da http://www.lysator.liu.se/c/rat/c3.html#3-3-2-2) –

risposta

27

Per capire perché non è probabile che si verifichi un sovraccarico in C, potrebbe essere utile imparare meglio come il sovraccarico viene gestito da C++.

Dopo aver compilato il codice, ma prima che sia pronto per l'esecuzione, è necessario collegare il codice dell'oggetto intermedio. Questo trasforma un rozzo database di funzioni compilate e altri oggetti in un file binario pronto da caricare/eseguire. Questo passaggio aggiuntivo è importante perché è il principale meccanismo di modularità disponibile per i programmi compilati. Questo passaggio ti consente di prendere il codice dalle librerie esistenti e di integrarlo con la tua logica applicativa.

In questa fase, il codice oggetto potrebbe essere stato scritto in qualsiasi lingua, con qualsiasi combinazione di funzionalità. Per rendere ciò possibile, è necessario avere una sorta di convenzione in modo che il linker sia in grado di scegliere l'oggetto giusto quando un altro oggetto si riferisce ad esso. Se stai codificando in linguaggio assembly, quando definisci un'etichetta, quell'etichetta viene utilizzata esattamente, perché si presume tu sappia cosa stai facendo.

In C, le funzioni diventano i nomi dei simboli per il linker, in modo che quando si scrive

int main(int argc, char **argv) { return 1; } 

il compilatore fornisce un archivio di codice oggetto, che contiene un oggetto chiamato main.

Questo funziona bene, ma significa che non è possibile avere due oggetti con lo stesso nome, perché il linker non sarebbe in grado di decidere quale nome utilizzare. Il linker non sa nulla sui tipi di argomenti e molto poco sul codice in generale.

C++ risolve questo codificando informazioni aggiuntive direttamente nel nome del simbolo. Il tipo di ritorno e il numero e il tipo di argomenti sono aggiunti al nome del simbolo e sono riferiti in quel modo al punto di una chiamata di funzione. Il linker non deve sapere che questo sta accadendo, dal momento che, per quanto possa sapere, la chiamata alla funzione non è ambigua.

Lo svantaggio di questo è che i nomi dei simboli non assomigliano ai nomi delle funzioni originali. In particolare, è quasi impossibile prevedere quale sarà il nome di una funzione sovraccaricata in modo che sia possibile collegarsi ad esso. Per collegarsi al codice foriegn, è possibile utilizzare extern "C", che fa sì che tali funzioni seguano lo stile C dei nomi dei simboli, ma ovviamente non è possibile sovraccaricare tale funzione.

Queste differenze sono correlate agli obiettivi di progettazione di ciascuna lingua. C è orientato alla portabilità e all'interoperabilità. C fa del suo meglio per fare cose prevedibili e compatibili. Il C++ è più fortemente orientato alla creazione di sistemi ricchi e potenti, e non molto focalizzato sull'interazione con altri linguaggi.

Penso che sia improbabile che C possa mai perseguire qualsiasi caratteristica che produca codice con cui sia difficile interagire come C++.

Edit: Imagist chiede:

sarebbe davvero essere meno portatile o più difficile interagire con una funzione se risolto int main (int argc , char ** argv) a qualcosa come main-int-int-char ** anziché a main (e questo faceva parte dello standard)? Non vedo un problema qui. In realtà, mi sembra che questo ti dà ulteriori informazioni (che potrebbero essere utilizzate per l'ottimizzazione e simili)

Per rispondere a questa, mi rivolgerò ancora a C++ e il modo in cui si tratta di sovraccarichi . C++ utilizza questo meccanismo, quasi esattamente come descritto, ma con un avvertimento. Il C++ non standardizza il modo in cui alcune parti di sé dovrebbero essere implementate, e poi continua a suggerire come alcune delle conseguenze di tale omissione. In particolare, C++ ha un sistema di tipo ricco, che include membri di classi virtuali. Il modo in cui questa caratteristica dovrebbe essere implementata è lasciata agli scrittori del compilatore, ei dettagli della risoluzione vtable hanno un forte effetto sulle firme delle funzioni. Per questo motivo, C++ suggerisce deliberatamente che i produttori di compilatori rendano il nome mangling incompatibile tra i compilatori o gli stessi compilatori con diverse implementazioni di queste funzionalità chiave.

Questo è solo un sintomo del problema più profondo che mentre i linguaggi di livello superiore come C++ e C hanno sistemi di tipi dettagliati, il codice macchina di livello inferiore è totalmente privo di caratteri. sistemi di tipo arbitrariamente ricchi sono costruiti sopra il binario non tipizzato fornito a livello di macchina. I linker non hanno accesso alle informazioni di tipo ricco disponibili per le lingue di livello superiore. Il linker è completamente dipendente dal compilatore per gestire tutte le astrazioni di tipo e produrre correttamente codice oggetto senza tipo.

C++ fa questo codificando tutte le informazioni di tipo necessarie nei nomi oggetto storpiati. C, tuttavia, ha un focus significativamente diverso, con l'obiettivo di essere una sorta di linguaggio assembly portatile. C preferisce quindi avere una stretta corrispondenza uno a uno tra il nome dichiarato e il nome del simbolo degli oggetti risultanti. Se C Mangled i suoi nomi, anche in un modo standardizzato e prevedibile, dovresti fare grandi sforzi per abbinare i nomi modificati ai nomi dei simboli desiderati, altrimenti dovresti spegnerlo come fai in C++. Questo sforzo in più non ha quasi alcun vantaggio, perché a differenza del C++, il sistema di tipo C è abbastanza piccolo e semplice.

Allo stesso tempo, è praticamente una pratica standard definire diverse funzioni C con nome simile che variano solo in base ai tipi che assumono come argomenti. per un lungo esempio di questo, date un'occhiata allo OpenGL namespace.

+1

Sarebbe davvero meno portatile o più difficile interagire con una funzione se risolvessi 'int main (int argc, char ** argv)' in qualcosa come 'main-int-int-char **' invece di 'main '(e questo faceva parte dello standard)? Non vedo un problema qui. In effetti, mi sembra che questo ti fornisca * più * informazioni (che potrebbero essere utilizzate per l'ottimizzazione e simili). – Imagist

+0

@Imagist - Sarebbe utile per una lingua. Renderà immediatamente inutilizzabili tutte le vecchie librerie C dal nuovo codice C e tutte le nuove librerie C inutilizzabili dal vecchio codice C. –

+0

@Chris Lutz Vero, ma come designer del linguaggio devi essere disposto a interrompere occasionalmente la retrocompatibilità. Con un linguaggio come C non dovrebbe accadere spesso, ma è una volta ogni dieci anni tanto chiedere? In molti casi siamo bloccati con decisioni di progettazione che potrebbero essere state buone, ma non sono buone ora, a causa della tua mentalità. – Imagist

19

Quando si compila una sorgente C, i nomi dei simboli rimarranno intatti. Se si introduce il sovraccarico della funzione, è necessario fornire una tecnica di manomissione del nome per evitare conflitti di nomi. Di conseguenza, come C++, avrai i nomi dei simboli generati dalla macchina nel binario compilato.

Inoltre, C non presenta una digitazione rigorosa. Molte cose sono implicitamente convertibili l'una nell'altra in C. La complessità delle regole di risoluzione del sovraccarico potrebbe introdurre confusione in questo tipo di linguaggio.

+0

Mi sembra che la risposta a questo sia semplice: fornire una tecnica di mangling del nome standard per mantenere le informazioni sul tipo. Qualcosa come 'foo (bar) -> foo-bar' e' foo (baz) -> foo-baz' non è difficile da implementare. – Imagist

+2

Ho menzionato tre punti in realtà: 1. digitazione debole 2. aumento della complessità 3. nome scontro. Potresti costruire una tecnica di mangling dei nomi standard, ma come lo renderesti compatibile con le librerie già compilate in C89 e se costruisci una libreria in C99, come potresti usarla in C89? –

6

Molti progettisti di linguaggi, incluso me, pensano che la combinazione di sovraccarico di funzione con le promozioni implicite di C possa risultare in un codice che è odiosamente difficile da comprendere. Per prova, guarda il corpus di conoscenze accumulate su C++.

In generale, C99 doveva essere una revisione modesta in gran parte compatibile con la pratica esistente. Sovraccarico sarebbe stata una partenza piuttosto grande.

+1

Molti progettisti di linguaggi, incluso me, pensano che l'accesso diretto alla memoria attraverso i puntatori possa risultare in un codice che è odiosamente difficile da comprendere. La soluzione è ovviamente non escludere i puntatori dal linguaggio (almeno non nel caso di C). – Imagist

+0

@Imagist. Sono d'accordo con te. Preferisco scrivere i miei sistemi operativi in ​​Modula-3, che fornisce meccanismi migliori per il controllo e la comprensione del codice puntatore. Ma i programmi puntatori devono essere scritti, e dopo aver letto l'articolo di Dennis Ritchie in HOPL-II, credo che C occupi quella nicchia così bene da non essere mai sostituito. Ma le persone dovrebbero smettere di scrivere applicazioni in esso! (E grazie a Java, molti hanno fatto proprio questo.) –

+0

Penso che le conversioni implicite in C rendano il codice abbastanza difficile da capire senza sovraccaricare! +1 – SingleNegationElimination

Problemi correlati