funzione statica in C.


172

Qual è lo scopo di rendere statica una funzione in C?


7
@nightcracker: Non ci sono cose come "metodi" in C ++. Penso che tu sia confuso con Objective-C.
Bo Persson,

1
No, sono confuso con Python. Una funzione all'interno di una classe è chiamata metodo in Python.
orlp

Risposte:


212

Fare una funzione la staticnasconde 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;
}

8
L'unità di traduzione è la terminologia corretta da utilizzare qui? Il file oggetto non sarebbe più preciso? Da quanto ho capito, una funzione statica è nascosta dal linker e il linker non opera su unità di traduzione.
Steven Eckhoff,

2
Avrei dovuto anche dire che mi piace pensarlo come nascosto dal linker; sembra più chiaro in questo modo.
Steven Eckhoff,

1
quindi, funzione interna (che sicuramente non chiameremo al di fuori del suo file c), dovremmo metterla come funzione statica, giusto? Quindi, possiamo essere certi che non può chiamare altrove. Grazie :)
hqt

1
Come si compila questo? Tu usi #include <helper_file.c>? Penso che la renderebbe una singola unità di traduzione allora ...
Atcold,

2
@Atcold: nel modo in cui ho scritto il codice includi semplicemente i 2 file sorgente nella riga di comando, in questo modo 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.
pm

80

pmg è perfetto sull'incapsulamento; oltre a nascondere la funzione ad altre unità di traduzione (o meglio, a causa di essa), creare funzioni staticpuò anche conferire vantaggi in termini di prestazioni in presenza di ottimizzazioni del compilatore.

Poiché una staticfunzione 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.


9
... a meno che non venga preso l'indirizzo della funzione.
Caf

1
@caf Cosa intendi per indirizzo della funzione è preso? Per me, la nozione di funzioni / variabili che hanno indirizzi o che hanno un indirizzo assegnato al momento della compilazione è un po 'confusa. Puoi per favore elaborare?
SayeedHussain,

2
@crypticcoder: il programma è caricato in memoria, pertanto le funzioni hanno anche una posizione di memoria e l'indirizzo può essere ottenuto. Con un puntatore a funzione, puoi chiamare uno di quelli. In tal caso, riduce l'elenco di ottimizzazioni che il compilatore può eseguire poiché il codice deve rimanere intatto nello stesso posto.

5
@crypticcoder: intendo che un'espressione valuta un puntatore alla funzione e fa qualcosa con essa oltre a chiamare immediatamente la funzione. Se un puntatore a una staticfunzione sfugge all'unità di traduzione corrente, tale funzione potrebbe essere chiamata direttamente da altre unità di traduzione.
Caf

@caf se viene preso l'indirizzo della funzione, il compilatore lo rileva e disattiva le ottimizzazioni della funzione statica menzionate in questa risposta (ad es. utilizzando un ABI non standard)? Suppongo che dovrebbe.
sevko,

28

La staticparola 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.


1
3Doub: l'uso della parola "cruft" è più preciso di quanto tu gli dia credito. Nel contesto della domanda, "cruft" è la parola giusta da usare qui.
Erik Aronesty,

@ 3Doubloons Sono d'accordo sul fatto che è semplificato, ma penso che lo renda molto più facile da capire per i principianti.
Ingo Bürk,

11

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.


5

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.


4

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.


0

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 */

Questa risposta non è molto utile.
fiscblog
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.