Forum: PC-Programmierung C - Warum '#include' wenn alle Funktionen 'extern'?


von Axel221 (Gast)


Lesenswert?

Hallo,

ich habe grad mit Dev-C++ ein seltsames Verhalten erzeugt, dass ich mir 
nicht erklären kann.

Hier das Programm:

______ foo.h ______
1
#ifndef FOO_H
2
#define FOO_H
3
4
int first_func();
5
6
#endif
_____________________



______ foo.c ______
1
int first_func()
2
{
3
  return 111;
4
}
_____________________



______ main.c ______
1
#include <stdio.h>
2
#include <stdlib.h>
3
4
int main(int argc, char *argv[]) {
5
  
6
  int a = first_func();
7
  
8
  printf("Das Ergebnis aus 'first_func' ist: %d\n", a);
9
  return 0;
10
}
_____________________




Die Ausgabe liefert nach erfolgreicher Kompilation:

Das Ergebnis aus 'first_func' ist: 111


Das verwundert mich, denn ich dachte, dass foo.h und foo.c ein Modul 
seien. In foo.h werden alle Funktionen definiert, die das Modul 
bereitstellt, in foo.c werden sie deklariert.

Wenn nun in main.c das Modul genutzt werden soll, so muss foo.h 
inkludiert werden.

Aber in dem Programm oben ist dies nicht der Fall, trotzdem kann auf die 
Funktion zugegriffen werden.

Warum?

Und noch schlimmer: In kann die Definition in foo.h streichen, selbst 
dann kompiliert und läuft das Programm mit korrektem Ergebnis. D.h., in 
main.c kann im Prinzip ohne foo.h auf alles in foo.c zugegriffen werden.

Was ist dann der Nutzen des Headers?

Vielen Dank,

Axel

: Bearbeitet durch User
von Noch ein Gast (Gast)


Lesenswert?

Du verwechselst Deklaration und Definition

Die Deklarationen im Headerfile machen deine Funktionen anderen 
Compile-Units bekannt.

von Fachmann (Gast)


Lesenswert?

Vielleicht gibt's irgendwo ein anderes foo? Und ich habe auch gemerkt, 
dass Dev-C++ einige bugs hat.

von Axel221 (Gast)


Lesenswert?

Noch ein Gast schrieb:
> Du verwechselst Deklaration und Definition
>
> Die Deklarationen im Headerfile machen deine Funktionen anderen
> Compile-Units bekannt.

So sollte es sein. Aber warum kompiliert das Programm von oben?

Ich mache die Funktion nicht bekannt in main.c, der Linker findet sie 
aber trotzdem.

Ist das wohldefiniertes Verhalten?

von Noch ein Gast (Gast)


Lesenswert?

Addendum:

Indem du Funktionen deklariert, stellst Du sicher, dass der Compiler die 
Parametrisierung checkt.

Du kommst auch ohne Deklaration klar, dein Programm wird 
-->vielleicht<-- funktionieren. Oder auch nicht.

von Lars (Gast)


Lesenswert?

Hast du mal ein clean gemacht?

von Axel221 (Gast)


Lesenswert?

Ich will meine Frage nicht etwas umformulieren:

In C sind alle Funktionen per default 'extern' (sofern nicht explizit 
'static'), also haben sie alle 'external linkage'.

Sind dann nicht alle Funktionen völlig global, also überall im Programm 
von jedem Modul aufrufbar, völlig unabhängig, ob der header eingebunden 
wird oder nicht?

von Noch ein Gast (Gast)


Lesenswert?

So ist es.

Die Deklaration in Headerfiles bringt dir aber den added-value, dass Die 
Funktionsaufrufe einem type-checking unterliegen.

von Axel221 (Gast)


Lesenswert?

Lars schrieb:
> Hast du mal ein clean gemacht?

Ja, geht trotzdem.

von Noch ein Gast (Gast)


Lesenswert?

..ich sollte dringend mein denglish überprüfen...

von Axel221 (Gast)


Lesenswert?

Noch ein Gast schrieb:
> So ist es.
>
> Die Deklaration in Headerfiles bringt dir aber den added-value, dass Die
> Funktionsaufrufe einem type-checking unterliegen.

