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
inti=0;
2
printf("Ätsch!",i);
Unabhängig davon, ob die Antwort "ja" oder "nein" lautet: woher kommt
die Antwort?
Viele Grüße
W.T.
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.
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.
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.
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!
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.
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.
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.
Stimmt. :-)
Habe mich irgendwie von "argument" irritieren lassen. Aber das können ja
Ausdrücke sein, und die werden dann auch ("as always") ausgewertet.
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").
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.
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.
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.
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).
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 ;-)
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.