2009-04-23 18 views

risposta

53

La funzione è definita nel file di intestazione? In modo che il codice vero e proprio è dato direttamente nella funzione, in questo modo:

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

Allora questo è solo un modo di fornire una funzione utile per molti file C differenti. 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 in pratica incolla semplicemente il contenuto dell'intestazione (e di eventuali altre intestazioni incluse da esso) nel file C come visualizzato dal compilatore. Il compilatore non sa mai che una particolare definizione di funzione proviene da un file di intestazione.

UPDATE: In molti casi, in realtà è una buona idea per fare qualcosa di simile al precedente, e mi rendo conto la mia risposta suona molto in bianco e nero di questo tipo che è di semplificare eccessivamente le cose un po '. Per esempio, il codice che i modelli (o utilizza solo) intrinsic functions può essere espresso come il precedente, e con un esplicito inline parola chiave anche:

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

Qui, la funzione __add_two_superquickly() è un intrinseco immaginario, e dato che vogliamo l'intero funzione per compilare fondamentalmente fino a una singola istruzione, vogliamo davvero che sia in linea. Tuttavia, quanto sopra è più pulito rispetto all'utilizzo di una macro.

Il vantaggio rispetto all'uso diretto intrinseco è naturalmente che avvolgendolo in un altro livello di astrazione rende possibile creare il codice su compilatori privi di quel particolare intrinseco, fornendo un'implementazione alternativa e scegliendo quella giusta in base alla quale il compilatore è in uso.

+12

Bene, il compilatore probabilmente incorpora funzioni brevi. Quindi potrebbe effettivamente utilizzare meno memoria, se la funzione è abbastanza breve.Ma vorrei anteporre un "inline", quindi non si ottengono avvisi di compilazione sulle funzioni statiche non utilizzate. – quinmars

+0

@quinmars Buon punto, ho modificato. Meglio tardi, spero. :) Grazie. – unwind

+0

Mi chiedo se il linker lo ottimizzerà. Vede che addTwo su b.obj non viene referenziato, quindi rimuove la definizione dall'oggetto? In tal caso, l'overhead è solo la (dimensione della funzione) * (numero di diversi file obj che lo fanno riferimento). Ancora più grande di (dimensione della funzione), ma non così male? – doorfly

10

In pratica creerà una funzione statica separata con lo stesso nome all'interno di ogni file cpp in cui è incluso. Lo stesso vale per le variabili globali.

7

Come altri stanno dicendo, ha esattamente lo stesso significato della funzione static nel file .c stesso. Questo perché non esiste una 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 i contenuti di tutti i file denominati in #include righe (di solito denominate) inserite nello stream come vengono viste dal preprocessore.

La convenzione che la sorgente C si trova in un file denominato .c e le dichiarazioni pubbliche si trovano nei 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 generalmente che lo stesso simbolo sia 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 sarebbe stata sottolineata, si corre il rischio che la funzione venga istanziata in ogni modulo che è successo a #include quel file di intestazione che nel migliore dei casi è uno spreco di memoria nel segmento di codice.

Non sono sicuro di quali casi d'uso giustificassero di fare questo in un colpo di testa pubblico generalmente disponibile.

Se il file viene generato .h codice e incluso solo in un unico file .c, quindi vorrei personalmente il nome del file qualcosa di diverso .h sottolineare che non è in realtà un colpo di testa del pubblico a tutti. 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 ed eventualmente anche le definizioni static di accessor o altra utilità correlata funzioni.

+1

Forse la funzione contiene variabili statiche che preservano il loro valore tra le chiamate (stato interno), e quindi ogni modulo ottiene "la propria copia privata" dei vars avendo il proprio clone della funzione? –

+1

@ranReloaded, Questa è una possibilità. Lo evitavo personalmente perché avrebbe fornito troppe possibilità a un intelligente ottimizzatore (o al manutentore del codice) di romperlo eliminando "intelligentemente" i corpi funzione apparentemente ridondanti. Inoltre, fornisce esattamente un insieme di stati interni per unità di traduzione. Sarebbe meno confuso e meno fragile mettere tutto lo stato in una variabile di stato esplicita, inoltrata a ogni chiamata. – RBerteig

+0

Sì, non sono d'accordo nemmeno con questo "design". Vorrei piuttosto incapsulare dati privati ​​in file vars statici globali (C) o membri di classe (C++) –

1

Non c'è differenza semantica nella definizione nel file di origine o nel file di intestazione, in pratica entrambi significano lo stesso in C normale quando si utilizza la 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 un normale funzione definita nel file di intestazione. Aggiungendo la definizione nell'intestazione non si ottiene ciò che si intende per funzione statica.

Pertanto, suggerisco di avere l'implementazione solo nel file sorgente e non nell'intestazione.

+0

C'è una forte differenza semantica se la funzione contiene una variabile locale statica. In un caso, la variabile statica sarà condivisa, nell'altro caso ci saranno più variabili statiche differenti per ogni unità di compilazione. Pertanto, il comportamento della funzione può essere completamente diverso. – galinette

1

È utile in alcune librerie "solo intestazioni" con funzioni inline di piccole dimensioni. 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 interfaccia e implementazione parti separate nel singolo file di intestazione:

// 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; 
} 

di Apple biblioteca matematica vettoriale nel quadro GLK utilizza tale constuction (ad esempio GLKMatrix4.h).

2

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

Questo può aumentare la dimensione del file 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 ci potrebbe essere una differenza grande nel fare ciò che non è stato menzionato in alcuna risposta. Se la funzione utilizza una variabile locale statica come ad esempio:

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

Piuttosto che:

//header 
int counter(); 

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

Poi ogni file sorgente tra cui questa intestazione avrà un 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.

Problemi correlati