Forum: Compiler & IDEs C: Variadische Funktion (printf)


von Walter T. (nicolas)


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.

von Klaus W. (mfgkw)


Lesenswert?

ja
(Erlaubt ist es, aber das Verhalten ist dann nicht mehr spezifiziert.)


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

Von mir.

von Klaus W. (mfgkw)


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.

von Walter T. (nicolas)


Lesenswert?

Klaus W. schrieb:
> Von mir.



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.

von Yalu X. (yalu) (Moderator)


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?

von Walter T. (nicolas)


Lesenswert?

OK, an Deiner zweiten Antwort (die sich mit meiner überschnitten hat) 
merkt man erst, daß es Dir nicht nur darum ging, schlechte Laune 
loszuwerden.

Danke für die Antwort!

von Walter T. (nicolas)


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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ah, ok. Dann ist ja alles klar.

von Johann L. (gjlayde) Benutzerseite


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

von Walter T. (nicolas)


Lesenswert?

Naja, in einer Umgebung, wo man bei Rubberducking oder Guru Meditation 
betreiben kann, werde zumindest ich nie (wieder) arbeiten oder leben.

Da muß es eben mikrocontrollernetting tun.

von Mikro 7. (mikro77)


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.

von Walter T. (nicolas)


Lesenswert?

Mikro 7. schrieb:
> Was wird denn da ausgewertet?

Wenn der Aufruf-Parameter ein Ausdruck ist (z.B. i++) wird der 
ausgeführt.

von Mikro 7. (mikro77)


Lesenswert?

Stimmt. :-)

Habe mich irgendwie von "argument" irritieren lassen. Aber das können ja 
Ausdrücke sein, und die werden dann auch ("as always") ausgewertet.

von dfIas (Gast)


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").

von W.S. (Gast)


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.

von Walter T. (nicolas)


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.

von Rufus Τ. F. (rufus) Benutzerseite


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.

von Markus F. (mfro)


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).

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Nun, ich bezog mich in erster Linie auf Pascal. Daß es davon 
tatsächlich Weiterentwicklungen gegeben hat, ist zu hoffen.

von Yalu X. (yalu) (Moderator)


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 ;-)

von Johann L. (gjlayde) Benutzerseite


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.