C / C ++: funzione statica nel file di intestazione, cosa significa?

So cosa significa quando la funzione statica è dichiarata nel file sorgente. Sto leggendo del codice, ho scoperto che la funzione statica nei file di intestazione potrebbe essere richiamata in altri file.

La funzione è definita nel file di intestazione? In modo che il codice effettivo sia dato direttamente nella funzione, come questo:

static int addTwo(int x) { return x + 2; } 

Quindi questo è solo un modo per fornire una funzione utile a molti diversi file C. Ogni file C che include l’intestazione avrà la propria definizione che può chiamare. Questo ovviamente spreca memoria, ed è (secondo me) una cosa abbastanza brutta da fare, dato che avere un codice eseguibile in un header non è generalmente una buona idea.

Ricorda che #include : l’intestazione fondamentalmente incolla il contenuto dell’intestazione (e qualsiasi altra intestazione inclusa da esso) nel file C come visto dal compilatore. Il compilatore non sa mai che una particolare definizione di funzione proviene da un file di intestazione.

AGGIORNAMENTO : In molti casi, è davvero una buona idea fare qualcosa come il precedente, e mi rendo conto che la mia risposta suona molto in bianco e nero su questo, che è un po ‘troppo semplicistico. Ad esempio, codifica che le funzioni intrinseche dei modelli (o solo usi) possono essere espresse come sopra, e con una parola chiave inline esplicita anche:

 static inline int addTwo(int *x) { __add_two_superquickly(x); } 

Qui, la funzione __add_two_superquickly() è un __add_two_superquickly() intrinseco fittizio, e dal momento che vogliamo che l’intera funzione si compili fondamentalmente in una singola istruzione, vogliamo davvero che sia in linea. Tuttavia, quanto sopra è più pulito rispetto all’utilizzo di una macro.

Il vantaggio rispetto all’utilizzo diretto dell’intrinseco è naturalmente che avvolgendolo in un altro livello di astrazione rende ansible build il codice su compilatori privi di quel particolare intrinseco, fornendo un’implementazione alternativa e scegliendo quella giusta in base a quale compilatore viene utilizzato .

Creerà efficacemente una funzione statica separata con lo stesso nome all’interno di ogni file cpp in cui è incluso. Lo stesso vale per le variabili globali.

Come altri stanno dicendo, ha esattamente lo stesso significato di una funzione static nel file .c stesso. Questo perché non c’è alcuna differenza semantica tra i file .c e .h ; c’è solo l’unità di compilazione composta dal file effettivamente passato al compilatore (di solito chiamato .c ) con il contenuto di tutti i file chiamati in #include lines (solitamente chiamati .h ) inseriti nello stream come sono visti da il preprocessore.

La convenzione che la sorgente C si trova in un file denominato .c e le dichiarazioni pubbliche si trovano in file denominati. .h è solo una convenzione. Ma è generalmente buono. In base a tale convenzione, le sole cose che dovrebbero apparire nei file .h sono dichiarazioni in modo da evitare in genere che lo stesso simbolo venga definito più volte in un singolo programma.

In questo caso particolare, la parola chiave static rende il simbolo privato del modulo, quindi non esiste un conflitto a più definizioni in attesa di causare problemi. Quindi in questo senso, è sicuro di fare. Ma in assenza di una garanzia che la funzione sia in linea, si corre il rischio che la funzione venga istanziata in ogni modulo che è successo a #include file di intestazione che nel migliore dei casi è uno spreco di memoria nel segmento di codice.

Non sono sicuro di quali casi d’uso giustifichino il fatto di farlo in un colpo di testa pubblico generalmente disponibile.

Se il file .h viene generato e incluso solo in un singolo file .c , allora personalmente chiamerei il file qualcosa di diverso da .h per sottolineare che in realtà non è affatto un header pubblico. Ad esempio, un’utilità che converte un file binario in una definizione di variabile inizializzata potrebbe scrivere un file che è destinato ad essere utilizzato tramite #include e potrebbe benissimo contenere una dichiarazione static della variabile, e possibilmente anche definizioni static di accessor o di altro tipo correlato funzioni di utilità.

Se si definisce la funzione in un file di intestazione (non semplicemente la si dichiara), verrà generata una copia della funzione in ogni unità di traduzione (fondamentalmente in ciascun file cpp che include questa intestazione).

Questo può aumentare la dimensione del tuo eseguibile, ma questo può essere trascurabile se la funzione è piccola. Il vantaggio è che la maggior parte dei compilatori può incorporare la funzione, il che può aumentare le prestazioni del codice.

Ma potrebbe esserci una grande differenza nel fare ciò che non è stato menzionato in nessuna risposta. Se la tua funzione utilizza una variabile locale statica come:

 static int counter() { static int ctr = 0; return ctr++; } 

Piuttosto che:

 //header int counter(); //source int counter() { static int ctr = 0; return ctr++; } 

Quindi ogni file sorgente che include questa intestazione avrà il proprio contatore. Se la funzione è dichiarata nell’intestazione e definita in un file sorgente, il contatore verrà condiviso nell’intero programma.

Così dicendo che l’unica differenza saranno le prestazioni e la dimensione del codice è sbagliata.

Non c’è differenza semantica nella definizione nel file di origine o nel file di intestazione, in pratica entrambi significano lo stesso in C semplice quando si utilizza una parola chiave statica che, si limita l’ambito.

Tuttavia, c’è un problema nella scrittura di questo nel file di intestazione, questo perché ogni volta che includi l’intestazione in un file sorgente avrai una copia della funzione con la stessa implementazione che è molto simile ad avere una normale funzione definita nell’intestazione file. Aggiungendo la definizione nell’intestazione non si ottiene ciò che si intende per funzione statica.

Pertanto, ti suggerisco di avere la tua implementazione solo nel tuo file sorgente e non nell’intestazione.

È utile in alcune librerie “header-only” con piccole funzioni inline. In tal caso, si desidera sempre fare una copia della funzione, quindi non è un cattivo motivo. Tuttavia, questo ti dà un modo semplice per inserire interfacce separate e parti di implementazione nel file di intestazione singola:

 // header.h // interface part (for user?!) static inline float av(float a, float b); // implementation part (for developer) static inline float av(float a, float b) { return (a+b)/2.f; } 

La libreria matematica vettoriale di Apple nel framework GLK utilizza tale configurazione (ad esempio GLKMatrix4.h).