Betrachten Sie den folgenden "C" Code:
#include<stdio.h>
main()
{
printf("func:%d",Func_i());
}
Func_i()
{
int i=3;
return i;
}
Func_i()
wird am Ende des Quellcodes definiert und es wird keine Deklaration bereitgestellt, bevor sie in verwendet wird main()
. Genau zu dem Zeitpunkt , wenn der Compiler sieht Func_i()
in main()
, kommt es aus dem heraus main()
und findet heraus Func_i()
. Der Compiler findet irgendwie den von zurückgegebenen Wert Func_i()
und gibt ihn an printf()
. Ich weiß auch, dass der Compiler den Rückgabetyp von nicht finden kann Func_i()
. Es wird standardmäßig nimmt (Vermutungen?) , Um den Rückgabetyp von Func_i()
sein int
. Das heißt, wenn der Code float Func_i()
den folgenden Fehler hätte, würde der Compiler ihn ausgeben: Konflikttypen fürFunc_i()
.
Aus der obigen Diskussion sehen wir, dass:
Der Compiler kann den von zurückgegebenen Wert finden
Func_i()
.- Wenn der Compiler den Wert zurückgeführt, indem finden können ,
Func_i()
indem kommen aus dermain()
den Quellcode und die Suche nach unten, dann warum kann es nicht die Art von Func_i () finden, die sich explizit erwähnt.
- Wenn der Compiler den Wert zurückgeführt, indem finden können ,
Der Compiler muss wissen, dass
Func_i()
es sich um den Typ float handelt. Aus diesem Grund tritt der Fehler von Typen auf, bei denen Konflikte auftreten.
- Wenn der Compiler weiß, dass
Func_i
es sich um den Typ float handelt, warum wird dann immer noch davon ausgegangenFunc_i()
, dass es sich um den Typ int handelt, und es wird der Fehler in Konflikt stehender Typen ausgegeben? Warum macht es nicht gewaltsam,Func_i()
vom Typ float zu sein?
Ich habe den gleichen Zweifel mit der Variablendeklaration . Betrachten Sie den folgenden "C" Code:
#include<stdio.h>
main()
{
/* [extern int Data_i;]--omitted the declaration */
printf("func:%d and Var:%d",Func_i(),Data_i);
}
Func_i()
{
int i=3;
return i;
}
int Data_i=4;
Der Compiler gibt den Fehler aus: 'Data_i' nicht deklariert (erstmalige Verwendung in dieser Funktion).
- Wenn der Compiler sieht
Func_i()
, geht er zum Quellcode, um den von Func_ () zurückgegebenen Wert zu finden. Warum kann der Compiler nicht dasselbe für die Variable Data_i tun?
Bearbeiten:
Ich kenne die Details der inneren Arbeitsweise von Compiler, Assembler, Prozessor usw. nicht. Die Grundidee meiner Frage ist, dass ich den Rückgabewert der Funktion nach der Verwendung endlich im Quellcode wiedergebe (schreibe) Von dieser Funktion aus kann der Computer mit der Sprache "C" diesen Wert finden, ohne dass ein Fehler auftritt. Warum kann der Computer den Typ nicht auf ähnliche Weise finden? Warum kann der Typ von Data_i nicht gefunden werden, da der Rückgabewert von Func_i () gefunden wurde? Selbst wenn ich die extern data-type identifier;
Anweisung verwende, sage ich nicht, dass der Wert von diesem Bezeichner (Funktion / Variable) zurückgegeben werden soll. Wenn der Computer diesen Wert findet, warum findet er dann den Typ nicht? Warum brauchen wir überhaupt die Forward-Deklaration?
Vielen Dank.
quelle
Func_i
ungültig gemacht. Es gab nie eine Regel, um undefinierte Variablen implizit zu deklarieren, daher war das zweite Fragment immer fehlerhaft. (Ja, Compiler akzeptieren das erste Beispiel immer noch, da es unter C89 / C90 gültig war, wenn es schlampig war.)Antworten:
Weil C eine statisch typisierte , schwach typisierte , kompilierte Sprache mit einem Durchgang ist .
Single-Pass bedeutet, dass der Compiler nicht nach vorne schaut, um die Definition einer Funktion oder Variablen zu sehen. Da der Compiler nicht in die Zukunft blickt, muss die Deklaration einer Funktion vor der Verwendung der Funktion erfolgen, da der Compiler sonst nicht weiß, um welche Typensignatur es sich handelt. Die Definition der Funktion kann jedoch später in derselben Datei oder sogar in einer anderen Datei insgesamt erfolgen. Siehe Punkt 4.
Die einzige Ausnahme ist das historische Artefakt, bei dem angenommen wird, dass nicht deklarierte Funktionen und Variablen vom Typ "int" sind. Die moderne Praxis besteht darin, implizite Typisierung zu vermeiden, indem Funktionen und Variablen immer explizit deklariert werden.
Statisch typisiert bedeutet, dass alle Typinformationen zur Kompilierzeit berechnet werden. Diese Informationen werden dann verwendet, um Maschinencode zu generieren, der zur Laufzeit ausgeführt wird. In C gibt es kein Konzept für die Typisierung zur Laufzeit. Einmal ein Int, immer ein Int, einmal ein Float, immer ein Float. Diese Tatsache wird jedoch durch den nächsten Punkt etwas verdeckt.
Schwach geschrieben bedeutet, dass der C-Compiler automatisch Code für die Konvertierung zwischen numerischen Typen generiert, ohne dass der Programmierer die Konvertierungsoperationen explizit angeben muss. Aufgrund der statischen Typisierung wird die gleiche Konvertierung jedes Mal im Programm auf die gleiche Weise ausgeführt. Wenn ein Gleitkommawert an einer bestimmten Stelle im Code in einen Int-Wert konvertiert wird, wird ein Gleitkommawert an dieser Stelle im Code immer in einen Int-Wert konvertiert. Dies kann zur Laufzeit nicht geändert werden. Der Wert selbst kann sich natürlich von einer Ausführung des Programms zur nächsten ändern, und bedingte Anweisungen können ändern, welche Codeabschnitte in welcher Reihenfolge ausgeführt werden, aber ein bestimmter einzelner Codeabschnitt ohne Funktionsaufrufe oder Bedingungen führt immer die exakte aus gleiche Operationen, wenn es ausgeführt wird.
Kompiliert bedeutet, dass der Prozess der Analyse des lesbaren Quellcodes und der Umwandlung in maschinenlesbare Anweisungen vollständig ausgeführt wird, bevor das Programm ausgeführt wird. Wenn der Compiler eine Funktion kompiliert, weiß er nicht, auf was er weiter unten in einer bestimmten Quelldatei stößt. Sobald die Kompilierung (und das Zusammenstellen, Verknüpfen usw.) abgeschlossen ist, enthält jede Funktion in der fertigen ausführbaren Datei numerische Zeiger auf die Funktionen, die sie beim Ausführen aufruft. Deshalb kann main () eine Funktion weiter unten in der Quelldatei aufrufen. Wenn main () tatsächlich ausgeführt wird, enthält es einen Zeiger auf die Adresse von Func_i ().
Maschinencode ist sehr, sehr spezifisch. Der Code zum Hinzufügen von zwei Ganzzahlen (3 + 2) unterscheidet sich von dem zum Hinzufügen von zwei Gleitkommazahlen (3.0 + 2.0). Beides unterscheidet sich vom Hinzufügen eines Int zu einem Float (3 + 2.0) und so weiter. Der Compiler bestimmt für jeden Punkt in einer Funktion, welche exakte Operation an diesem Punkt ausgeführt werden muss, und generiert Code, der diese exakte Operation ausführt. Danach kann es nicht mehr geändert werden, ohne die Funktion neu zu kompilieren.
Wenn Sie all diese Konzepte zusammenfassen, kann main () nicht weiter unten nachsehen, um den Typ von Func_i () zu bestimmen. Dies liegt daran, dass die Typanalyse zu Beginn des Kompilierungsprozesses stattfindet. Zu diesem Zeitpunkt wurde nur der Teil der Quelldatei bis zur Definition von main () gelesen und analysiert, und die Definition von Func_i () ist dem Compiler noch nicht bekannt.
Der Grund, warum main () "sehen" kann, wo Func_i () aufzurufen ist, ist, dass der Aufruf zur Laufzeit erfolgt, nachdem die Kompilierung bereits alle Namen und Typen aller Bezeichner aufgelöst hat, hat Assembly bereits alle konvertiert Die Verknüpfung von Funktionen mit dem Maschinencode hat bereits die korrekte Adresse jeder Funktion an jeder Stelle eingefügt, an der sie aufgerufen wird.
Ich habe natürlich die meisten blutigen Details ausgelassen. Der eigentliche Prozess ist sehr viel komplizierter. Ich hoffe, ich habe genug Überblick gegeben, um Ihre Fragen zu beantworten.
Bitte denken Sie außerdem daran, dass das, was ich oben geschrieben habe, speziell für C gilt.
In anderen Sprachen führt der Compiler möglicherweise mehrere Durchläufe durch den Quellcode durch, sodass der Compiler die Definition von Func_i () abrufen kann, ohne dass diese deklariert wird.
In anderen Sprachen können Funktionen und / oder Variablen dynamisch typisiert werden, sodass eine einzelne Variable zu unterschiedlichen Zeiten enthalten oder eine einzelne Funktion übergeben oder zurückgegeben werden kann, eine Ganzzahl, ein Gleitkomma, eine Zeichenfolge, ein Array oder ein Objekt.
In anderen Sprachen ist die Typisierung möglicherweise stärker, sodass die Konvertierung von Gleitkomma in Ganzzahl explizit angegeben werden muss. In noch anderen Sprachen kann die Eingabe schwächer sein, sodass die Konvertierung von der Zeichenfolge "3.0" in die Gleitkommazahl 3.0 in die Ganzzahl 3 automatisch ausgeführt wird.
In anderen Sprachen kann Code zeilenweise interpretiert oder zu Byte-Code kompiliert und dann interpretiert oder just-in-time kompiliert oder einer Vielzahl anderer Ausführungsschemata unterzogen werden.
quelle
Func_()+1
: hier bei der Kompilierung der Compiler haben die Art der wissen ,Func_i()
um den entsprechenden Maschinencode zu erzeugen. Vielleicht ist es entweder nicht möglich, dass die AssemblyFunc_()+1
den Typ zur Laufzeit aufruft, oder es ist möglich, aber dies verlangsamt das Programm zur Laufzeit. Ich denke, es ist genug für mich.int func(...)
... sind, dh, dass sie eine Liste variabler Argumente enthalten. Das bedeutet, wenn Sie eine Funktion als definieren,int putc(char)
aber vergessen, sie zu deklarieren, wird sie stattdessen als aufgerufenint putc(int)
(da char, das über eine Liste variabler Argumente übergeben wird, in befördert wirdint
). Während also das Beispiel des OP funktioniert hat, weil seine Signatur mit der impliziten Deklaration übereinstimmt, ist verständlich, warum von diesem Verhalten abgeraten wurde (und entsprechende Warnungen hinzugefügt wurden).Eine Konstruktionsbeschränkung der C-Sprache bestand darin, dass sie von einem Single-Pass-Compiler kompiliert werden sollte, wodurch sie für Systeme mit sehr eingeschränktem Speicher geeignet ist. Daher kennt der Compiler zu jedem Zeitpunkt nur die zuvor genannten Dinge. Der Compiler kann in der Quelle nicht vorwärts springen, um eine Funktionsdeklaration zu finden, und dann zurückgehen, um einen Aufruf dieser Funktion zu kompilieren. Daher sollten alle Symbole deklariert werden, bevor sie verwendet werden. Sie können eine Funktion wie vorab deklarieren
am Anfang oder in einer Header-Datei, um den Compiler zu helfen.
In Ihren Beispielen verwenden Sie zwei zweifelhafte Merkmale der C-Sprache, die vermieden werden sollten:
Wenn eine Funktion verwendet wird, bevor sie ordnungsgemäß deklariert wurde, wird dies als "implizite Deklaration" verwendet. Der Compiler verwendet den unmittelbaren Kontext, um die Funktionssignatur herauszufinden. Der Compiler durchsucht den Rest des Codes nicht, um herauszufinden, was die echte Deklaration ist.
Wenn etwas ohne einen Typ deklariert wird, wird der Typ als gegeben angenommen
int
. Dies ist zB bei statischen Variablen oder Funktionsrückgabetypen der Fall.Also
printf("func:%d",Func_i())
, wir eine implizite Erklärung habenint Func_i()
. Wenn der Compiler die Funktionsdefinition erreichtFunc_i() { ... }
, ist dies mit dem Typ kompatibel. Aber wenn Siefloat Func_i() { ... }
an dieser Stelle geschrieben haben, haben Sie die Implizität deklariertint Func_i()
und die explizit deklariertfloat Func_i()
. Da die beiden Deklarationen nicht übereinstimmen, gibt der Compiler einen Fehler aus.Einige Missverständnisse beseitigen
Der Compiler findet den von zurückgegebenen Wert nicht
Func_i
. Das Fehlen eines expliziten Typs bedeutet, dass der Rückgabetypint
standardmäßig ist. Auch wenn Sie dies tun:dann ist der Typ
int Func_i()
und der Rückgabewert wird stillschweigend abgeschnitten!Der Compiler lernt schließlich den realen Typ von kennen
Func_i
, kennt jedoch den realen Typ während der impliziten Deklaration nicht. Erst wenn es später die echte Deklaration erreicht, kann es herausfinden, ob der implizit deklarierte Typ korrekt war. Zu diesem Zeitpunkt ist die Assembly für den Funktionsaufruf möglicherweise bereits geschrieben und kann im C-Kompilierungsmodell nicht geändert werden.quelle
Unit
macht einen schönen Standardtyp aus typentheoretischer Sicht, scheitert aber an der praktischen Anwendbarkeit der nahe an den Metallsystemen liegenden Programmierung, für die B und dann C entworfen wurden.Func_i()
, generiert und speichert er sofort den Code, damit der Prozessor zu einer anderen Position springen, eine Ganzzahl empfangen und dann fortfahren kann. Wenn der Compiler dieFunc_i
Definition später findet , stellt er sicher, dass die Signaturen übereinstimmen. Wenn dies der Fall ist, platziert er die Assembly fürFunc_i()
diese Adresse und weist sie an, eine Ganzzahl zurückzugeben. Wenn Sie das Programm ausführen, folgt der Prozessor diesen Anweisungen mit dem Wert3
.Erstens sind Ihre Programme für den C90-Standard gültig, jedoch nicht für die folgenden. implizites int (das Deklarieren einer Funktion ohne Angabe des Rückgabetyps) und implizites Deklarieren von Funktionen (das Verwenden einer Funktion ohne Angabe) sind nicht mehr gültig.
Zweitens funktioniert das nicht so, wie Sie denken.
Der Ergebnistyp ist in C90 optional und gibt kein
int
Ergebnis an. Das gilt auch für die Variablendeklaration (aber Sie müssen eine Speicherklasse angeben,static
oderextern
).Wenn der Compiler das
Func_i
ohne vorherige Deklaration aufruft, geht er davon aus, dass es eine Deklaration gibtIm Code wird nicht weiter untersucht, wie effektiv
Func_i
die Deklaration ist. Wenn diesFunc_i
nicht deklariert oder definiert wurde, ändert der Compiler sein Verhalten beim Kompilieren nichtmain
. Die implizite Deklaration gilt nur für Funktion, für Variable gibt es keine.Beachten Sie, dass die leere Parameterliste in der Deklaration nicht bedeutet, dass die Funktion keine Parameter akzeptiert (Sie müssen dies angeben
(void)
). Dies bedeutet, dass der Compiler die Typen der Parameter nicht überprüfen muss und dies auch tun wird implizite Konvertierungen, die auf Argumente angewendet werden, die an verschiedene Funktionen übergeben werden.quelle
extern int Func_i()
. Es sieht nirgendwo aus.-S
(sofern Sie es verwendengcc
), können Sie den vom Compiler generierten Assembly-Code anzeigen. Dann können Sie eine Vorstellung davon bekommen, wie Rückgabewerte zur Laufzeit behandelt werden (normalerweise unter Verwendung eines Prozessorregisters oder etwas Platz auf dem Programmstapel).Sie haben in einem Kommentar geschrieben:
Das ist ein Irrtum: Die Ausführung erfolgt nicht zeilenweise. Die Kompilierung erfolgt zeilenweise und die Namensauflösung erfolgt während der Kompilierung. Dabei werden nur Namen aufgelöst, keine Werte zurückgegeben.
Ein hilfreiches konzeptionelles Modell ist: Wenn der Compiler die Zeile liest:
es gibt Code aus, der äquivalent ist zu:
Der Compiler macht auch eine Notiz in einer internen Tabelle, bei der
function #2
es sich um eine noch nicht deklarierte Funktion mit dem NamenFunc_i
handelt, die eine nicht angegebene Anzahl von Argumenten verwendet und ein int (die Standardeinstellung) zurückgibt.Später, wenn es das analysiert:
Der Compiler schlägt
Func_i
in der oben genannten Tabelle nach und prüft, ob die Parameter und der Rückgabetyp übereinstimmen. Wenn dies nicht der Fall ist, wird es mit einer Fehlermeldung beendet. In diesem Fall wird die aktuelle Adresse zur internen Funktionstabelle hinzugefügt und in die nächste Zeile gewechselt.Der Compiler hat also nicht "gesucht",
Func_i
als er die erste Referenz analysiert hat. Es machte einfach eine Notiz in einer Tabelle, die die nächste Zeile weiter analysierte. Am Ende der Datei befinden sich eine Objektdatei und eine Liste der Sprungadressen.Später nimmt der Linker all dies und ersetzt alle Zeiger auf "Funktion # 2" durch die tatsächliche Sprungadresse, so dass er ungefähr Folgendes ausgibt:
Viel später, wenn die ausführbare Datei ausgeführt wird, ist die Sprungadresse bereits aufgelöst, und der Computer kann einfach zur Adresse 0x1215 springen. Keine Namenssuche erforderlich.
Haftungsausschluss : Wie gesagt, das ist ein konzeptionelles Modell und die reale Welt ist komplizierter. Compiler und Linker führen heute alle möglichen verrückten Optimierungen durch. Sie könnten sogar "rauf und runter springen"
Func_i
, obwohl ich es bezweifle. Aber die C-Sprachen sind so definiert, dass man so einen supereinfachen Compiler schreiben kann. Es ist also die meiste Zeit ein sehr nützliches Modell.quelle
1. call "function #2", put the return-type onto the stack and put the return value on the stack?
printf(..., Func_i()+1);
- der Compiler muss den Typ kennenFunc_i
, damit er entscheiden kann, ob er eineadd integer
oder eineadd float
Anweisung ausgeben soll . In einigen Sonderfällen kann es vorkommen, dass der Compiler ohne die Typinformationen weiterarbeitet, der Compiler jedoch in allen Fällen arbeiten muss.float
Werte können in einem FPU-Register gespeichert werden - dann gäbe es überhaupt keine Anweisung. Der Compiler verfolgt nur, welcher Wert in welchem Register während des Kompilierens gespeichert wird, und gibt Dinge wie "Addiere Konstante 1 zu FP-Register X" aus. Oder es könnte auf dem Stapel leben, wenn es keine freien Register gibt. Dann gäbe es den Befehl "Stapelzeiger um 4 erhöhen", und der Wert würde als etwas wie "Stapelzeiger - 4" "referenziert". All diese Dinge funktionieren jedoch nur, wenn die Größen aller Variablen (davor und danach) auf dem Stapel zur Kompilierungszeit bekannt sind.Func_i()
oder / und erstellen kannData_i
, muss er deren Typ bestimmen. In der Assemblersprache ist ein Aufruf des Datentyps nicht möglich. Ich muss die Dinge selbst im Detail studieren, um sicher zu sein.C und eine Reihe anderer Sprachen, die Deklarationen erfordern, wurden in einer Ära entwickelt, in der Prozessorzeit und Speicher teuer waren. Die Entwicklung von C und Unix Hand in Hand ging seit geraumer Zeit, und dieser hatte nicht den virtuellen Speicher bis 3BSD 1979. Ohne den zusätzlichen Raum zur Arbeit erschien, neigten Compiler zu sein Single-Pass - Angelegenheiten , weil sie es nicht taten erfordern die Fähigkeit, eine Darstellung der gesamten Datei auf einmal im Speicher zu halten.
Single-Pass-Compiler haben wie wir die Unfähigkeit, in die Zukunft zu schauen. Dies bedeutet, dass die einzigen Dinge, die sie mit Sicherheit wissen können, das sind, was ihnen explizit gesagt wurde, bevor die Codezeile kompiliert wird. Es ist für jeden von uns klar, dass dies
Func_i()
später in der Quelldatei deklariert wird, aber der Compiler, der jeweils nur einen kleinen Teil des Codes bearbeitet, hat keine Ahnung, dass er kommt.Im frühen C (AT & T, K & R, C89) führte die Verwendung einer Funktion
foo()
vor der Deklaration zu einer de facto oder impliziten Deklaration vonint foo()
. Ihr Beispiel funktioniert, wennFunc_i()
deklariert wurde,int
weil es mit dem übereinstimmt, was der Compiler in Ihrem Namen deklariert hat. Das Ändern in einen anderen Typ führt zu einem Konflikt, da er nicht mehr mit dem übereinstimmt, was der Compiler ohne explizite Deklaration ausgewählt hat. Dieses Verhalten wurde in C99 behoben, wo die Verwendung einer nicht deklarierten Funktion zu einem Fehler wurde.Was ist mit Rückgabetypen?
Die Aufrufkonvention für Objektcode erfordert in den meisten Umgebungen, dass nur die Adresse der aufgerufenen Funktion bekannt ist, was für Compiler und Linker relativ einfach zu handhaben ist. Die Ausführung springt zum Start der Funktion und kehrt zurück, wenn sie zurückkehrt. Alles andere, insbesondere die Anordnung der Übergabe von Argumenten und ein Rückgabewert, wird vollständig vom Aufrufer und Angerufenen in einer Anordnung bestimmt, die als Aufrufkonvention bezeichnet wird . Solange beide dieselben Konventionen verwenden, kann ein Programm Funktionen in anderen Objektdateien aufrufen, unabhängig davon, ob sie in einer Sprache kompiliert wurden, die diese Konventionen verwendet. (Beim wissenschaftlichen Rechnen stößt man auf viele C-Aufrufe von FORTRAN und umgekehrt, und die Fähigkeit, dies zu tun, beruht auf einer Aufrufkonvention.)
Ein weiteres Merkmal von Anfang C war, dass Prototypen, wie wir sie jetzt kennen, nicht existierten. Sie können den Rückgabetyp einer Funktion deklarieren (z. B.
int foo()
), aber nicht ihre Argumente ( z. B.int foo(int bar)
war keine Option). Dies lag daran, dass das Programm, wie oben ausgeführt, immer an einer Aufrufkonvention festhielt, die durch die Argumente bestimmt werden konnte. Wenn Sie eine Funktion mit dem falschen Argumenttyp aufgerufen haben, war dies eine Garbage-In-, Garbage-Out-Situation.Da der Objektcode den Begriff "return" hat, jedoch keinen Rückgabetyp, muss der Compiler den Rückgabetyp kennen, um den zurückgegebenen Wert verarbeiten zu können. Wenn Sie Maschinenbefehle ausführen, handelt es sich nur um Bits, und es ist dem Prozessor egal, ob der Speicher, in dem Sie einen Vergleich durchführen möchten,
double
tatsächlich einen enthältint
. Es macht einfach, was Sie verlangen, und wenn Sie es brechen, besitzen Sie beide Teile.Betrachten Sie diese Codebits:
Der Code auf der linken Seite kompiliert sich zu einem Aufruf und
foo()
kopiert das über die Call / Return-Konvention bereitgestellte Ergebnis an denx
Speicherort. Das ist der einfache Fall.Der Code auf der rechten Seite zeigt eine Typkonvertierung und ist der Grund, warum Compiler den Rückgabetyp einer Funktion kennen müssen. Gleitkommazahlen können nicht in den Speicher geschrieben werden, wo anderer Code eine erwartet,
int
da keine magische Konvertierung stattfindet. Wenn das Endergebnis eine Ganzzahl sein muss, müssen Anweisungen vorhanden sein, die den Prozessor anleiten, die Konvertierung vor dem Speichern durchzuführen. Ohne den Rückgabetyp vonfoo()
im Voraus zu kennen, hätte der Compiler keine Ahnung, dass Konvertierungscode erforderlich ist.Multi-Pass-Compiler ermöglichen alle Arten von Dingen, darunter die Möglichkeit, Variablen, Funktionen und Methoden nach ihrer ersten Verwendung zu deklarieren. Dies bedeutet, dass der Compiler, wenn er mit dem Kompilieren des Codes beschäftigt ist, bereits die Zukunft gesehen hat und weiß, was zu tun ist. Java zum Beispiel schreibt Mehrfachdurchläufe vor, da seine Syntax eine Deklaration nach der Verwendung ermöglicht.
quelle
Func_i()
der Rückgabewert von gefunden wurde?double foo(); int x; x = foo();
gibt einfach den fehler aus. Ich weiß, dass wir das nicht können. Meine Frage ist, dass der Prozessor im Funktionsaufruf nur den Rückgabewert findet; warum kann es auch den return-Typ nicht finden?foo()
, damit der Compiler weiß, was er damit machen soll.