Forum: Compiler & IDEs "static"-Funktionen


von Dennis S. (eltio)


Lesenswert?

Hallo zusammen,

ich habe ein relativ komplexes Programm geschrieben, die Funktionen in 
den Header-Dateien deklariert und in den Quelldateien definiert. Soweit 
so gut.. jetzt war meine Idee, die "Hilfsfunktionen" mit dem 
Schlüsselwort static nur in den jeweiligen Dateien sichtbar zu machen. 
Beispiel

Datei: Foo.h
1
void FooMain(int32_t *data, int32_t cnt);
2
float FooAux(float x, const int8_t prec);

Datei: Foo.c
1
void FooMain(int32_t *data, int32_t cnt){...}
2
float FooAux(float x, const int8_t prec){...}

FooAux ist die Hilfsfunktion, die nur in Foo.c gebraucht wird und 
sichtbar sein soll. Ein einfaches "static" davor in Header und Quellcode 
führt zu:
1
warning: FooAuxdeclared 'static' but never defined

Ich habe schon den einen oder anderen Thread dzu gelesen, aber 
verstanden habe ich es noch nicht richig. Kann mir da jemand 
weiterhelfen?

Gruß Dennis

von Rene H. (Gast)


Lesenswert?

Zeig doch mal wie Du das gemacht hast. Den Teil hast Du nämlich 
unterschlagen.

Grüsse,
René

von Dennis S. (eltio)


Lesenswert?

Ich hatte es beschrieben... ;-)

Datei Foo.h
1
void FooMain(int32_t *data, int32_t cnt);
2
static float FooAux(float x, const int8_t prec);

Datei: Foo.c
1
void FooMain(int32_t *data, int32_t cnt){...}
2
static float FooAux(float x, const int8_t prec){...}

Auch "interessant": wenn ich die Deklaration aus dem Header rausnehme 
(hatte ich in einem anderen Forum gelesen) und nur vor die Funktion im 
C-File "static" schreibe dann kommt:
1
error: conflicting types for 'FooAux'

von Klaus F. (kfalser)


Lesenswert?

Wenn deine Funktion FooAux außen nicht sichtbar und deshalb static sein 
soll, dann gibt es auch keinen Grund, diese im Headerfile zu 
deklarieren.

Lösche sie aus dem Header-File und mache sie static im C-Quellfile und 
gut ist's.

Das Header-File dient nur dazu, gemeinsam benutzte Funktion in alle 
C-Files identisch zu deklarieren, damit die Verwendung überall 
übereinstimmt.

von mar IO (Gast)


Lesenswert?

Wenn FooAux() nur im Modul Foo.c gebraucht wird, dann lass halt die 
Deklaration in Foo.h weg und schreibe sie dafür in Foo.c => Nicht 
sichtbar für andere Module, da man Foo.c idR nicht einbinden wird (mit 
der Annahme, dass Foo.h irgendwo eingebunden wird).

von Klaus F. (kfalser)


Lesenswert?

Dennis S. schrieb:
> Auch "interessant": wenn ich die Deklaration aus dem Header rausnehme
> (hatte ich in einem anderen Forum gelesen) und nur vor die Funktion im
> C-File "static" schreibe dann kommt:error: conflicting types for
> 'FooAux'

Das kommt wahrscheinlich daher, dass die Funktion schon verwendet wird, 
bevor sie effektiv definiert wurde.

Gib die Funktion ganz an den Anfang.

von Dennis S. (eltio)


Lesenswert?

Dennis S. schrieb:
> Auch "interessant": wenn ich die Deklaration aus dem Header rausnehme
> (hatte ich in einem anderen Forum gelesen) und nur vor die Funktion im
> C-File "static" schreibe dann kommt:
>
1
error: conflicting types for 'FooAux'

KlatschVornKopf Ich hatte die Hilfsfunktion "FooAux" unter die 
"FooMain" im C-File geschrieben.. Klar, dass das nicht läuft...