Wenn also zwei Programmierer an einem großen Projekt arbeiten, dürfen 
sie niemals auf den gleichen Namen für zwei unterschiedliche 'extern' 
Funktionen kommen, selbst wenn sie in völlig unterschiedlichen Modulen 
deklariert und definiert werden?

Das erscheint mit nicht klug.

von Noch ein Gast (Gast)


Lesenswert?

Nun.
Deine Beobachtung ist absolut korrekt.

Du musst bedenken, dass C schon sehr alt ist. Es hat einen Grund, dass 
jede library ihre innewohnenden Funktionen mit einem einheitlichen 
prefix...nun.... prefixen.

Heute löst man das z.B. über Namespaces. usw.

von Noch ein Gast (Gast)


Lesenswert?

Hinweis:

2 Programmierer arbeiten eher an einem etwas "weniger kleinem" Projekt 
;-)

Die beiden sollten sich abstimmen können.

von Axel221 (Gast)


Lesenswert?

Eine letzte Frage:

Folgendes Programm (ist auf die schnelle nicht richtig, am besten 
Kommentare lesen und Erklärung unten):

____ foo.h ____

#ifndef FOO_H
#define FOO_H

// mit dieser Funktion kann mit **funktion auf eine beliebige andere 
Funktion
// gezeigt werden
void funktion1(int (**funktion)());

#endif
___________________



____ foo.c ____

#include <stdio.h>
#include <stdlib.h>

// eine static funktion in foo.c
static voic funktion2()
{
    printf("test\n");
};


// nun zeige ich mit **funktion auf die 'static' Funktion 'funktion2'
void funktion1(int (**funktion)(void))
{
  *funktion = &funktion2;
}
___________________



____ main.c ____

#include <stdio.h>
#include <stdlib.h>
#include "foo.h"

// prototype pointer auf eine Funktion
int (**funktion3)();

int main(int argc, char *argv[]) {


  // obwohl funktion2 'static' ist, zeigt nun 'funktion3' auf 
'funktion2'
  funktion1(funktion3);

  // hier kann 'function2' ausgeführt werden, obwohl sie 'static' in foo 
ist
  (*funktion3)();

  return 0;
}
___________________


Die Ausgabe ist: test


Also. Das Modul foo hat eine 'static'-Funktion, sie ist also nur in dem 
Modul zugänglich. Wenn ich aber nun über eine zweite 'extern' Funktion 
des Modul die Adresse der 'static'-Funktion nach außen weiterreiche, 
dann ist die Funktion auch in anderen Modulen anwendbar, obwohl sie 
'static' ist.

Kann man damit static nicht gewissermaßen aushebeln?

von Axel221 (Gast)


Lesenswert?

Die Einrückung tut mir leid, ich kann es nicht editieren.

von Noch ein Gast (Gast)


Lesenswert?

Ja kannst Du.

Du musst sehen:

C wurde erfunden als eine Art Hochsprachenassembler. Ergo kannst Du 
ALLES
machen.

Das ist einerseits gut, wenn du in der Systemprogrammierung zu hause 
bist.

Andererseits schlecht in der Anwendungsentwicklung. Wenn ich ehrlich 
bin...
man sollte auch in der Systemprogrammierung drauf verzichten.

Man muss vor allem nicht jedes Sprachmittel verwenden nur weil es geht.

Ein spannendes Thema. Was aber mit der verwendeten Programmiersprache 
nur marginal zu tun hat. Dieses Prinzip gilt m.E. immer...

von Axel221 (Gast)


Lesenswert?

Super. Vielen Dank für die Hilfe.

von Rolf M. (rmagnus)


Lesenswert?

Axel221 schrieb:
> Wenn also zwei Programmierer an einem großen Projekt arbeiten, dürfen
> sie niemals auf den gleichen Namen für zwei unterschiedliche 'extern'
> Funktionen kommen, selbst wenn sie in völlig unterschiedlichen Modulen
> deklariert und definiert werden?

Nein, wie kommst du darauf? Der Linker sollte eine Fehlermeldung 
liefern, daß die Funktion mehrfach definiert ist.

a.c:
1
void foo()
2
{
3
}
b.c:
1
void foo()
2
{
3
}
c.c:
1
int main()
2
{
3
    foo();
4
}
1
$ gcc a.c b.c c.c -ansi -pedantic -Wall -Wextra -g3
2
c.c: In function ‘main’:
3
c.c:3:5: warning: implicit declaration of function ‘foo’ [-Wimplicit-function-declaration]
4
     foo();
