Warum muss ich jedes Mal in C den Datentyp für printf() und scanf() angeben?
Lesezeit: 8 Minuten
Akki619
Wie Sie dem Code-Snippet unten entnehmen können, habe ich eines deklariert char variabel und eins int Variable. Wenn der Code kompiliert wird, muss er die Datentypen von Variablen identifizieren str und i.
Warum muss ich beim Scannen meiner Variablen erneut mitteilen, dass es sich um eine Zeichenfolgen- oder Integer-Variable handelt, indem ich spezifiziere %s oder %d zu scanf? Ist der Compiler nicht ausgereift genug, um das zu erkennen, wenn ich meine Variablen deklariert habe?
#include <stdio.h>
int main ()
{
char str [80];
int i;
printf ("Enter your family name: ");
scanf ("%s",str);
printf ("Enter your age: ");
scanf ("%d",&i);
return 0;
}
%x, %d, %s sind alles Formatbezeichner, die “sagen” printf() wie man die Daten anzeigt; dh ob der Bitstrom als Hexadezimalzahl oder als Dezimalzahl oder als ASCII-Darstellung dargestellt werden soll. Daten sind Daten sind Daten. 🙂 Der Programmierer (der printf verwendet) kann es beliebig interpretieren.
– TheCodeArtist
13. August 2013 um 8:06 Uhr
Siehe Yu Haos Antwort … das Problem ist, dass printf und scanf Varargs-Funktionen sind, was bedeutet, dass ihre Parameter keine statischen Typen haben. Und C ist schwach typisiert … es gibt keine Laufzeittypinformationen, die die Funktionen untersuchen können; Das Format dient diesem Zweck.
– Jim Balter
13. August 2013 um 8:12 Uhr
Was ist mit meiner Antwort zu Formatbezeichnern? und denkst du stdio.h ist nicht notwendig zu verwenden scanf() wie Herr @mvp vorschlägt?
– Nico
13. August 2013 um 9:27 Uhr
Obwohl die Einschränkungen der C-Sprache und die Tatsache, dass Sie den Format-String zur Laufzeit erstellen können, bedeuten, dass für printf, scanfund Freunde müssen Sie den Typ immer erneut angeben, wenn der Formatstring fest codiert ist, werden einige Compiler zumindest für Sie überprüfen, ob die Typen übereinstimmen: gcc.gnu.org/onlinedocs/gcc/…
– rakslice
13. August 2013 um 18:33 Uhr
Der Punkt ist jedoch gültig, es gibt keinen wirklichen Grund, warum ein Compiler “%?” (wobei %? eine Compiler-Erweiterung ist) und verwenden Sie typeof(next_arg_in_list), um zu erraten, was ‘?’ sollte durch ersetzt werden, mit einem Bonus für die Verwendung von Kontexten wie “0x”, um Hex usw. anzugeben …
– Technosaurus
14. August 2013 um 7:53 Uhr
Yu Hao
Weil es keinen tragbaren Weg für Variablenargumentfunktionen wie gibt scanf und printf um die Typen der Variablenargumente zu kennen, nicht einmal, wie viele Argumente übergeben werden.
Aus diesem Grund muss mindestens ein festes Argument vorhanden sein, um die Anzahl und möglicherweise die Typen der variablen Argumente zu bestimmen. Und dieses Argument (der Standard nennt es parmNsiehe C11(ISO/IEC 9899:201x) §7.16 Variable Argumente ) spielt diese besondere Rolle und wird an das Makro übergeben va_start. Mit anderen Worten, Sie können in Standard-C keine Funktion mit einem solchen Prototyp haben:
void foo(...);
+1 für die einzige Antwort hier, die das Problem erkennt … fehlende statische Typen für Varargs.
– Jim Balter
13. August 2013 um 8:09 Uhr
Außerdem gibt es, wie in den Kommentaren oben erwähnt, keine Eins-zu-Eins-Entsprechung zwischen dem var-Typ und dem printf formatieren Sie “Typ”.
– MarkHu
14. August 2013 um 1:38 Uhr
@MarkHu Stimmt, aber das ist eher ein Ergebnis als ein Grund. Wie auch immer, denken Sie darüber nach, warum der Formatbezeichner von printf Verwendet %f auszugeben doublewie wäre es mit float?
– Yu Hao
14. August 2013 um 8:43 Uhr
Der zweite Absatz, der nach meinem obigen Kommentar hinzugefügt wurde, ist ziemlich unglücklich und völlig falsch. Das erste Argument wird nur benötigt, weil es keine andere Möglichkeit gibt, die Startadresse der Argumente in einer stapelbasierten Implementierung anzugeben, ohne ein zusätzliches Sprachfeature hinzuzufügen. Es gibt viele andere Möglichkeiten, die Argumenttypen zu bestimmen, einschließlich Globals, und es gibt keinen besonderen Grund, warum Typinformationen in der enthalten sein müssen Erste Argument … Sie könnten beispielsweise 5 Argumente bekannten Typs haben, bevor Sie Argumente unbekannten Typs beginnen, und Sie müssen immer noch verwenden va_arg für alle.
– Jim Balter
22. Juli 2014 um 7:21 Uhr
@JimBalter habe ich nicht gesagt Erste Argument irgendwo, habe ich? parmNper definitionem, ist der Parameter ganz rechts davor ...es muss nicht sein Erste Streit. Und es wird in Standard C benötigt.
– Yu Hao
22. Juli 2014 um 7:41 Uhr
Devolus
Der Grund, warum der Compiler die notwendigen Informationen nicht bereitstellen kann, liegt einfach darin, dass der Compiler hier nicht beteiligt ist. Der Prototyp der Funktionen gibt die Typen nicht an, da diese Funktionen Variablentypen haben. Die eigentlichen Datentypen werden also nicht zur Kompilierzeit, sondern zur Laufzeit bestimmt. Die Funktion nimmt dann ein Argument nach dem anderen vom Stack. Diesen Werten sind keine Typinformationen zugeordnet, sodass die Funktion die Daten nur interpretieren kann, indem sie die vom Aufrufer bereitgestellten Informationen verwendet, bei denen es sich um die Formatzeichenfolge handelt.
Die Funktionen selbst wissen nicht, welche Datentypen übergeben werden, noch kennen sie die Anzahl der übergebenen Argumente, also gibt es keine Möglichkeit printf kann dies selbst entscheiden.
In C++ können Sie das Überladen von Operatoren verwenden, aber das ist ein ganz anderer Mechanismus. Denn hier wählt der Compiler anhand der Datentypen und der verfügbaren überladenen Funktion die passende Funktion aus.
Um dies zu veranschaulichen, printfsieht kompiliert so aus:
Es werden also keine Typinformationen übertragen, außer denen, die in der Formatzeichenfolge bereitgestellt werden.
Schöne Erklärung des Mechanismus im Allgemeinen und dann mehr Fachsprache.
– Benutzer2363448
13. August 2013 um 9:16 Uhr
Und eine gute Verwendung eines Strings als Format besteht darin, dass das Format tatsächlich aus verschiedenen Quellen stammen kann: Es ist nicht im Quellcode fest codiert. Sie könnten den Benutzer sogar bitten, seinen eigenen Formatstring anzugeben oder ihn mit gettext zu verwenden, um die Musterreihenfolge zu ändern.
– Max-P
13. August 2013 um 11:16 Uhr
@Max-P, ja, das ist definitiv ein Vorteil, aber auch ein gefährlicher Pitfill. 🙂 Und natürlich müssten Sie sowieso einen Wrapper schreiben, denn wenn Sie den Formatstring (sozusagen) auf der Befehlszeile bereitstellen können, müssen Sie sicherstellen, dass auch die entsprechenden Argumente bereitgestellt werden.
– Devolus
13. August 2013 um 11:20 Uhr
Dass push … pushcall Sequenz ist eine Möglichkeit, a zu implementieren printf Anruf. Die tatsächliche Aufrufkonvention wird nicht vom C-Standard angegeben (sie wird normalerweise von der ABI für die Plattform angegeben). Beispielsweise könnten einige Argumente in Registern übergeben werden, und sie können in beliebiger Reihenfolge übergeben werden.
– Keith Thompson
16. August 2013 um 17:35 Uhr
@KeithThompson, ich weiß. Es hängt von der Implementierung und den Optimierungen ab. Unabhängig davon, wie es gemacht wird, werden jedoch keine Typinformationen zugeordnet, sondern nur die rohen Binärwerte, die vom Aufrufer angegeben werden.
– Devolus
17. August 2013 um 13:17 Uhr
Tarik
printf ist kein intrinsische Funktion. Es ist per se kein Teil der C-Sprache. Der Compiler generiert lediglich Code zum Aufrufen printf, übergeben beliebige Parameter. Nun, weil C nicht bereitstellt Betrachtung Als Mechanismus, um Typinformationen zur Laufzeit herauszufinden, muss der Programmierer die erforderlichen Informationen explizit bereitstellen.
@Kevin Panko Danke für deine nette Bearbeitung! Ich bin noch ein Laie, was die Bearbeitung angeht.
– Tarik
13. August 2013 um 21:24 Uhr
+1 für die Erwähnung printf ist kein Sprachmerkmal oder in irgendeiner Weise speziell, daher ist der Compiler nicht verpflichtet, es zu optimieren
– Sebastian
4. Januar 2014 um 9:55 Uhr
@Tarik Wenn printf nicht Teil der C-Sprache ist, wo wird es dann tatsächlich implementiert? Durch das Betriebssystem?
– Tejas Chandrashekhar
28. Juni 2019 um 11:22 Uhr
@TejasChandrashekhar Sehr späte Antwort auf Ihre Frage, aber hier ist sie: Was eine Sprache ausmacht, sind die Schlüsselwörter, die Syntax und das resultierende Verhalten. printf ist nur eine Funktion, die teilweise in C implementiert ist und schließlich einen Systemaufruf ausführt, um die formatierten Bytes in eine Datei oder auf den Bildschirm zu bekommen. Den Quellcode für printf finden Sie hier: code.woboq.org/linux/linux/arch/x86/boot/printf.c.html
– Tarik
25. Oktober 2020 um 14:40 Uhr
Compiler mag schlau sein, funktioniert aber printf oder scanf sind dumm – sie wissen nicht, was der Typ des Parameters ist, den Sie bei jedem Aufruf übergeben. Aus diesem Grund müssen Sie bestehen %s oder %d jedes Mal.
Der erste Parameter ist a Zeichenfolge formatieren. Wenn Sie eine Dezimalzahl drucken, kann dies so aussehen:
"%d" (Dezimalzahl)
"%5d" (Dezimalzahl mit Leerzeichen auf Breite 5 aufgefüllt)
"%05d" (Dezimalzahl auf Breite 5 mit Nullen aufgefüllt)
"%s - %d" (eine Zeichenfolge, dann etwas Inhalt, dann eine Zahl)
Ben Mosher
Ist der Compiler nicht ausgereift genug, um das zu erkennen, als ich meine Variable deklariert habe?
Nein.
Sie verwenden eine Sprache, die vor Jahrzehnten festgelegt wurde. Erwarten Sie keine moderne Designästhetik von C, weil es keine moderne Sprache ist. Moderne Sprachen neigen dazu, ein wenig Effizienz bei der Zusammenstellung, Interpretation oder Ausführung gegen eine Verbesserung der Benutzerfreundlichkeit oder Klarheit einzutauschen. Cha stammt aus einer Zeit, als Rechenzeit für Computer teuer und nur sehr begrenzt verfügbar war, und sein Design spiegelt dies wider.
Das ist auch der Grund, warum C und C++ die Sprachen der Wahl bleiben, wenn es Ihnen wirklich darum geht, schnell, effizient oder nah am Metall zu sein.
Tagesrai
scanf als Prototyp int scanf ( const char * format, ... ); sagt, speichert die angegebenen Daten gemäß dem Parameterformat an den Stellen, auf die die zusätzlichen Argumente verweisen.
Es hat nichts mit dem Compiler zu tun, es dreht sich alles um die für definierte Syntax scanf.Parameterformat ist erforderlich, um zuzulassen scanf kennen die Größe, die für einzugebende Daten reserviert werden soll.
14158900cookie-checkWarum muss ich jedes Mal in C den Datentyp für printf() und scanf() angeben?yes
%x
,%d
,%s
sind alles Formatbezeichner, die “sagen”printf()
wie man die Daten anzeigt; dh ob der Bitstrom als Hexadezimalzahl oder als Dezimalzahl oder als ASCII-Darstellung dargestellt werden soll. Daten sind Daten sind Daten. 🙂 Der Programmierer (der printf verwendet) kann es beliebig interpretieren.– TheCodeArtist
13. August 2013 um 8:06 Uhr
Siehe Yu Haos Antwort … das Problem ist, dass printf und scanf Varargs-Funktionen sind, was bedeutet, dass ihre Parameter keine statischen Typen haben. Und C ist schwach typisiert … es gibt keine Laufzeittypinformationen, die die Funktionen untersuchen können; Das Format dient diesem Zweck.
– Jim Balter
13. August 2013 um 8:12 Uhr
Was ist mit meiner Antwort zu Formatbezeichnern? und denkst du
stdio.h
ist nicht notwendig zu verwendenscanf()
wie Herr @mvp vorschlägt?– Nico
13. August 2013 um 9:27 Uhr
Obwohl die Einschränkungen der C-Sprache und die Tatsache, dass Sie den Format-String zur Laufzeit erstellen können, bedeuten, dass für
printf
,scanf
und Freunde müssen Sie den Typ immer erneut angeben, wenn der Formatstring fest codiert ist, werden einige Compiler zumindest für Sie überprüfen, ob die Typen übereinstimmen: gcc.gnu.org/onlinedocs/gcc/…– rakslice
13. August 2013 um 18:33 Uhr
Der Punkt ist jedoch gültig, es gibt keinen wirklichen Grund, warum ein Compiler “%?” (wobei %? eine Compiler-Erweiterung ist) und verwenden Sie typeof(next_arg_in_list), um zu erraten, was ‘?’ sollte durch ersetzt werden, mit einem Bonus für die Verwendung von Kontexten wie “0x”, um Hex usw. anzugeben …
– Technosaurus
14. August 2013 um 7:53 Uhr