2009-02-26 35 views
22

Qual è la differenza tra una funzione di membro statico e una funzione di collegamento "C" esterna? Ad esempio, quando si utilizza "makecontext" in C++, ho bisogno di passare un puntatore alla funzione. Google consiglia di utilizzare il collegamento extern "C" per questo, perché "makecontext" è C. Ma ho scoperto che anche l'uso di statico funziona. Sono solo fortunato o ...statico vs extern "C"/"C++"

class X { 
    public: 
    static void proxy(int i) {} 
} 
makecontext(..., (void (*)(void)) X::proxy, ...); 

vs

extern "C" void proxy(int i) {} 
makecontext(..., (void (*)(void)) proxy, ...); 

EDIT: Potete mostrare un compilatore o di architettura in cui la versione membro statico non funziona (e non è un bug nel compilatore) ?

+0

'Mi dispiace, ma non sono ancora convinto '... di cosa? il fatto che lo standard sia più autorevole rispetto al comportamento definito dall'implementazione casuale di alcuni compilatori? –

+0

Questo è un vecchio post (8 anni fa oggi) Il mio punto era al momento qualcosa come IMHO se ogni implementazione esistente differisce dallo standard quindi forse dovresti fare una domanda sul fatto che sia lo standard che è sbagliato. Stavo cercando esempi di piattaforme in cui non funziona. –

+0

Abbastanza giusto.Ovviamente non è inaudito che gli standard C o C++ contengano conseguenze indesiderate ignorate da tutti i compilatori esistenti. Questo è il mio preferito al momento: http://stackoverflow.com/a/42335543/2757035 Ma in questo caso, penso che lo Standard stia dicendo chiaramente cosa significa, e le implementazioni potrebbero cambiare il loro comportamento in qualsiasi momento se c'è qualche vantaggio –

risposta

33

Sì, sei solo fortunato :) L'extern "C" è un collegamento linguistico per il linguaggio C che ogni compilatore C++ deve supportare, oltre a extern "C++" che è l'impostazione predefinita. I compilatori possono supportare altri collegamenti linguistici. GCC ad esempio supporta extern "Java" che consente l'interfacciamento con il codice java (anche se è piuttosto ingombrante).

extern "C" indica al compilatore che la funzione è richiamabile dal codice C. Ciò può, ma non deve, includere la convenzione di chiamata appropriata e il nome appropriato del linguaggio c mangling (a volte chiamato "decorazione"), tra le altre cose, a seconda dell'implementazione. Se si dispone di una funzione membro statica, la convenzione di chiamata per esso è quella del compilatore C++. Spesso sono gli stessi del compilatore C di quella piattaforma - quindi ho detto che sei solo fortunato. Se si dispone di un'API C e si passa un puntatore a funzione, sempre meglio mettere uno a una funzione dichiarata con extern "C" come

extern "C" void foo() { ... } 

Anche se il tipo di puntatore a funzione non contiene la specifica di collegamento, ma piuttosto sembra

void(*)(void) 

il collegamento è parte integrante del tipo - non si può esprimere direttamente senza un typedef:

extern "C" typedef void(*extern_c_funptr_t)(); 

il Comeau C++, in modalità rigorosa , ad esempio, si emetterà un errore se si tenta di assegnare l'indirizzo della funzione "C" extern di sopra a (void(*)()), beause questo è un puntatore a una funzione con collegamento C++.

+1

Per aggiungere alla risposta di litb, dovresti leggere le convenzioni di chiamata su Wikipedia - http://en.wikipedia.org/wiki/X86_calling_conventions. extern C implica convenzione di chiamata cdecl; il tuo compilatore usa lo stesso per le funzioni membro statiche. Altri compilatori potrebbero anche scegliere un altro. –

+0

Compilatore @Comeau, si tratta di un errore o di un avviso che emette? –

+0

Helltone, provalo http://www.comeaucomputing.com/tryitout/ dice: "" ComeauTest.c ", riga 4: errore: non è possibile utilizzare un valore di tipo" void (*)() C "per Inizializza un'entità di tipo "void (*)()" ' –

4

extern "C" disattiva il nome del compilatore C++ che si sta maneggiando (necessario per sovraccaricare).