5
     ^
6
/tmp/ccUmCqp2.o: In Funktion `foo':
7
b.c:2: Mehrfachdefinition von `foo'
8
/tmp/ccYWMSXp.o:a.c:2: first defined here
9
collect2: error: ld returned 1 exit status

Axel221 schrieb:
> Also. Das Modul foo hat eine 'static'-Funktion, sie ist also nur in dem
> Modul zugänglich. Wenn ich aber nun über eine zweite 'extern' Funktion
> des Modul die Adresse der 'static'-Funktion nach außen weiterreiche,
> dann ist die Funktion auch in anderen Modulen anwendbar, obwohl sie
> 'static' ist.
>
> Kann man damit static nicht gewissermaßen aushebeln?

static bezieht sich nicht auf den Inhalt der Funktion, sondern auf ihren 
Namen. Dieser ist von außen nicht zugänglich. Wenn du einen Zeiger auf 
die Funktion definierst, kannst du diesen aber natürlich nutzen. Du hast 
damit also "static" nicht ausgehebelt. Es ist explizit vorgesehen, daß 
das so geht.

[EDIT]:
Das wird z.B. bei Callback-Funktionen gerne genutzt. Nimm z.B. qsort. 
Das erwartet einen Zeiger auf eine Vergleichsfunktion, die man selbst 
schreiben muß. Du willst aber nicht, daß deren Name den Namensraum 
unnötig füllt, und außer über diesen Zeiger soll sie eh nicht aufgerufen 
werden. Also machst du die Funktion static und überigbst den Zeiger auf 
diese an qsort, so daß sie von dort aufgerufen werden kann.

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Axel221 schrieb:
> Was ist dann der Nutzen des Headers?

Ist Dir schon aufgefallen, daß der Compiler beim Weglassen des Headers 
(bzw. des Funktionsprototypen) Warnungen ausgibt? Hast Du die 
abgeschaltet, oder ignorierst Du die?

von Karl H. (kbuchegg)


Lesenswert?

Axel221 schrieb:

> Aber in dem Programm oben ist dies nicht der Fall, trotzdem kann auf die
> Funktion zugegriffen werden.
>
> Warum?

Wie du schon raus gefunden hast, sind alle Funktion (die du nicht static 
definiert hast) von allen anderen Programmteilen sichtbar.

Gibt es keinen expliziten Prototypen, dann bastelt sich der Compiler 
selbst einen, indem er ihn aus der Verwendung an der Aufrufstelle 
ableitet. Dabei gelten Default-Regeln für die Argumente. Wenn etwas ein 
Pointer sein kann, dann ist er es auch. Wenn etwas ein double sein kann, 
dann ist er es auch. Wenn etwas für einen int zu gross ist, dann ist es 
ein long. Alles andere ist ein int.

Aus deiner Verwendung
1
...
2
  int a = first_func();
3
...
leitet der Compiler also her, dass die Deklaration der Funktion so 
aussehen muss
1
int first_func( void );

und zufällig ist das sogar richtig.

Würdest du die Funktion so benutzen
1
  int a = first_func( 5 );

dann würde der Compiler daraus schliessen, dass die Funktion diese 
Signatur haben müsste
1
int first_func( int arg );
Tja. das wäre falsch, denn deine Funktion sieht ja nicht so aus.

Genauso würde ein C Compiler aus
1
int main()
2
{
3
  first_func( 5.0 );
4
}
die implizite Funktionsdeklaration
1
int first_func( double arg );
ableiten, was genauso falsch wäre.

Du siehst also: Funktionsdeklarationen haben schon ihren Sinn. Denn 
findet sich keine, dann trifft der Compiler eben Standardannahmen, 
basierend darauf wie du die Funktion verwendest und welche Argumente du 
der Funktion mitgibst (nur der Returntyp der Funktion wird immer als int 
angenommen).
Das kann, wenn du sorgfältig bist, sogar zum korrekten Ergebnis führen. 
Muss es aber nicht.
Mit einer selbst geschriebenen Funktionsdeklaration bist du hingegen auf 
der sicheren Seite. Dann muss niemand was annehmen. Und wenn du in foo.c 
dann auch noch den eigenen Header includierst
____ foo.c ____
1
#include "foo.h"
2
3
int first_func()
4
{
5
  return 111;
6
}

