char geben Sie va_arg ein

Lesezeit: 6 Minuten

Ich habe die folgende Funktion, die übergebene Argumente in eine Binärdatei schreibt.

void writeFile(FILE *fp, const int numOfChars, ...)
{
   va_list ap;
   va_start(ap, numOfChars);
   for(int i = 0; i < numOfChars; i++)
   {
      const char c = va_arg(ap, char);
      putc(c, fp);
   }
   va_end(ap);
}

Beim Kompilieren erhalte ich die folgende Warnung vom Compiler

 warning: second argument to 'va_arg' is of promotable type 'char'; this va_arg
      has undefined behavior because arguments will be promoted to 'int' [-    Wvarargs]

Jetzt, wie ich es verstehe, möchte C den Typ char zu int befördern. Warum will C das tun? Zweitens, ist die beste Lösung, um das int zurück in ein Zeichen zu werfen?

Jetzt, wie ich es verstehe, möchte C den Typ char zu int befördern. Warum will C das tun?

Denn so steht es in der Norm. Wenn Sie einen ganzzahligen Wert übergeben, dessen Conversion-Rang kleiner als der von ist int (z.B char, bool oder short) in eine Funktion mit einer variablen Anzahl von Argumenten, in die sie umgewandelt wird int. Vermutlich hat dies seine Wurzeln in der Leistung, wo es besser war (und tatsächlich oft noch heute ist), Werte zu übergeben, die an einer Maschinenwortgrenze ausgerichtet sind.

Zweitens, ist die beste Lösung, um das int zurück in ein Zeichen zu werfen?

Ja, aber Sie brauchen nicht einmal eine Besetzung, eine implizite Konvertierung reicht aus:

char ch = va_arg(ap, int);

  • Es ist weder ein Cast noch eine implizite Konvertierung erforderlich. Siehe das Ende meiner (aktualisierten) Antwort.

    – Keith Thompson

    21. Januar 2015 um 15:55 Uhr

  • @KeithThompson Was meinst du damit, dass keine implizite Konvertierung erforderlich ist? Du kannst sicher nicht schreiben char ch = va_arg(ap, char) da es undefiniert ist? Wenn Sie möchten char aus der Argumentliste müssen Sie bestehen int zu va_argist das nicht richtig?

    – Das paramagnetische Croissant

    21. Januar 2015 um 18:05 Uhr

  • Was ich meine ist, dass der richtige Code wäre int c = va_arg(ap, int); putc(c, fp);. Du könnte machen c a charaber seit putc() dauert ein int Argument, es hat nicht viel Sinn, dies zu tun.

    – Keith Thompson

    21. Januar 2015 um 18:13 Uhr


  • Es ist nicht falsch (obwohl es einige Probleme haben könnte). va_arg(ap, int) gibt ein Ergebnis vom Typ zurück intdie umgewandelt wird aus int zu char durch die Initialisierung von ch. Wenn klar char signiert ist und das Ergebnis übersteigt CHAR_MAX (normalerweise 127), dann ist das Ergebnis der Konvertierung implementierungsdefiniert, obwohl es fast immer dem üblichen 2er-Komplement-Wraparound unterzogen wird. Wenn ch wird dann weitergegeben putcdiese Funktion selbst wird es konvertieren unsigned char; das Ergebnis dieser Konvertierung ist wohldefiniert.

    – Keith Thompson

    21. Januar 2015 um 18:21 Uhr

  • Aber seit va_arg() kann a nicht erbringen charund putc dauert ein int Argument, könnten Sie genauso gut verwenden int konsequent. (Falls der Anrufer von writeFile passiert ein Argument, das außerhalb des Bereichs von liegt unsigned chares wird konvertiert durch putcwas Ihnen normalerweise das erwartete Verhalten geben sollte.)

    – Keith Thompson

    21. Januar 2015 um 18:22 Uhr

Benutzeravatar von Keith Thompson
Keith Thompson

Variadische Funktionen werden speziell behandelt.

Bei einer nicht-variadischen Funktion gibt der Prototyp (Deklaration) die Typen aller Parameter an. Parameter können von jedem Typ (kein Array, keine Funktion) sein – einschließlich Typen, die schmaler als sind int.

Bei einer Variadic-Funktion kennt der Compiler die Typen der entsprechenden Parameter nicht , .... Aus historischen Gründen und um die Arbeit des Compilers zu erleichtern, sind alle entsprechenden Argumente von Typen schmaler als int dazu befördert werden int oder zu unsigned intund alle Argumente vom Typ float dazu befördert werden double. (Deshalb printf verwendet für beide die gleichen Formatbezeichner float oder double Argumente.)

Also eine variadische Funktion kann nicht empfangen Argumente des Typs char. Sie können eine solche Funktion mit a aufrufen char Argument, aber es wird gefördert werden int.