Aber mal noch eine abschließende Frage: wenn ich die Sichtbarkeit so 
einfach einschränken kann, in dem ich die Deklaration aus dem Header 
nehme: wann benötgt man dann das Schlüsselwort static im Zusammenhang 
mit Funktionen?

Gruß Dennis

von Axel S. (a-za-z0-9)


Lesenswert?

Dennis S. schrieb:

> wenn ich die Deklaration aus dem Header rausnehme
> (hatte ich in einem anderen Forum gelesen) und nur vor die Funktion im
> C-File "static" schreibe dann kommt:error: conflicting types for
> 'FooAux'

Wahrscheinlich hast du FooAux() in Foo.c aufgerufen bevor du die 
Funktion definiert hast. Der Compiler nimmt bei der Verwendung einer 
nicht deklarierten Funktion diese Signatur an:

1
int FooAux(void)

Wenn das dann mit der realen Definition von FooAux() nicht mehr 
zusammenpaßt, bekommst du diese Fehlermeldung.

Korrekt ist, daß man Hilfsfunktionen nicht im offiziellen Headerfile 
deklariert. Man hat dann im wesentlichen 3 Möglichkeiten:

1. man definiert die Funktionen in Foo.c in der Reihenfolge, daß jede 
Funktion erst definiert wird bevor sie woanders aufgerufen wird. 
Problem: das geht nicht immer. Und es ist mühsam.

2. man deklariert alle Hilfsfunktionen am Anfang von Foo.c. Danach kann 
man die Definition der Funktionen in beliebiger Reihenfolge vornehmen.

3. man schreibt die Deklaration der Hilfsfunktionen in ein privates 
Headerfile (z.B. Foo_priv.h), das man dann in Foo.c includiert.

In meinen Augen ist 2. die sauberste variante. 3. ist etwas exotisch und 
lohnt auch nur, wenn man sehr viele Hilfsfunktionen hat.


XL

von Stefan E. (sternst)


Lesenswert?

Dennis S. schrieb:
> Aber mal noch eine abschließende Frage: wenn ich die Sichtbarkeit so
> einfach einschränken kann, in dem ich die Deklaration aus dem Header
> nehme: wann benötgt man dann das Schlüsselwort static im Zusammenhang
> mit Funktionen?

Der erste Teil ist schlicht falsch. Das Weglassen der Deklaration aus 
dem "öffentlichen" Header schränkt die Sichtbarkeit keineswegs ein. 
Jeder kann die Funktion weiterhin nutzen indem er einfach den Prototyp 
in seinem Code selbst hinzufügt.

von Axel S. (a-za-z0-9)


Lesenswert?

Dennis S. schrieb:
> Aber mal noch eine abschließende Frage: wenn ich die Sichtbarkeit so
> einfach einschränken kann, in dem ich die Deklaration aus dem Header
> nehme: wann benötgt man dann das Schlüsselwort static im Zusammenhang
> mit Funktionen?

Damit der Compiler das Symbol für diese Funktion im Objektfile 
entsprechend markiert. Die Dereferenzierung "Aufruf der Hilfsfunktion" 
auf die tatsächliche Adresse der Hilfsfunktion macht ja der Linker. Und 
der muß wissen, daß die Hilfsfunktion nur für Dereferenzierungen in 
diesem Modul verwendet werden darf. Erst das erlaubt den gleichen 
Funktionsnamen für Hilfsfunktionen in unterschiedlichen 
Übersetzungseinheiten.


XL

von Karl H. (kbuchegg)


Lesenswert?

Dennis S. schrieb:

> Aber mal noch eine abschließende Frage: wenn ich die Sichtbarkeit so
> einfach einschränken kann, in dem ich die Deklaration aus dem Header
> nehme


Das stimmt nun wieder nicht.
Du schränkst die Sichtbarkeit nicht durch rausnehmen aus einem Header 
File ein.

