Qual è lo scopo di rendere statica una funzione in C?
Qual è lo scopo di rendere statica una funzione in C?
Risposte:
Fare una funzione la static
nasconde da altre unità di traduzione, il che aiuta a fornire l' incapsulamento .
helper_file.c
int f1(int); /* prototype */
static int f2(int); /* prototype */
int f1(int foo) {
return f2(foo); /* ok, f2 is in the same translation unit */
/* (basically same .c file) as f1 */
}
int f2(int foo) {
return 42 + foo;
}
main.c :
int f1(int); /* prototype */
int f2(int); /* prototype */
int main(void) {
f1(10); /* ok, f1 is visible to the linker */
f2(12); /* nope, f2 is not visible to the linker */
return 0;
}
#include <helper_file.c>
? Penso che la renderebbe una singola unità di traduzione allora ...
gcc -std=c99 -pedantic -Wall -Wextra main.c helper_file.c
. I prototipi per le funzioni sono presenti in entrambi i file di origine (non sono necessari file di intestazione). Il linker risolverà le funzioni.
pmg è perfetto sull'incapsulamento; oltre a nascondere la funzione ad altre unità di traduzione (o meglio, a causa di essa), creare funzioni static
può anche conferire vantaggi in termini di prestazioni in presenza di ottimizzazioni del compilatore.
Poiché una static
funzione non può essere chiamata da nessuna parte al di fuori dell'unità di traduzione corrente (a meno che il codice non indichi un puntatore al suo indirizzo), il compilatore controlla tutti i punti di chiamata al suo interno.
Ciò significa che è libero di utilizzare un ABI non standard, integrarlo interamente o eseguire un numero qualsiasi di altre ottimizzazioni che potrebbero non essere possibili per una funzione con collegamento esterno.
static
funzione sfugge all'unità di traduzione corrente, tale funzione potrebbe essere chiamata direttamente da altre unità di traduzione.
La static
parola chiave in C viene utilizzata in un file compilato (.c anziché .h) in modo che la funzione esista solo in quel file.
Normalmente, quando si crea una funzione, il compilatore genera una cruft che il linker può usare per collegare una chiamata di funzione a quella funzione. Se si utilizza la parola chiave statica, altre funzioni all'interno dello stesso file possono chiamare questa funzione (perché può essere eseguita senza ricorrere al linker), mentre il linker non ha informazioni che consentano ad altri file di accedere alla funzione.
Guardando i post sopra vorrei sottolineare un dettaglio.
Supponiamo che il nostro file principale ("main.c") assomigli a questo:
#include "header.h"
int main(void) {
FunctionInHeader();
}
Consideriamo ora tre casi:
Caso 1: Il nostro file di intestazione ("header.h") è simile al seguente:
#include <stdio.h>
static void FunctionInHeader();
void FunctionInHeader() {
printf("Calling function inside header\n");
}
Quindi il seguente comando su Linux:
gcc main.c header.h -o main
ci riuscirà ! In seguito, se uno corre
./main
L'output sarà
Funzione di chiamata all'interno dell'intestazione
Questo è ciò che dovrebbe stampare quella funzione statica.
Caso 2: Il nostro file di intestazione ("header.h") è simile al seguente:
static void FunctionInHeader();
e abbiamo anche un altro file "header.c", che assomiglia a questo:
#include <stdio.h>
#include "header.h"
void FunctionInHeader() {
printf("Calling function inside header\n");
}
Quindi il seguente comando
gcc main.c header.h header.c -o main
darà un errore.
Caso 3:
Simile al caso 2, tranne per il fatto che ora il nostro file di intestazione ("header.h") è:
void FunctionInHeader(); // keyword static removed
Quindi lo stesso comando del caso 2 avrà esito positivo e l'esecuzione ulteriore ./main darà il risultato atteso.
Quindi da questi test (eseguiti su macchine Acer x86, sistema operativo Ubuntu) ho ipotizzato che
la parola chiave static impedisce che la funzione venga chiamata in un altro file * .c rispetto a dove è definita.
Correggimi se sbaglio.
I programmatori C usano l'attributo statico per nascondere le dichiarazioni di variabili e funzioni all'interno dei moduli, proprio come faresti con le dichiarazioni pubbliche e private in Java e C ++. I file sorgente C svolgono il ruolo di moduli. Qualsiasi variabile o funzione globale dichiarata con l'attributo statico è privata per quel modulo. Allo stesso modo, qualsiasi variabile o funzione globale dichiarata senza l'attributo statico è pubblica e può essere letta da qualsiasi altro modulo. È buona prassi di programmazione proteggere le variabili e le funzioni con l'attributo statico ove possibile.
La risposta di pmg è molto convincente. Se desideri sapere come funzionano le dichiarazioni statiche a livello di oggetto, queste informazioni di seguito potrebbero essere interessanti per te. Ho riutilizzato lo stesso programma scritto da pmg e lo ho compilato in un file .so (oggetto condiviso)
I seguenti contenuti sono dopo aver scaricato il file .so in qualcosa di leggibile dall'uomo
0000000000000675 f1 : indirizzo della funzione f1
000000000000068c f2 : indirizzo della funzione f2 (staticc)
notare la differenza nell'indirizzo della funzione, significa qualcosa. Per una funzione dichiarata con un indirizzo diverso, può benissimo significare che f2 vive molto lontano o in un diverso segmento del file oggetto.
I linker usano qualcosa chiamato PLT (tabella di collegamento di procedura) e GOT (tabella di offset globale) per comprendere i simboli a cui hanno accesso al link.
Per ora pensa che GOT e PLT leghino magicamente tutti gli indirizzi e una sezione dinamica contiene informazioni su tutte queste funzioni che sono visibili dal linker.
Dopo aver scaricato la sezione dinamica del file .so otteniamo un sacco di voci ma siamo interessati solo alla funzione f1 e f2 .
La sezione dinamica contiene la voce solo per la funzione f1 all'indirizzo 0000000000000675 e non per f2 !
Num: Valore Dimensione Tipo Bind Vis Ndx Name
9: 0000000000000675 23 FUNC GLOBAL DEFAULT 11 f1
E questo è tutto! Da ciò risulta chiaro che il linker non riuscirà a trovare la funzione f2 poiché non si trova nella sezione dinamica del file .so.
Quando è necessario limitare l'accesso ad alcune funzioni, utilizzeremo la parola chiave statica durante la definizione e la dichiarazione di una funzione.
/* file ab.c */
static void function1(void)
{
puts("function1 called");
}
And store the following code in another file ab1.c
/* file ab1.c */
int main(void)
{
function1();
getchar();
return 0;
}
/* in this code, we'll get a "Undefined reference to function1".Because function 1 is declared static in file ab.c and can't be used in ab1.c */