Se si dichiara una funzione in A.cpp come static, quindi non può essere trovata da B.cpp (è rimasta da C e ha lo stesso effetto di inserire una funzione all'interno di uno spazio dei nomi anonimo).

+0

Questo non risponde alla mia domanda –

+0

Può anche cambiare la convenzione di chiamata. –

+0

Nota: l'OP stava parlando di _methods_ statici ("funzioni membro"), non di funzioni _global_ con collegamento statico. –

5

Nota, che extern C è il consigliato per l'interoperabilità C/C++. Here è il padrone che ne parla. Per aggiungere alla risposta di eduffy: si noti che le funzioni statiche e le variabili nello spazio dei nomi globale sono deprecate. Utilizzare almeno uno spazio dei nomi anonimo.

Torna a extern C: se non si utilizza C esternamente, è necessario conoscere il nome esatto e usarlo. Questo è molto più di un dolore.

+0

Nota: l'OP era parlando di _methods_ statici ("funzioni membro"), non funzioni _global_ con collegamento statico. –

2

La maggior parte di ciò che fa extern "C" dipende in gran parte dal compilatore. Molte piattaforme cambiano il nome mangling e la convenzione di chiamata in base alla dichiarazione, ma nessuna di queste viene specificata dallo standard. In realtà l'unica cosa che lo standard richiede è che il codice nel blocco sia richiamabile dalle funzioni C.Per quanto riguarda la tua domanda specifica, la norma dice:

Two function types with different language linkages are distinct types even if they are otherwise identical.

Questo significa extern "C" void proxy(int i) {} e /*extern "C++"*/void proxy(int i) {} hanno diversi tipi, e di conseguenza puntatori a queste funzioni avrebbero tipi differenti pure. Il compilatore non manca il codice per lo stesso motivo non avrebbe mancato un grande pezzo di lavoro come:

int *foo = (int*)50; 
makecontext(..., (void (*)(void)) foo, ...); 

Questo codice potrebbe funzionare su qualche piattaforma, ma questo non significa che funzionerà su un altro piattaforma (anche se il compilatore era pienamente conforme allo standard). Stai sfruttando il funzionamento della tua particolare piattaforma, che potrebbe essere ok se non sei preoccupato di scrivere codice portatile.

Come per le funzioni membro statiche, non è necessario disporre di un puntatore this in modo che il compilatore sia libero di considerarle come una funzione non membro. Di nuovo, il comportamento qui è specifico della piattaforma.

+0

Buona risposta, ma ti manca totalmente il punto della mia domanda. La mia domanda riguarda la differenza tra la funzione "C" extern e la funzione * static member *. –

2

In generale

classi di stoccaggio:

classi di archiviazione vengono usati per indicare la durata e la portata di una variabile o identificatore.

Durata:

Durata indica la durata della vita di una variabile.

Portata:

Scope indica la visibilità della variabile.

Classe di stoccaggio statico:

La classe di memorizzazione statico viene utilizzato per dichiarare un identificatore che è una variabile locale sia per una funzione o un file e che esiste e mantiene il suo valore dopo il controllo passa da dove era dichiarato. Questa classe di archiviazione ha una durata che è permanente. Una variabile dichiarata di questa classe conserva il suo valore da una chiamata della funzione alla successiva. L'ambito è locale. Una variabile è conosciuta solo dalla funzione dichiarata all'interno o se dichiarata globalmente in un file, è conosciuta o vista solo dalle funzioni all'interno di quel file. Questa classe di memorizzazione garantisce che la dichiarazione della variabile inizializzi anche la variabile a zero o tutti i bit disattivati.

Esterno Classe di stoccaggio:

La classe di memorizzazione extern viene utilizzata per dichiarare una variabile globale che sarà conosciuto alle funzioni in un file e in grado di essere noto a tutte le funzioni in un programma. Questa classe di archiviazione ha una durata che è permanente. Qualsiasi variabile di questa classe mantiene il suo valore fino a quando non viene modificata da un altro compito. Lo scopo è globale. Una variabile può essere conosciuta o vista da tutte le funzioni all'interno di un programma.