dann prüft der Compiler auch gleich noch, ob der Funktionsprototyp mit 
der tatsächlichen Funktion übereinstimmt. Und wenn du dann noch die 
Funktionsdeklaration im Header als
1
#ifndef FOO_H
2
#define FOO_H
3
4
int first_func( void );
5
6
#endif
schreibst, dann wäre auch die noch vollständig korrekt. Denn so wie du 
die geschrieben hast, mit lediglich einer leeren Klammerung
1
int first_func();
bedeutet das, das über die Argumente der Funktion nichts näheres bekannt 
ist.

Funktionsdeklarationen, aka Protoypen, sind also das, was im Zirkus das 
Netz unter den Trapezartisten ist. Klar, wenn man es kann und die Regeln 
genau kennt, dann kann man darauf verzichten. Aber dann darf auch nichts 
schief gehen.
Und die Regeln wiederrum sind so umfangreich und zum Teil nicht intuitiv 
bzw. historisch gewachsen, dass man ein C Buch benötigt, das einen da 
durchführt um sie zu lernen.

: Bearbeitet durch User
von Masl (Gast)


Lesenswert?

Der Compiler sollte eine Warnung der Art "Implicit declaration of 
function foo..." bringen.
Das heißt, die Funktion wurde vorher nicht explizit bekannt gemacht.

Durch den Aufruf geht er aber implizit davon aus, dass es foo() irgendwo 
gibt.
Der Linker findet sie auch, deswegen kommt es nicht zu einem 
Linkerfehler.

von Sebastian V. (sebi_s)


Lesenswert?

Dieses Verhalten ist übrigens C spezifisch und in C++ müssen Funktionen 
immer vorher deklariert werden.

von Axel221 (Gast)


Lesenswert?

Rufus Τ. Firefly schrieb:
> Axel221 schrieb:
>> Was ist dann der Nutzen des Headers?
>
> Ist Dir schon aufgefallen, daß der Compiler beim Weglassen des Headers
> (bzw. des Funktionsprototypen) Warnungen ausgibt? Hast Du die
> abgeschaltet, oder ignorierst Du die?

Also Dev-C++ gibt keine Warnungen aus, "Error: 0" und "Warnings: 0". Ich 
kenne mich mit DEV-C++ nicht aus.

Wenn ich bei GCC eine Warnung sehe, behebe ich sie sofort. Aber wenn man 
mal kurz drüber nachdenkt, was sie eigentlich bedeutet, dann stößt man 
auf Verständnisprobleme wie das Jetzige.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Axel221 schrieb:
> Also Dev-C++ gibt keine Warnungen aus

Tatsächlich?

Dann würde ich mir die Compilereinstellungen noch mal sehr genau 
ansehen.

von Axel221 (Gast)


Lesenswert?

Rolf Magnus schrieb:
>
> static bezieht sich nicht auf den Inhalt der Funktion, sondern auf ihren
> Namen. Dieser ist von außen nicht zugänglich. Wenn du einen Zeiger auf
> die Funktion definierst, kannst du diesen aber natürlich nutzen. Du hast
> damit also "static" nicht ausgehebelt. Es ist explizit vorgesehen, daß
> das so geht.

Ein Programmbeispiel:

Angenommen ich habe zwei Module, foo.h + foo.c und bar.h + bar.c.

Beide Module liegen in jeweils einem eigenen Ordner auf GLEICHER Ebene. 
Zusätzlich liegt main.c ebenfalls auf dieser Ebene.


+-+-main.c
|
|
+-+-FOO
| |
| |
| +-+-foo.h
|   |
|   +-foo.c
|
+-+-BAR
| |
| |
| +-+-bar.h
|   |
|   +-bar.c
|


FOO kann bar.h nicht inkludieren und umgekehrt, da ich sonst beim 
inkludieren eine Ordnerebene "höher" gehen müsste, was - soweit ich weis 
- in C eine schlechte Praxis ist.

Wenn nun aber in bar.c eine Funktion aus foo.c aufrufen werden soll, die 
dann auch noch "static" ist, gibt es also folgende Möglichkeit:

