C: Variadische Funktion (printf)

#4490572
Lesenswert?

Hallo zusammen,

ich bin mal wieder bei einer C-Anfängerfrage gelandet: Ist es erlaubt, 
"printf" einen Parameter mitzugeben, der gar nicht interpretiert wird?
Also so:
1
int i = 0;
2
printf("Ätsch!",i);

Unabhängig davon, ob die Antwort "ja" oder "nein" lautet: woher kommt 
die Antwort?

Viele Grüße
W.T.
#4490615
Lesenswert?

Prinzipiell ist es in C möglich, an eine Funktion mehr oder weniger 
Parameter zu übergeben.
Die aufgerufene Funktion KANN die zusätzlichen Parameter anschauen, wenn 
sie will (z.B. im Fall von printf() wenn sie anhand des Formatstrings da 
noch Werte vermutet), muß es aber nicht.

Problematisch ist es nur, zu wenige Werte zu übergeben oder Werte mit 
anderem Typ als erwartet.
Moderator #4490634
Lesenswert?

Walter T. schrieb:
> ich bin mal wieder bei einer C-Anfängerfrage gelandet: Ist es erlaubt,
> "printf" einen Parameter mitzugeben, der gar nicht interpretiert wird?

Ja.

> Unabhängig davon, ob die Antwort "ja" oder "nein" lautet: woher kommt
> die Antwort?

Aus dem C11-Standard:

1
7.19.6.1 The fprintf function
2
...
3
If the format is exhausted while arguments remain, the excess arguments
4
are evaluated (as always) but are otherwise ignored.
5
...

Walter T. schrieb:
> Hm. Da finde ich die Antwort von hier:
>
> http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf
>
> Seite 286(PDF)/274(gedruckt) besser.

Ich weiß nicht, auf welche Textstelle du dich genau beziehst, aber warum
ist diese Antwort für dich nur besser, aber nicht ausreichend?
#4490637
Lesenswert?

Yalu X. schrieb:
> Ich weiß nicht, auf welche Textstelle du dich genau beziehst, aber warum
> ist diese Antwort für dich nur besser, aber nicht ausreichend?

Genau diese Textstelle. Sie ist für mich ausreichend. Ich habe sie nur 
erst gefunden, nachdem ich meine Frage formuliert habe. Leider passiert 
es mir recht oft, daß mir erst durch das Formulieren einer Frage die 
richtigen Suchbegriffe einfallen.
Persönliche Seite #4490678
Lesenswert?

Walter T. schrieb:
> Ich habe sie nur erst gefunden, nachdem ich meine Frage formuliert habe.
> Leider passiert es mir recht oft, daß mir erst durch das Formulieren
> einer Frage die richtigen Suchbegriffe einfallen.

Nimm eine Gummiente!
 
1
1) Beg, borrow, steal, buy, fabricate or otherwise obtain a rubber duck.
http://www.rubberduckdebugging.com/
 
1
Place a rubber duck on your monitor and describe your problems to it.
2
There's something magical about stating your problems aloud that makes
3
the solution more clear.
http://c2.com/cgi/wiki?RubberDucking
 
1
Rubber duck debugging is [...] a method of debugging code. The name
2
is a reference to a story in the book "The Pragmatic Programmer" in which
3
a programmer would carry around a rubber duck and debug their code by
4
forcing themselves to explain it, line-by-line, to the duck.
https://en.wikipedia.org/wiki/Rubber_duck_debugging
#4490864
Lesenswert?

Ich finde die Beschreibung im Standard etwas irritierend:
1
If the format is exhausted while arguments remain, the excess arguments
2
are _evaluated_ (as always) but are otherwise ignored.

Was wird denn da ausgewertet? Ist nicht vielleicht "übergeben" gemeint:
1
If the format is exhausted while arguments remain, the excess arguments
2
are still passed (as always) but are otherwise ignored.
Gast #4500581
Lesenswert?

Walter T. schrieb:
> Mikro 7. schrieb:
>> Was wird denn da ausgewertet?
>
> Wenn der Aufruf-Parameter ein Ausdruck ist (z. B. i++) wird der
> ausgeführt.

Dort könnten auch Funktionsaufrufe stehen, die wiederum als Parameter 
i++ oder weitere Funktionsaufrufe enthalten.

Zum anderen muss der Format-String keine Konstante sein, die zur 
Compile-Zeit ausgewertet werden könnte. Interessant wird es auch mit 
der Verschachtelung von Argumenten, wenn zur Vervollständigung eines 
Format-Strings weitere Parameter benötigt werden, z. B. so etwas wie 
"%*.*f".

Nicht jeder Compiler unterstützt hier alle Möglichkeiten. Manche 
blockieren auch erlaubte Konstruktionen (z. B. Format-String als 
Nicht-"const").
Gast #4505034
Lesenswert?

Mikro 7. schrieb:
> Ich finde die Beschreibung im Standard etwas irritierend:

Das ist auch kein Wunder.

Also: Die Erfinder von C hatten es sich damals so gedacht, daß ein 
Programm, was eine Funktion aufruft, deren Argumente der Reihe nach auf 
den Stack packt - und zwar das allerletzte zuerst, dann die Funktion 
aufruft und anschließend selbst die Argumente wieder vom Stack löscht. 
Deswegen war es für die Funktion völlig schnurz, ob weiter oben auf dem 
Stack noch irgend welche weiteren Argumente herumlungern, sie hatte sich 
ja nicht selber um das Bereinigen des Stacks zu kümmern.

