Wat is een “statische” functie in C?

De vraag ging over eenvoudige c-functies, niet c++staticmethoden, zoals verduidelijkt in opmerkingen.

Ik begrijp wat een staticvariabele is, maar wat is een staticfunctie?

En waarom is het dat als ik een functie declareer, laten we zeggen void print_matrix, in laten we zeggen a.c(ZONDER a.h) en voeg "a.c"– ik krijg "print_matrix@@....) already defined in a.obj", MAAR als ik het declareer als static void print_matrixdan compileert het?

UPDATEEven voor de duidelijkheid – ik weet dat het opnemen van .cslecht is, zoals velen van jullie hebben opgemerkt. Ik doe het gewoon om tijdelijk ruimte vrij te maken in main.ctotdat ik een beter idee heb hoe ik al die functies moet groeperen in de juiste .hen .cbestanden. Slechts een tijdelijke, snelle oplossing.


Antwoord 1, autoriteit 100%

staticfuncties zijn functies die alleen zichtbaar zijn voor andere functies in hetzelfde bestand (meer precies dezelfde vertaaleenheid).

EDIT: voor degenen die dachten dat de auteur van de vragen een ‘klassenmethode’ bedoelde: aangezien de vraag is getagd met C, bedoelt hij een gewone oude C-functie . Voor (C++/Java/…) klassenmethoden betekent staticdat deze methode op de klasse zelf kan worden aangeroepen, geen instantie van die klasse nodig.


Antwoord 2, autoriteit 28%

Er is een groot verschil tussen statische functies in C en statische lidfuncties in C++. In C is een statische functie niet zichtbaar buiten de vertaaleenheid, het objectbestand waarin het is gecompileerd. Met andere woorden, het statisch maken van een functie beperkt de reikwijdte ervan. Je kunt een statische functie beschouwen als “privé” voor zijn *.c-bestand (hoewel dat niet helemaal correct is).

In C++ kan “statisch” ook van toepassing zijn op lidfuncties en gegevensleden van klassen. Een statisch gegevenslid wordt ook wel een “klassevariabele” genoemd, terwijl een niet-statisch gegevenslid een “instantievariabele” is. Dit is Smalltalk-terminologie. Dit betekent dat er slechts één exemplaar van een statisch gegevenslid is dat wordt gedeeld door alle objecten van een klasse, terwijl elk object zijn eigen exemplaar van een niet-statisch gegevenslid heeft. Een statisch gegevenslid is dus in wezen een globale variabele, dat wil zeggen een lid van een klasse.

Niet-statische lidfuncties hebben toegang tot alle gegevensleden van de klasse: statisch en niet-statisch. Statische lidfuncties kunnen alleen werken op de statische gegevensleden.

Een manier om hierover na te denken is dat in C++ statische gegevensleden en statische lidfuncties niet tot een object behoren, maar tot de hele klasse.


Antwoord 3, autoriteit 9%

Voorbeeld van minimaal uitvoerbaar bereik met meerdere bestanden

Hier illustreer ik hoe staticde reikwijdte van functiedefinities over meerdere bestanden beïnvloedt.

a.c

#include <stdio.h>
/* Undefined behavior: already defined in main.
 * Binutils 2.24 gives an error and refuses to link.
 * https://stackoverflow.com/questions/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
 */
/*void f() { puts("a f"); }*/
/* OK: only declared, not defined. Will use the one in main. */
void f(void);
/* OK: only visible to this file. */
static void sf() { puts("a sf"); }
void a() {
    f();
    sf();
}

main.c

#include <stdio.h>
void a(void);        
void f() { puts("main f"); }
static void sf() { puts("main sf"); }
void m() {
    f();
    sf();
}
int main() {
    m();
    a();
    return 0;
}

GitHub stroomopwaarts.

Compileren en uitvoeren:

gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o
./main

Uitvoer:

main f
main sf
main f
a sf

Interpretatie

  • er zijn twee aparte functies sf, één voor elk bestand
  • er is één gedeelde functie f

Zoals gebruikelijk, hoe kleiner het bereik, hoe beter, dus declareer functies altijd staticals je kunt.

In C-programmering worden bestanden vaak gebruikt om “klassen” weer te geven, en static-functies vertegenwoordigen “privé”-methoden van de klasse.

Een algemeen C-patroon is om een thisstruct door te geven als het eerste “methode” -argument, wat in feite is wat C++ onder de motorkap doet.

Wat de normen erover zeggen

C99 N1256-concept6.7.1 “Storage-class specifiers” zegt dat staticeen “storage-class specifier” is.

6.2.2/3 “Koppelingen van identifiers” zegt dat staticinternal linkageimpliceert:

Als de declaratie van een bestandsomvang-ID voor een object of een functie de opslagklasse-specificatie static bevat, heeft de identifier een interne koppeling.

en 6.2.2/2 zegt dat internal linkagezich gedraagt zoals in ons voorbeeld:

In de set van vertaaleenheden en bibliotheken die een heel programma vormt, duidt elke declaratie van een bepaalde identifier met externe koppeling hetzelfde object of dezelfde functie aan. Binnen één vertaaleenheid duidt elke declaratie van een identifier met interne koppeling hetzelfde object of dezelfde functie aan.

waarbij “vertaaleenheid” een bronbestand is na voorbewerking.

Hoe GCC dit implementeert voor ELF (Linux)?

Met de STB_LOCALbinding.

Als we compileren:

int f() { return 0; }
static int sf() { return 0; }

en demonteer de symbolentabel met:

readelf -s main.o

de uitvoer bevat:

Num:    Value          Size Type    Bind   Vis      Ndx Name
  5: 000000000000000b    11 FUNC    LOCAL  DEFAULT    1 sf
  9: 0000000000000000    11 FUNC    GLOBAL DEFAULT    1 f

dus de binding is het enige significante verschil tussen beide. Valueis slechts hun offset in de sectie .bss, dus we verwachten dat deze zal verschillen.

STB_LOCALis gedocumenteerd op de ELF-specificatie op http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html:

STB_LOCAL LOKALE SYMBOLEN zijn niet zichtbaar buiten het objectbestand dat hun definitie bevat. Lokale symbolen van dezelfde naam kunnen in meerdere bestanden bestaan ​​zonder met elkaar te storen

wat het een perfecte keuze maakt om staticte vertegenwoordigen.

Functies zonder statisch zijn STB_GLOBAL, en de specificatie zegt:

Wanneer de linkeditor verschillende relocatable objectbestanden combineert, staat deze niet toe dat meerdere definities van STB_Global-symbolen met dezelfde naam zijn.

die coherent is met de koppelingsfouten op meerdere niet-statische definities.

Als we de optimalisatie met -O3verdraaien, wordt de sfsymbool volledig uit de symbooltabel verwijderd: het kan sowieso niet van buitenaf worden gebruikt. Todo Waarom statische functies op de symbooltafel houden wanneer er geen optimalisatie is? Kunnen ze voor iets worden gebruikt?

Zie ook

C++ Anonieme naamruimten

In C++ wilt u misschien anonieme naamruimten gebruiken in plaats van statisch, wat een soortgelijk effect bereikt, maar verdere verbindingstypedefinities: Unnamed / anonieme naamruimten versus statische functies


Antwoord 4, Autoriteit 3%

Het volgende gaat over gewone C-functies – in een C++-klasse heeft de modifier ‘static’ een andere betekenis.

Als je maar één bestand hebt, maakt deze modifier absoluut geen verschil. Het verschil zit in grotere projecten met meerdere bestanden:

In C wordt elke “module” (een combinatie van sample.c en sample.h) onafhankelijk gecompileerd en daarna worden al die gecompileerde objectbestanden (sample.o) door de linker aan elkaar gekoppeld aan een uitvoerbaar bestand.

Stel dat je verschillende bestanden hebt die je in je hoofdbestand opneemt en dat twee ervan een functie hebben die alleen voor het gemak intern wordt gebruikt, genaamd add(int a, b)– de compiler zou gemakkelijk maak objectbestanden voor die twee modules, maar de linker zal een foutmelding geven, omdat het twee functies met dezelfde naam vindt en het niet weet welke het moet gebruiken (zelfs als er niets is om te linken, omdat ze niet ergens worden gebruikt anders maar in zijn eigen bestand).

Daarom maak je van deze functie, die alleen intern wordt gebruikt, een statische functie. In dit geval maakt de compiler niet de typische “you can link this thing”-flag voor de linker, zodat de linker deze functie niet ziet en geen fout genereert.


Antwoord 5, autoriteit 2%

statische functiedefinities markeren dit symbool als intern. Het is dus niet zichtbaar voor koppelingen van buitenaf, maar alleen voor functies in dezelfde compilatie-eenheid, meestal hetzelfde bestand.


Antwoord 6, autoriteit 2%