Ich übergebe beim Programmstart einfach den Pointer der entsprechenden 
"static" Funktion aus foo.c nach bar.c über init()-Funktionen. Dabei 
hilft mir main.c mit der init()-Funktion.

In bar.c wird der Pointer gespeichert und so kann die Funktion von dort 
aufgerufen werden, wenn beide Module gleiche Funktionsprototypen haben.


Warum liegen beide auf gleicher Ebene?
Weil die Abstraktion des Programms das als beste Lösung ergeben hat.
Angenommen FOO sammelt Daten über reiche Kunden und BAR über arme Kunden 
einer Bank. Beide sind gleichwertig, aber ab und an wechseln sie auch 
die Seite.

Ist das eine legale und gute Praxis?

Wenn nein, was wäre eine bessere Lösung?

von Karl H. (kbuchegg)


Lesenswert?

Axel221 schrieb:

> FOO kann bar.h nicht inkludieren und umgekehrt, da ich sonst beim
> inkludieren eine Ordnerebene "höher" gehen müsste, was - soweit ich weis
> - in C eine schlechte Praxis ist.

C hat dazu überhaupt nichts zu sagen.

Ob das sinnvoll ist oder nicht, oder ob eine bessere Lösung darin 
bestehen würde, die Ordnerhierarchie zu ändern, muss man am konkreten 
Fall festmachen.

Aber C an sich ist das völig egal.

> Wenn nun aber in bar.c eine Funktion aus foo.c aufrufen werden soll, die
> dann auch noch "static" ist, gibt es also folgende Möglichkeit:

Wenn in bar.c eine Funktion aus foo.c aufgerufen werden soll, dann ist 
die entsprechende Funktion nicht static zu machen, ein entsprechender 
Protoyp kommt ins Header File, bar.c inkludiert diesen Header File und 
ruft die Funktion auf.

Alles andere sind Sonderfälle, die in vielleicht 1 von 100 Fällen mal 
vorkommen.

Weniger künsteln und mehr straight forward arbeiten. Dann programmierst 
du automatisch besser.

von Karl H. (kbuchegg)


Lesenswert?

Gewöhn dir die Sache mit dem Funktionspointer auf static gleich wieder 
ab. Du liest da zu viel hinein, nur weil man das machen könnte. Nicht 
alles was man machen könnte ist auch sinnvoll.

Wenn ein Modul eine Funktion exportiert, dann ist die entsprechende 
Funktion nicht static. Es mag vereinzelt Ausnahmen geben, aber es ist 
nicht der Regelfall.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Axel221 schrieb:
> Also Dev-C++ gibt keine Warnungen aus, "Error: 0" und "Warnings: 0". Ich
> kenne mich mit DEV-C++ nicht aus.

Handle, wie hier beschrieben:

  https://answers.yahoo.com/question/index?qid=20130401061142AAKy5nB

Dann wirst du die folgende Warnung erhalten:
1
main.c: In function ‘main’:
2
main.c:6:3: warning: implicit declaration of function ‘first_func’ [-Wimplicit-function-declaration]
3
   int a = first_func();
4
   ^

Mit einem
1
#include "foo.h"

in main.c verschwindet die Warnung, wieder, weil der Compiler jetzt eine
explizite Deklaration von first_func sieht.

Die gleiche Include-Zeile solltest du auch am Anfang von foo.c einfügen.
Das gibt dem Compiler die Möglichkeit zu überprüfen, ob die Deklaration
in foo.h mit der Definition in foo.c übereinstimmt. Wenn nicht (weil du
bspw. der Funktion first_func nachträglich noch ein Funktionsargument
verpasst), meckert der Compiler entsprechend.

Man sollte also Header-Files mit Funktionsdeklarationen sowohl dort
includen, wo die Funktionen definiert werden, als auch dort, wo sie
aufgerufen werden. Dann garantiert die modulübergreifende Typsicherheit
für diese Funktionen.

von Karl H. (kbuchegg)


Lesenswert?

Die Aufgabe von static auf Compilation Unit Ebene ist es, Dinge 
innerhalb dieser Compilation Unit insofern soweit zu verstecken, dass 
sie im globalen Namensraum nicht mehr auftauchen und es daher bei 
unterschiedlichen Modulen nur insofern zu einem Name-Clash kommen kann, 
als es ihre externen Interfaces betrifft.