(In frühen Versionen von C, bevor Prototypen eingeführt wurden, alle Funktionen haben sich so verhalten. Sogar C11 erlaubt Nicht-Prototyp-Deklarationen, in denen enge Argumente gefördert werden int, unsigned intoder double. Aber angesichts der Existenz von Prototypen gibt es wirklich keinen Grund, Code zu schreiben, der von solchen Beförderungen abhängt – mit Ausnahme des Sonderfalls variadischer Funktionen.)

Aus diesem Grund hat es keinen Sinn, es zu haben va_arg() annehmen char als Typargument.

Aber die Sprache nicht verbieten eine solche Berufung auf va_arg(); in der Tat den Abschnitt der Norm beschreibt <stdarg.h> erwähnt keine Argumentförderung. Die Regel ist im Abschnitt über Funktionsaufrufe angegeben, N1570 6.5.2.2 Absatz 7:

Wenn der Ausdruck, der die aufgerufene Funktion bezeichnet, einen Typ hat, der einen Prototyp enthält, werden die Argumente implizit wie durch Zuweisung in die Typen der entsprechenden Parameter konvertiert, wobei der Typ jedes Parameters die nicht qualifizierte Version seiner Deklaration ist Typ. Die Ellipsennotation in einem Funktionsprototypdeklarator bewirkt, dass die Argumenttypkonvertierung nach dem letzten deklarierten Parameter angehalten wird. Die Heraufstufung von Standardargumenten wird für nachgestellte Argumente durchgeführt.

Und die Beschreibung der va_arg() Makro, 7.16.1.1, sagt (Hervorhebung hinzugefügt):

Wenn es kein tatsächliches nächstes Argument gibt oder wenn der Typ nicht mit dem Typ des tatsächlichen nächsten Arguments kompatibel ist (wie nach dem Standardargument promotions befördert), ist das Verhalten undefiniert, mit Ausnahme der folgenden Fälle:
[SNIP]

Die „Default Argument Promotions“ wandeln schmale Argumente um int, unsigned intoder double. (Ein Argument eines vorzeichenlosen ganzzahligen Typs, dessen Maximalwert überschreitet INT_MAX werden befördert unsigned int. Es ist theoretisch möglich für char sich so zu verhalten, aber nur in einer sehr ungewöhnlichen Implementierung.)

Zweitens, ist die beste Lösung, um das int zurück in ein Zeichen zu werfen?

Nein, in diesem Fall nicht. Gipsverbände sind selten erforderlich; In den meisten Fällen können implizite Konvertierungen die gleiche Aufgabe erfüllen. In diesem speziellen Fall:

const char c = va_arg(ap, char);
putc(c, fp);

das erste Argument zu putc ist schon typ intalso besser geschrieben als:

const int c = va_arg(ap, int);
putc(c, fp);

Das int Wert wird umgewandelt durch putc zu unsigned char und angeschrieben fp.

  • Danke Keith. Ich lerne jeden Tag neue Dinge über C. 🙂

    – Ein Mann

    20. Januar 2015 um 20:25 Uhr

  • „Aber die Sprache nicht verbieten […]” — Ich bin hier verloren. Es ist explizit undefiniert: 7.16.1.1 (va_arg) p2 “[I]f Typ nicht mit dem Typ des tatsächlichen nächsten Arguments kompatibel ist (wie es gemäß den Standard-Argument-Promotions befördert wird), ist das Verhalten undefiniert, mit Ausnahme der folgenden Fälle: […]”

    – Mafso

    21. Januar 2015 um 11:04 Uhr

  • @mafso: Undefiniertes Verhalten bedeutet nicht, dass es verboten ist. va_arg(ap, char) ist weder ein Syntaxfehler noch eine Einschränkungsverletzung; ein konformer Compiler muss sie nicht ablehnen oder diagnostizieren.

    – Keith Thompson

    21. Januar 2015 um 15:49 Uhr

  • Ich stimme zu (ich bin mir nicht einmal sicher, ob ein Compiler ein Programm mit einem (unerreichten) ablehnen darf) va_arg(ap, char), GCC warnt, dass das Programm abbricht, wenn der Code erreicht wird), war ich verwirrt über “erwähnt keine Argumentförderung”. Wie auch immer, ich halte diese Antwort mit oder ohne diesen Teil für nützlich, auch wenn ich sie leicht irreführend finde. (Ich würde “Verbieten” als “Verbieten für streng konformen Code” lesen, aber vielleicht bin das nur ich.)

    – Mafso

    21. Januar 2015 um 16:05 Uhr

  • @mafso: Sie waren wahrscheinlich verwirrt über “erwähnt keine Argumentförderung”, weil das falsch war. Ich habe es gerade aktualisiert.

    – Keith Thompson

    13. Juni 2019 um 21:36 Uhr

1433150cookie-checkchar geben Sie va_arg ein

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

Privacy policy