Ten eerste: het is over het algemeen een slecht idee om een .cpp-bestand in een ander bestand op te nemen – dit leidt tot dit soort problemen 🙂 De normale manier is om afzonderlijke compilatie-eenheden te maken en een header toe te voegen bestand voor het bijgevoegde bestand.

Ten tweede:

C++ heeft hier wat verwarrende terminologie – ik wist er niets van tot ik erop gewezen werd in opmerkingen.

a) static functions– overgenomen van C, en waar je het hier over hebt. Buiten elke les. Een statische functiebetekent dat deze niet zichtbaar is buiten de huidige compilatie-eenheid – dus in jouw geval heeft a.obj een kopie en heeft je andere code een onafhankelijke kopie. (Het laatste uitvoerbare bestand opblazen met meerdere exemplaren van de code).

b) static member function– wat Object Oriëntatie een statische methodenoemt. Woont in een klas. Je roept dit aan met de klasse in plaats van via een objectinstantie.

Deze twee verschillende statische functiedefinities zijn totaal verschillend. Wees voorzichtig – hier zijn draken.


Antwoord 7

Een statische functie is een functie die kan worden aangeroepen op de klasse zelf, in tegenstelling tot een instantie van de klasse.

Een niet-statisch zou bijvoorbeeld zijn:

Person* tom = new Person();
tom->setName("Tom");

Deze methode werkt op een instantie van de klasse, niet op de klasse zelf. U kunt echter een statische methode hebben die kan werken zonder een instantie te hebben. Dit wordt soms gebruikt in het fabriekspatroon:

Person* tom = Person::createNewPerson();

Antwoord 8

Kleine opmerking: statische functies zijn zichtbaar voor een vertaaleenheid, wat in de meeste praktische gevallen het bestand is waarin de functie is gedefinieerd. De fout die u krijgt, wordt gewoonlijk een overtreding van de One Definition Rule genoemd.

De standaard zegt waarschijnlijk zoiets als:

“Elk programma zal precies één definitie van elke niet-inline bevatten
functie of object dat in dat programma wordt gebruikt; geen diagnose
vereist.”

Dat is de C-manier om naar statische functies te kijken. Dit is echter verouderd in C++.

In C++ kunt u bovendien lidfuncties statisch declareren. Dit zijn meestal metafuncties, d.w.z. ze beschrijven/wijzigen niet het gedrag/de toestand van een bepaald object, maar werken op de hele klasse zelf. Dit betekent ook dat u geen object hoeft te maken om een statische lidfunctie aan te roepen. Verder betekent dit ook dat je alleen toegang krijgt tot statische lidvariabelen vanuit zo’n functie.

Ik zou aan Parrots voorbeeld het Singleton-patroon toevoegen dat is gebaseerd op dit soort statische lidfunctie om een enkel object te krijgen/gebruiken gedurende de levensduur van een programma.


Antwoord 9

Het antwoord op de statische functie hangt af van de taal:

1) In talen zonder OOPS zoals C, betekent dit dat de functie alleen toegankelijk is binnen het bestand waarin het is gedefinieerd.

2)In talen met OOPS zoals C++ betekent dit dat de functie rechtstreeks in de klasse kan worden aangeroepen zonder er een instantie van te maken.


Antwoord 10

Wat is een “static” functie in C?

Laten we bij het begin beginnen.

Het is allemaal gebaseerd op iets dat “koppeling” wordt genoemd:

Een identifier die in verschillende scopes of meer dan eens in dezelfde scope is gedeclareerd, kan worden gemaakt om naar hetzelfde object of dezelfde functie te verwijzen door een proces dat koppeling wordt genoemd. 29) Er zijn drie soorten koppeling: extern, intern , en geen.

Bron: C18, 6.2.2/1


“In de reeks vertaaleenheden en bibliotheken die een volledig programma vormen, duidt elke verklaring van een bepaalde identifier met externe koppeling hetzelfde object of functie aan. Binnen één vertaaleenheid, elke verklaring van een Identifier met Interne koppeling geeft hetzelfde object of de functie aan. Elke verklaring van een identifier zonder koppeling duidt op een unieke entiteit. “

BRON: C18, 6.2.2 / 2


Als een functie is gedefinieerd zonder een specificator voor opslagklasse, heeft de functie externAL-koppeling standaard:

“Als de verklaring van een identificator voor een functie geen specificator voor opslagklasse heeft, wordt de koppeling precies bepaald alsof deze is gedeclareerd met de opslagklassespecificator extern .”