Ein Header File hat so gesehen eigentlich nur organisatorische Gründe. 
Für den eigentlichen C-Compiler spielt ein Header File überhaupt keine 
Rolle. Vor allen Dingen deshalb nicht, weil der eigentliche Compiler ein 
Header File überhaupt nicht zu Gesicht kriegt.

> wann benötgt man dann das Schlüsselwort static im Zusammenhang
> mit Funktionen?

weil dich nix und niemand drann hindert, das ganze so zu schreiben:
File: a.c
1
void foo()
2
{
3
  ...
4
}

File b.c
1
extern foo( void );
2
3
void bar()
4
{
5
  ...
6
  foo();
7
}

der Default für Funktionen ist es, dass sie aus einer 
Übersetzungseinheit heraus exportiert werden.
Ob du das machst, indem du in einem anderen C-File eine explizite 
'extern' Deklaration reinbaust, oder ob du die extern Deklaration in ein 
Header File schreibst und dieses includierst, spielt keine Rolle (und 
kommt effektiv aufs gleiche raus).
Und ja, bei Funktionen darf man 'extern' weglassen, weil durch die 
Schreibweise klar ist, ob es sich um eine Deklaration oder einer 
Definition handelt.

Edit:
Und nochmal, weil das wichtig ist:
Alle Zeilen, die mit # beginnen, sind Anweisungen an den Präprozessor!
Das ist im Grunde nichts anderes als ein Texteditor, der den 
Programmtext modifiziert, ehe ihn der Compiler zu Gesicht kriegt.
Der Präprozessor macht bei einer Zeile
1
#include "test.h"
nichts anderes, als den Inhalt der Datei test.h an der Stelle in den 
Text einzufügen, an der die #include Zeile steht.

D.h. für den Compiler besteht zwischen
test.h
1
extern foo( void );
bar.c
1
#include "test.h"
2
3
void bar()
4
{
5
  ...
6
  foo();
7
}

und
bar2.c
1
extern foo( void );
2
3
void bar()
4
{
5
  ...
6
  foo();
7
}
KEIN Unterschied! Denn der Präprozessor macht aus der 'include-Form' 
durch Text-Ersetzung exakt die 2.te Form, ehe sich dann der C-Compiler 
den Text vornimmt.

: Bearbeitet durch User
von Karlo (Gast)


Lesenswert?

Durch "static" kann der Compiler auch aggresiver optimieren, da er nicht 
mehr davon ausgehen muss dass die Funktion "von außen" aufgerufen wird.
Somit kennt er jeden Aufruf der Funktion und kann diese inlinen oder 
ganz weglassen, etc.

von Axel S. (a-za-z0-9)


Lesenswert?

Nachtrag:

Axel Schwenke schrieb:
> Dennis S. schrieb:
>> ... wann benötgt man dann das Schlüsselwort static im Zusammenhang
>> mit Funktionen?
>
> Damit der Compiler das Symbol für diese Funktion im Objektfile
> entsprechend markiert.

Tatsächlich kann der Compiler noch mehr machen, wie ich gerade bei einem 
Test festgestellt habe. Testobjekt ist ein simples "nur main.c" Projekt, 
das außer main() noch zwei ISR und zwei Hilfsfunktionen enthält. Jede 
der Hilfsfunktionen wird in main() nur je genau einmal aufgerufen.

Normal erzeugt der avr-gcc für jede der beiden Hilfsfunktionen einen 
Funktionskörper a'la
1
.global funktionsname
2
funktionsname:
3
...
4
ret

(zu finden im Listing main.lst)

und ruft die Funktionen entsprechend mit
1
rcall funktionsname

auf. Natürlich mit dem Umschaufeln von Argumenten und Returnwert in die 
entsprechenden Register.

Wenn ich die Hilfsfunktionen hingegen als static definiere, dann werden 
sie direkt inlined. Das spart auch signifikant Code. Von 780 Bytes geht 
die Größe auf 740 Bytes zurück.


XL

von Dennis S. (eltio)


Lesenswert?

Klasse! Das hat mich mal wieder weitergebracht! :-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.