Auf lange Sicht ist das ein blödes Verfahren, denn das Stackbereinigen 
durch das aufrufende Programm kostet mehr Zeit und Code als das 
Bereinigen durch die aufgerufene Funktion. Andere Systeme (z.B. Pascal) 
machen es anders und das auch aus anderweitigen Gründen, z.B. 
Stringhandling.


Bei µC ist das alles ganz anders. Bei ARM wird z.B. so verfahren, daß 
die ersten 4 Argumente zu je 32 Bit (ja, waren 4 wenn ich mich recht 
erinnere) oder ein Äquivalent davon (double oder int64) in den ersten 
Registern R0..3 übergeben werden. Wie das bei anderen µC-Architekturen 
gehandhabt wird, ist Sache der Compilerhersteller.

Deswegen finde ich Walters Frage eher akademisch und würde derartige 
Konstrukte einfach nicht verwenden. Man braucht sowas auch nicht in der 
µC-Programmierung.

W.S.
#4505319
Lesenswert?

W.S. schrieb:
> Deswegen finde ich Walters Frage eher akademisch und würde derartige
> Konstrukte einfach nicht verwenden. Man braucht sowas auch nicht in der
> µC-Programmierung.

Stimmt. Das braucht man nicht. Es ist der reine Luxus. So wie structs. 
Oder enums. Oder ein Compiler. Oder insgesamt ein frei programmierbarer 
µC. Oder erst Recht das Gerät, das damit gebaut wurde.

Man braucht vieles nicht in der µC-Programmierung. Wenn man es trotzdem 
benutzt, kann man aber oft vieles vereinfachen. In meinem Fall hat es 
dazu beigetragen, es weitaus schwieriger zu machen, eine sporadisch, 
aber an vielen Stellen aufgerufene Funktion, die ihrerseits printf 
aufruft, mit falschen Parametern, die erst zur Laufzeit erkannt würden, 
aufzurufen. Den Luxus gönne ich mir.
Persönliche Seite #4505536
Lesenswert?

W.S. schrieb:
> Auf lange Sicht ist das ein blödes Verfahren, denn das Stackbereinigen
> durch das aufrufende Programm kostet mehr Zeit und Code als das
> Bereinigen durch die aufgerufene Funktion.

Ach ja? Magst Du das ein bisschen elaborieren? Das Verändern eines 
Prozessorregisters um einen konstanten Wert dauert unterschiedlich lang, 
in Abhängigkeit davon, wo es geschieht?

Was ist das für ein Effekt?

> Andere Systeme (z.B. Pascal) machen es anders

Und kennen keine variadischen Funktionen. So etwas wie writeln kann in 
Pascal selbst nicht geschrieben werden.
#4505559
Lesenswert?

Rufus Τ. F. schrieb:
> Und kennen keine variadischen Funktionen. So etwas wie writeln kann in
> Pascal selbst nicht geschrieben werden.

Nicht ganz richtig. Mit einem Level-1 konformen Compiler (wenn es denn 
so etwas gäbe) könnte man mit einem conformant array schema (das ist 
so etwas wie ein "variables Array") aus varianten Records variadische 
Funktionen nachbilden. Modula 3 und Ada machen das so (auch wenn's nicht 
unbedingt "schön" ist).
Moderator #4505605
Lesenswert?

Rufus Τ. F. schrieb:
> W.S. schrieb:
>> Auf lange Sicht ist das ein blödes Verfahren, denn das Stackbereinigen
>> durch das aufrufende Programm kostet mehr Zeit und Code als das
>> Bereinigen durch die aufgerufene Funktion.
>
> Ach ja? Magst Du das ein bisschen elaborieren? Das Verändern eines
> Prozessorregisters um einen konstanten Wert dauert unterschiedlich lang,
> in Abhängigkeit davon, wo es geschieht?

Es ist sogar so, dass dass die Stackbereinigung im aufgerufenen
Unterprogramm (wie in Pascal) deutlich mehr als nur eine Operation
erfordert, da hier der Stackpointer nicht direkt verändert werden kann
(sonst würde die Rücksprungadresse nicht mehr gefunden werden).

Aber es kommt noch besser: Bei der Stackbereinigung im aufrufenden
Programmteil (wie in C) muss sie nicht unbedingt nach jedem
Unterprogrammaufruf stattfinden. Bei einer Sequenz von mehreren
Unterprogrammaufrufen, zwischen denen keine Verzweigungen vorkommen,
genügt es, die Stackbereinigung ein einziges Mal an deren Ende
durchzuführen.

Im Mittel kostet die Stackbereinigung in C also weniger als eine
Operation, während dafür in Pascal gleich mehrere Operationen
erforderlich sind.

@W.S.:

Bevor man sich mit seinen Behauptungen zu weit aus dem Fenster lehnt,
ist es mitunter ratsam, sie sie mit einem kleinen Testprogrämmchen kurz
nachzuprüfen ;-)
Persönliche Seite #4505638
Lesenswert?

Yalu X. schrieb:
> Aber es kommt noch besser: Bei der Stackbereinigung im aufrufenden
> Programmteil (wie in C) muss sie nicht unbedingt nach /jedem/
> Unterprogrammaufruf stattfinden. Bei einer Sequenz von mehreren
> Unterprogrammaufrufen, zwischen denen keine Verzweigungen vorkommen,
> genügt es, die Stackbereinigung ein einziges Mal an deren Ende
> durchzuführen.

Bei avr-gcc kann man sogar per -maccumulate-args zwischen zwei 
unterschiedliche Verfahren wählen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren