Sitzungsmitschrift:
> type lookma.c
int main() {
printf("%s", "no stdio.h");
}
> cl lookma.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
lookma.c
Microsoft (R) Incremental Linker Version 8.00.50727.762
Copyright (C) Microsoft Corporation. All rights reserved.
/out:lookma.exe
lookma.obj
> lookma
no stdio.h
Sie hatten dieses C++ ursprünglich markiert, aber es scheint ein C-Programm zu sein. C stellt automatisch eine implizite Deklaration für eine Funktion bereit, wenn kein Prototyp im Gültigkeitsbereich vorhanden ist (z. B. aufgrund des Weglassens von #include ). Die implizite Deklaration wäre:
int printf();
Das bedeutet, dass printf eine Funktion ist, die ein int zurückgibt und eine beliebige Anzahl von Argumenten annehmen kann. Dieser Prototyp hat zufällig für Ihren Anruf funktioniert. Sie sollten #einschließen
Abschließend sollte ich hinzufügen, dass der aktuelle C-Standard (ISO/IEC 9899:1999 oder umgangssprachlich „C99“) dies tut nicht erlaubt implizite Deklarationen, und dieses Programm wäre nicht konform. Implizite Deklarationen wurden entfernt. Ich glaube, Ihr Compiler unterstützt C99 nicht. C++ erfordert auch korrekte Prototypen und führt keine impliziten Deklarationen durch.
Im strikten Compliance-Modus (das bedeutet „theoretisch“) rufen Sie ein undefiniertes Verhalten auf (was schlecht ist), wenn Sie eine Funktion aufrufen, die eine variable Anzahl von Argumenten ohne eine Prototypdeklaration der Funktion im Gültigkeitsbereich akzeptiert. Das bedeutet, dass der Compiler mit einem Programm, das verwendet, machen darf, was er will printf()
ohne den Prototyp aus #include <stdio.h>
oder eine gleichwertige Erklärung. “Alles, was es mag” schließt das korrekte Arbeiten als eine der Optionen ein; das scheint die von Ihrem Beispiel gewählte Option zu sein.
In der Praxis funktioniert der Code mit den meisten praktischen Compilern auch ohne die formelle Deklaration der printf()
Funktion.
Wie von qrdl darauf hingewiesen wurde, wurde die Funktion gefunden, weil der C-Compiler mit der C-Bibliothek verknüpft ist.
Beachten Sie, dass Chris Youngs Kommentar zu C99 und „implicit int“ korrekt ist, aber die Regel „Funktionen mit variablen Argumenten müssen einen Prototyp im Geltungsbereich haben“ gilt sowohl für C89 als auch für C99. Die meisten Compiler arbeiten standardmäßig nicht in einem strengen C99-Kompatibilitätsmodus, da es zu viel Code gibt, der nicht so kompiliert werden würde.
Chris Young kommentierte:
Zur Verdeutlichung bezog sich mein Kommentar darauf, dass C99 implizite Deklarationen entfernt. Wenn Sie “implicit int” sagen, beziehen Sie sich meiner Meinung nach auf die C89-Funktion, Deklarationen wie foo(void) zuzulassen. bedeutet int foo(void);, etwas, das C99 auch entfernt hat.
Chris hat natürlich Recht. Aus dem C99-Standard wurden zwei Merkmale der „impliziten Deklaration“ entfernt. Das Vorwort der Norm listet sie auf als:
- implizit entfernen
int
- Implizite Funktionsdeklaration entfernen
Ich habe nicht klar genug gedacht (und daher auch nicht geschrieben). Trotzdem erfordern sowohl C89 als auch C99 einen Prototyp im Geltungsbereich für Funktionen, die eine variable Anzahl von Argumenten annehmen.
Um zu zeigen:
extern int pqr();
int main(void)
{
int i = pqr(1, 3);
return i;
}
Ohne die erste Zeile ist dies ein korrektes C89-Fragment mit einer impliziten Deklaration der Funktion pqr()
als eine Funktion, die eine ganze Zahl zurückgibt (mit nicht spezifizierten Argumenten). Wenn die erste Zeile ersetzt wird durch extern pqr();
dann ist dies ein korrektes C89-Fragment mit einer expliziten Deklaration von pqr()
als eine Funktion, die eine Ganzzahl zurückgibt (mit nicht spezifizierten Argumenten), aber der Rückgabetyp ist „implizit int
‘. Wie geschrieben, wird die Funktion explizit deklariert und hat eine explizite int
Rückgabetyp – aber es hat immer noch nicht spezifizierte Argumente. Ich glaube, das ist gültig C99 – wenn auch nicht ganz wünschenswert. Natürlich akzeptiert GCC (3.4.4) es mit den Optionen ‘-std=c99 -pedantic
“. Idealerweise sollte die Funktionsdeklaration den vollständigen Prototyp enthalten. (Und, falls pqr()
mit Auslassungspunkten definiert wurden, wäre dieser Prototyp erforderlich in der Theorie!)
printf()
befindet sich in der Standard-C-Bibliothek und der Linker verknüpft die Standardbibliothek immer mit Ihrer ausführbaren Datei, sodass alle Standardfunktionen gefunden werden und es keine Verknüpfungsprobleme gibt.
Wird der entsprechende Header nicht eingefügt, führt dies zur Verwendung einer Funktion, für die kein Prototyp erstellt wurde, was zu Problemen führen kann, da der C-Compiler davon ausgeht, dass eine Funktion ohne Prototyp zurückgegeben wird int
und nimmt eine variable Anzahl von Argumenten an. Fügen Sie also immer den Header hinzu – es ist Ihr Sicherheitszaun.
Beachten Sie, dass Sie in C89/C90 einen Rückgabewert von angeben sollten
main()
. C99 erlaubt das Weglassenreturn 0;
oder gleichwertig ab Endemain()
.– Jonathan Leffler
30. Oktober 2015 um 2:30 Uhr
In welchem Zusammenhang? Befehlszeilenfenster an Windows?
– Peter Mortensen
26. April 2021 um 12:39 Uhr