Warum muss ich jedes Mal in C den Datentyp für printf() und scanf() angeben?

Lesezeit: 8 Minuten

Benutzeravatar von Akki619
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

Benutzeravatar von Yu Hao
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.

Siehe C-FAQ: Wie kann ich herausfinden, mit wie vielen Argumenten eine Funktion tatsächlich aufgerufen wurde?


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

Benutzeravatar von Devolus
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:

 push value1
 ...
 push valueN
 push format_string
 call _printf

Und der Prototyp von printf ist das:

int printf ( const char * format, ... );

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 pushpush call 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

Benutzeravatar von Tarik
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)
  • "%+d" (Dezimalzahl, immer mit Vorzeichen)
  • "Value: %d\n" (etwas Inhalt vor/nach der Zahl)

usw., siehe z Formatieren Sie Platzhalter auf Wikipedia um eine Vorstellung davon zu bekommen, was Formatzeichenfolgen enthalten können.

Auch hier kann es mehr als einen Parameter geben:

"%s - %d" (eine Zeichenfolge, dann etwas Inhalt, dann eine Zahl)

Benutzeravatar von Ben Mosher
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.

Benutzeravatar von Dayal Rai
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.

1415890cookie-checkWarum muss ich jedes Mal in C den Datentyp für printf() und scanf() angeben?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy