Hallo Michael,
Mit der Definition des Funktionsprototypen ist mir aber
trotzdem nicht ganz klar? Was meinst du mit
„wird als int func(…)“ angenommen? Geht der Compiler
einfach davon aus, dass die Funktion „schon irgendwann
definiert werden wird“? Mit alledem kann ich aber noch
leben.
Ja, wie Peter schon andeutete, seit dem alten K&R-C,
sowie IMHO auch im alten Ansi-C ist eine Deklaration einer
Variablen oder einer Funktion „ohne Typ“ für den Compiler
so zu verstehen, dass diese den Typ „vorzeichenbehaftetes
Maschinenwort“ besitzen,z.B.
const VAR; ==\> const int VAR;
Wird beim Compilieren auf einen Funktionsaufruf gestossen,
der noch nicht (forward-)deklariert wurde, nimmt der Compiler
an, dass es sich um eine Funktion mit obigem Rückgabetyp
handelt (und mit beliebigen Parametern!).
VisualStudio6 warnt dementsprechend:
warning C4013: 'testfunk' undefined; assuming extern returning int
Du kannst also nach altem Ansi eine
Funktion nicht deklarieren, mit beliebigen
Argumenten versehen und in einem anderen Modul
mit ganz anderen formalen Parametern stehen haben:
Modul_1:
...
...
tab[10] = -1;
n = 44;
n = testfunk(tab, n, argc); /\* testfunk() nicht deklariert /\*
...
Modul_2:
void testfunk(int a)
{
printf("Testfunk, %i\n", a);
}
Das geht!
Was ich überhaupt nicht verstehe ist die Speicherallokierung
für den Array.
Aber wieso geht das auch dynamisch? Ersetzt der Compiler das
dann automatisch durch
int *tab = malloc(n*sizeof(int));
Nein. Der Compiler unterteilt den aktuellen Block (Funktion)
in Unterblöcke, in denen die Variablen eben verwendet werden.
Das kann er (obwohl es nicht „offiziell“ ist) machen, muss
es aber nicht.
Der Schlüssel: lokale Variablen werden auf dem sog.
Stack abgelegt. Der Stack ist lediglich ein Speicher-
segment, das dem Programm zur Verfügung steht und der
von „oben nach unten“ (hinten nach vorn) belegt wird.
Dazu wird bei einer Allokation lediglich der Stack-
Speicherzähler „heruntergezählt“. Am Anfang hat das
Programm z.B. einen Stack von 1MB vom Prozess einge-
richtet bekommen. Der Stack-Zähler steht auf 0x100000
Wenn in C eine Funktion aufgerufen wird, bekommt sie
vom aufrufenden Programmteil die „Argumente“ auf dem
Stack angeordnet, dann wird sie aufgerufen:
int a = 10;
int b = 20;
**testfunk(a, b)
:weiter
c = a + b;**
...
wird -->
SUB STACKPOINTER 4 // a-\> stack jetzt bei 1,000,000 - 4
WRITE\_STACK a
SUB STACKPOINTER 4 // b-\> stack jetzt bei 1,000,000 - 8
WRITE\_STACK b
SUB STACKPONTER 4 // Adresse von Ausführungspunkt nach testfunk()
WRITE\_STACK ADDR(:weiter)
CALL \_\_testfunk // Aufruf der Funktion
ADD STACKPOINTER 12 // nach Aufruf räumt C den Stack auf!
WEITER: ADD C = A + B
...
Aber wenn das so einfach ginge, wofür gibt es dann überhaupt
noch die malloc-Funktion?
Die malloc, free, alloc usw.-Funktionen sind dadurch
charakterisiert, dass sie einen Speicherblock zur
Verfügung stellen, der nicht auf dem Stack des
laufenden Programms liegt. Daher „überdauert“ der
Block auf Funktionsaufrufe beliebiger Tiefe und
gefährdet nicht den überhaupt zur Verfügung stehenden
Stackspeicher durch zu hohes/schnelles „Abzwacken“.
Zu Zeiten von Turbopascal sagte man, der Speicher
kommt vom „Heap“. Das ist eigentlich nur ein Interface
in die Speicherverwaltung des Betriebssystems, das
von der Laufzeitbibliothek des Compilers (eben new/alloc)
„optimiert“ wird (schnelle Bereitstellung, Vermeidung
von Fragmentierung etc. etc.)
Grüße
CMБ