static in einer derartigen Verwendung verhindert nur, dass andere Module 
wissen bzw. sich darum kümmern müssen, dass es diese Funktion oder 
Variable überhaupt gibt. static markiert ein Implementierungsdetail 
eines Moduls, das ausser diesem Modul niemand anderen etwas angeht. 
Genau in diesem Sinne sollst du es auch benutzen.
Das man derartiges aushebeln kann, ist eine andere Geschichte. Aber 
warum sollst du als Programmierer eines Moduls deine eigene 
Namens-Barriere aushebeln? Das ist so, wie wenn du die Haustür 
abschliesst, den Schlüssel unter die Matte legst und an die Tür einen 
Zettel mit "Schlüssel liegt unter der Matte" hängst. Klar, du kannst das 
machen. Du kannst aber auch allen das Leben einfacher machen und ganz 
einfach nicht zusperren.

: Bearbeitet durch User
von Axel221 (Gast)


Lesenswert?

Karl Heinz schrieb:
> Gewöhn dir die Sache mit dem Funktionspointer auf static gleich
> wieder
> ab. Du liest da zu viel hinein, nur weil man das machen könnte. Nicht
> alles was man machen könnte ist auch sinnvoll.
>
> Wenn ein Modul eine Funktion exportiert, dann ist die entsprechende
> Funktion nicht static. Es mag vereinzelt Ausnahmen geben, aber es ist
> nicht der Regelfall.

Verstehe ich. Nur mir scheint, als gäbe es Fälle, in denen das nicht so 
einfach ist. Die Ordnerstruktur mit den Dateien widerspricht der 
Programmsturktur bzw. sind nicht voll kompatibel.

Ich denke mir mal ein gutes (unwiderlegbares) Beispiel aus. Wenn mir 
eines einfällt, poste ich es.

von Axel221 (Gast)


Lesenswert?

Karl Heinz schrieb:

> (Namens-Barriere aushebeln?) Das ist so, wie wenn du die Haustür
> abschliesst, den Schlüssel unter die Matte legst und an die Tür einen
> Zettel mit "Schlüssel liegt unter der Matte" hängst. Klar, du kannst das
> machen. Du kannst aber auch allen das Leben einfacher machen und ganz
> einfach nicht zusperren.

Der ist gut. So gut, dass ich ihm das merke. Wenn mal jemand zu mir mit 
dem gleichen Problem kommt, erzähle ich deinen Vergleich als wäre es mir 
selbst eingefallen ;-)

von Rolf Magnus (Gast)


Lesenswert?

Axel221 schrieb:
> FOO kann bar.h nicht inkludieren und umgekehrt, da ich sonst beim
> inkludieren eine Ordnerebene "höher" gehen müsste, was - soweit ich weis
> - in C eine schlechte Praxis ist.

Dafür gibt es den Compiler-Kommadonzeilenparameter -I. Damit gibt man 
die Verzeichnisse an, in denen nach Headern gesucht werden soll.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Axel221 schrieb:
> FOO kann bar.h nicht inkludieren und umgekehrt, da ich sonst beim
> inkludieren eine Ordnerebene "höher" gehen müsste

Musst Du nicht; Du kannst auch beide Verzeichnisse zum Include-Pfad 
hinzufügen.

Abgesehen davon: Selbstverständlich ist es legitim,

#include "../bar/bar.h"

zu verwenden.

Ob das eine sinnvolle Quelltextstrukturierung ist, ist eine ganz 
andere Frage.

von Andreas D. (rackandboneman)


Lesenswert?

Wer mehr Warnungen will, nimmt lint hinzu.

von (prx) A. K. (prx)


Lesenswert?

Solche impliziten Deklarationen sind heute nur noch eine Verneigung vor 
Dennis Ritchie, der in grauer Vorzeit auf diese dusselige Idee kam, und 
man alte Programme zwar gerne mit Warnungen bewerfen, aber nicht über 
Bord schmeissen wollte.

von Andreas D. (rackandboneman)


Lesenswert?

Die Notwendigkeit zwei Funktionsköpfe zu führen ist eigentlich auch ein 
antikes Relikt :)

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.