BRON: C18, 6.2.2 / 5

Dat betekent dat – als uw programma is opgenomen van verschillende vertaaleenheden / bronbestanden (.cof .cpp) – De functie is zichtbaar in All Vertaaleenheden / bronbestanden Uw programma heeft.

Dit kan in sommige gevallen een probleem zijn. Wat als u F.E wilt gebruiken. Twee verschillende functie (definities), maar met dezelfde functienaam in twee verschillende contexten (eigenlijk de bestandscontext).

In C en C++, de staticopslagklasse kwalificatie toegepast op een functie bij bestandscope (niet een statische lidfunctie van a Klasse in C++ of een functie binnen een ander blok) komt nu om te helpen en betekent dat de respectieve functie alleen zichtbaar is in de vertaaleenheid / bronbestand die het is gedefinieerd in en niet in de andere TLU’s / bestanden.

“Als de declaratie van een bestandsomvang-ID voor een object of een functie de opslagklasse-specificatie staticbevat, heeft de identifier een interne koppeling. 30)”


  1. Een functiedeclaratie kan de statische specificatie van de opslagklasse alleen bevatten als deze zich binnen het bestandsbereik bevindt; zie 6.7.1.

Bron: C18, 6.2.2/3


Dus, een staticfunctie heeft alleen zin, iff:

  1. Uw programma bestaat uit verschillende vertaaleenheden/bronbestanden (.cof .cpp).

en

  1. U wilt het bereik van een functie beperken tot het bestand waarin de specifieke functie is gedefinieerd.

Als niet beidevan deze vereisten overeenkomen, hoeft u zich geen zorgen te maken over het kwalificeren van een functie als static.


Kanttekeningen:

  • Zoals reeds vermeld, heeft een staticfunctie absoluut geen enkel verschiltussen C en C++, aangezien dit een functie is die C++ heeft geërfd van C.

Het maakt niet uit dat er in de C++-gemeenschap een hartverscheurend debat is over de waardevermindering van kwalificerende functies als staticin vergelijking met het gebruik van naamloze naamruimtenin plaats daarvan, eerst geïnitialiseerd door een misplaatste paragraaf in de C++03-standaard, waarin het gebruik van statische functies als verouderd werd verklaard, wat al snel werd herzien door de commissie zelf en verwijderd in C++11.

Dit was het onderwerp van verschillende SO-vragen:

Naamloze/anonieme naamruimten versus statische functies

Superioriteit van naamloze naamruimte boven statische?

Waarom een naamloze naamruimte een “superieure” is alternatief voor statisch?

Beëindiging van het statische zoekwoord… niet meer?

In feite is het nog niet verouderd volgens de C++-standaard. Het gebruik van static-functies is dus nog steeds legitiem. Zelfs als naamloze naamruimtenvoordelen hebben, is de discussie over het al dan niet gebruiken van statische functies in C++ een kwestie van een mening (opinion-based) en daarmee niet geschikt voor deze website.


Antwoord 11

Aangezien de statische functie alleen in dit bestand zichtbaar is.
Eigenlijk kan de compiler wat optimalisatievoor je doen als je “statisch” declareert bij een bepaalde functie.

Hier is een eenvoudig voorbeeld.

main.c

#include <stdio.h>
static void test() 
{
    ghost(); // This is an unexist function.
}
int main()
{
    int ret = 0;
#ifdef TEST
#else
    test();
#endif
    return (ret);
} 

En compileren met

gcc -o main main.c

Je zult zien dat het is mislukt. Omdat je de ghost()-functie zelfs niet implementeert.

Maar wat als we het volgende commando gebruiken.

gcc -DTEST -O2 -o main main.c

Het succes, en dit programma kan normaal worden uitgevoerd.

Waarom? Er zijn 3 belangrijke punten.

  1. -O2 : Compiler-optimalisatieniveau minimaal 2.
  2. -DTEST: definieer test, dus test () zal niet worden gebeld.
  3. gedefinieerd “statisch” om te testen ().

Alleen als deze 3 voorwaarden allemaal waar zijn, kunt u het compileren passeren.
Vanwege deze “statische” verklaring kan de compiler die test () nooit in een ander bestand worden gebeld. Uw compiler kan Test () verwijderen bij het compileren. Omdat we geen test () nodig hebben, maakt het er niet uit of Ghost () is gedefinieerd of geïmplementeerd.

Other episodes