Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

printf und Wechselwirkung mit fehlendem cast

1 view
Skip to first unread message

Robert Hartmann

unread,
Oct 8, 2009, 10:34:55 AM10/8/09
to
Hallo zusammen,

Gegeben folgender fehlerhafte aber compilierbare Code -
es fehlen ein paar casts.

Ich bin gefragt worden, warum es im Code unten
augenscheinlich ausreicht, jeweils nur die erste
printf-gleitpunkt-Ausgabe mit korrektem cast
durch zufᅵhren und bei der jeweils zweiten
den cast wegzulassen und trotzdem das erwartete
Ergebnis lesen zu kᅵnnen.

Und die nᅵchste Frage ist:

Wieso man, wenn die a-Fᅵlle mit korrekten casts ausgestattet sind
und die b-Fᅵlle nicht kastet, das selbe Ergebnis fᅵr b wie bei a bekommt.

Gruᅵ Robert

P.S. Ich wᅵrde mich zur Erklᅵrung des Verhaltens ungerne auf
"Da muss du eben den Cast machen, sonst ist es einfach falsch
mit unsicherem Ergebnis" zurᅵckziehen.

#include <stdio.h>

int main(int argc, char** argv ) {

int a=123;
int b=-7;

printf("a als Zeichen: %c\n", a);
printf("a als vorzeichenlose Ganzahl: %u\n", a);
printf("a als Hexadezimalzahl: %x\n", a);
printf("a als vorzeichenbehaftete Ganzzahl: %d \n", a);
printf("Gleitpunktzahl einfache Genauigkeit 2 Nachkommastellen
%.2f\n",(float)a);
printf("Gleitpunktzahl doppelte Genauigkeit exponent. Darst. %E\n",a);

printf("\n \n");

printf("b als Zeichen: %c\n", b);
printf("b als vorzeichenlose Ganzahl: %u\n", b);
printf("b als Hexadezimalzahl: %x\n", b);
printf("b als vorzeichenbehaftete Ganzzahl: %d \n", b);
printf("Gleitpunktzahl einfache Genauigkeit 2 Nachkommastellen
%.2f\n",(float)b);
printf("Gleitpunktzahl doppelte Genauigkeit exponent. Darst. %E\n",b);


return 0;
}

Rainer Weikusat

unread,
Oct 8, 2009, 11:14:45 AM10/8/09
to
Robert Hartmann <Robert_...@gmx.net> writes:
> Gegeben folgender fehlerhafte aber compilierbare Code -
> es fehlen ein paar casts.
>
> Ich bin gefragt worden, warum es im Code unten
> augenscheinlich ausreicht, jeweils nur die erste
> printf-gleitpunkt-Ausgabe mit korrektem cast
> durch zuf�hren und bei der jeweils zweiten

> den cast wegzulassen und trotzdem das erwartete
> Ergebnis lesen zu k�nnen.

Um das herauszufinden, wirst Du analysieren muessen, was die
printf-Implementierung, die Du benutzt hast, in diesen Faellen tut.
Mit C hat das allerdings nichts zu tun.

[...]

> P.S. Ich w�rde mich zur Erkl�rung des Verhaltens ungerne auf


> "Da muss du eben den Cast machen, sonst ist es einfach falsch

> mit unsicherem Ergebnis" zur�ckziehen.

Das ist aber genau die Antwort auf Deine Frage. So, wie der Code
in Deinem Posting steht, ist sein Verhalten undefiniert, weil die
Typen der Argumente nach Anwendung der 'integer promotions' zT nicht
denen entsprechen, die Argument fuer die entsprechenden 'conversion
specifier' haben sollen. Um es mal mit einen Beispiel zu versuchen:


?aufgebaut so nicht Deutschen im Saetze werden Warum

Schliesslich 'funktioniert' das doch auch und ueberhaupt handelt
sich bloss um von n denkbaren, grundsaetzlich willkuerlichen
Konventionen. Genauso willkuerlich wie die Konvention, nach der

2 + 7 = 9

eine wahre Aussage darstellt.

Markus Wichmann

unread,
Oct 8, 2009, 11:59:49 AM10/8/09
to
Robert Hartmann (Robert_...@gmx.net) schrieb:

> Hallo zusammen,
>
> Gegeben folgender fehlerhafte aber compilierbare Code -
> es fehlen ein paar casts.
>
> Ich bin gefragt worden, warum es im Code unten
> augenscheinlich ausreicht, jeweils nur die erste
> printf-gleitpunkt-Ausgabe mit korrektem cast
> durch zuführen und bei der jeweils zweiten

> den cast wegzulassen und trotzdem das erwartete
> Ergebnis lesen zu können.
>

Siehe unten. Es _sollte_ nicht gehen.

> Und die nächste Frage ist:
>
> Wieso man, wenn die a-Fälle mit korrekten casts ausgestattet sind
> und die b-Fälle nicht kastet, das selbe Ergebnis für b wie bei a bekommt.
>
> Gruß Robert
>
> P.S. Ich würde mich zur Erklärung des Verhaltens ungerne auf


> "Da muss du eben den Cast machen, sonst ist es einfach falsch

> mit unsicherem Ergebnis" zurückziehen.


>
>
>
> #include <stdio.h>
>
> int main(int argc, char** argv ) {
>
> int a=123;
> int b=-7;
>
> printf("a als Zeichen: %c\n", a);
> printf("a als vorzeichenlose Ganzahl: %u\n", a);
> printf("a als Hexadezimalzahl: %x\n", a);
> printf("a als vorzeichenbehaftete Ganzzahl: %d \n", a);

Bis hier erwartet printf anhand der Umwandlungen immer ein int, dass
dann irgendwie gecastet wird. Dabei ist zu beachten, dass alle
Ganzzahltypen kleiner als int bei Übergabe als va-Argument zu int
umgewandelt werden.

> printf("Gleitpunktzahl einfache Genauigkeit 2 Nachkommastellen
> %.2f\n",(float)a);

Und hier wird a explizit in ein float umgewandelt, dass allerdings, da
va-Arg, implizit in double umgewandelt wird.

> printf("Gleitpunktzahl doppelte Genauigkeit exponent. Darst. %E\n",a);
>

Das hier sollte nicht funktionieren. Möglicherweise ein kaputter
Compiler. Mit dem tinycc geht es jedenfalls nicht gut (ich bekomme
"NAN"). tendracc liefert ebenfalls Unsinn. Lediglich der gcc ist nicht
davon abzuhalten, a zu double zu konvertieren. Es ist allerdings kein
Bug, das zu tun, da an dieser Stelle sowieso undefiniertes Verhalten
herrscht.

> printf("\n \n");
>
> printf("b als Zeichen: %c\n", b);
> printf("b als vorzeichenlose Ganzahl: %u\n", b);
> printf("b als Hexadezimalzahl: %x\n", b);
> printf("b als vorzeichenbehaftete Ganzzahl: %d \n", b);
> printf("Gleitpunktzahl einfache Genauigkeit 2 Nachkommastellen
> %.2f\n",(float)b);
> printf("Gleitpunktzahl doppelte Genauigkeit exponent. Darst. %E\n",b);
>

Dasselbe wie oben.
>
> return 0;
> }

Zu deiner zweiten Frage: Hier liegt sowieso undefiniertes Verhalten vor.
Der gcc versucht, daraus noch irgendwie einen Sinn zu entnehmen, aber
tinycc und tendracc machen genau das, was du schreibst: Unsinn.

Um das Verhalten genauer zu erklären, kann man sich lediglich den
Quelltext der glibc und das Compilierergebnis des gcc ansehen. Letzteres
geht mit der Option "-S".

HTH,
Markus
--
Nur weil ein Genie nix reißt, muß ja nun nicht gleich jeder Idiot
pausieren... Bully hats ja auch geschafft.
-- gUnter nanonüm in de.alt.anime

Georg Bauhaus

unread,
Oct 8, 2009, 12:47:38 PM10/8/09
to
Rainer Weikusat schrieb:

> Robert Hartmann <Robert_...@gmx.net> writes:
>> Gegeben folgender fehlerhafte aber compilierbare Code -
>> es fehlen ein paar casts.
>>
>> Ich bin gefragt worden, warum es im Code unten
>> augenscheinlich ausreicht, jeweils nur die erste
>> printf-gleitpunkt-Ausgabe mit korrektem cast
>> durch zuf�hren und bei der jeweils zweiten
>> den cast wegzulassen und trotzdem das erwartete
>> Ergebnis lesen zu k�nnen.
>
> Um das herauszufinden, wirst Du analysieren muessen, was die
> printf-Implementierung, die Du benutzt hast, in diesen Faellen tut.
> Mit C hat das allerdings nichts zu tun.

F�r die Leute, denen der Umstand erkl�rt werden soll,
vielleicht eine andere Formulierung? Denn wie sollten
sie nachvollziehen, dass die Bedeutung von C-Programmtext
nichts mit C zu tun hat?
"Cs Definition sieht keine Vorhersage eines
bestimmten Ergebnisses f�r diesen nur formal
g�ltigen Programmtext vor. Mehr noch, ...
Deshalb unbedingt ..."

Stefan Reuther

unread,
Oct 8, 2009, 2:28:20 PM10/8/09
to
Markus Wichmann wrote:
> Robert Hartmann (Robert_...@gmx.net) schrieb:

>> printf("Gleitpunktzahl doppelte Genauigkeit exponent. Darst. %E\n",a);
>
> Das hier sollte nicht funktionieren. Möglicherweise ein kaputter
> Compiler. Mit dem tinycc geht es jedenfalls nicht gut (ich bekomme
> "NAN"). tendracc liefert ebenfalls Unsinn. Lediglich der gcc ist nicht
> davon abzuhalten, a zu double zu konvertieren.

Der gcc konvertiert nicht. Die Zahl, die da ausgegeben wird, ist auch
nicht '123', wovon man sich durch Ersetzen des '%E' durch '%.20E' leicht
überzeugen kann.

Ein double hat heute 64 Bits. '123' ergibt die Bits 0x405EC00000000000.
Diese Bits landen bei 'printf(".2f", (float) a)' auf dem Stack. Der gcc
recycelt nun zufälligerweies Stackpositionen derart, dass die 123 aus
'printf("%E", a)' dort landet, wo die 32 niederwertigsten Bits des
double standen. Die höherwertigsten bleiben noch stehen. 'printf' sucht
nun einen double, bastelt die alten und die neuen Bits zusammen zu
0x405EC0000000007B, und gibt das als double aus, und das ist eben wieder
annähernd 123.

Um es etwas plastischer zu machen: letztlich weist man hier 'printf' an,
nicht (korrekt) initialisierten Speicher zu lesen. Und das ist halt zu
vermeiden. Letztlich gibt der gcc auch für
#include <stdio.h>
int main() {
{
int a = 123;
printf("%d\n", a);
}
{
int b;
printf("%d\n", b);
}
}
zwei identische Werte aus, weil 'b' halt zufällig dort angelegt wird, wo
früher 'a' stand, aber dass das undefiniertes Verhalten ist dürfte jedem
einsichtig sein.


Stefan

Thomas Koenig

unread,
Oct 9, 2009, 12:47:51 PM10/9/09
to
On 2009-10-08, Stefan Reuther <stefa...@arcor.de> wrote:
> Markus Wichmann wrote:

>> Das hier sollte nicht funktionieren. Mᅵglicherweise ein kaputter


>> Compiler. Mit dem tinycc geht es jedenfalls nicht gut (ich bekomme
>> "NAN"). tendracc liefert ebenfalls Unsinn. Lediglich der gcc ist nicht
>> davon abzuhalten, a zu double zu konvertieren.

> Der gcc konvertiert nicht.

... er warnt aber, wenn man ihn lieb drum bittet. -Wall liefert eine
entsprechende Warnung.

Rainer Weikusat

unread,
Oct 23, 2009, 2:28:53 PM10/23/09
to
Georg Bauhaus <rm.dash...@futureapps.de> writes:
> Rainer Weikusat schrieb:
>> Robert Hartmann <Robert_...@gmx.net> writes:
>>> Gegeben folgender fehlerhafte aber compilierbare Code -
>>> es fehlen ein paar casts.
>>>
>>> Ich bin gefragt worden, warum es im Code unten
>>> augenscheinlich ausreicht, jeweils nur die erste
>>> printf-gleitpunkt-Ausgabe mit korrektem cast
>>> durch zuf�hren und bei der jeweils zweiten
>>> den cast wegzulassen und trotzdem das erwartete
>>> Ergebnis lesen zu k�nnen.
>>
>> Um das herauszufinden, wirst Du analysieren muessen, was die
>> printf-Implementierung, die Du benutzt hast, in diesen Faellen tut.
>> Mit C hat das allerdings nichts zu tun.
>
> F�r die Leute, denen der Umstand erkl�rt werden soll,
> vielleicht eine andere Formulierung? Denn wie sollten
> sie nachvollziehen, dass die Bedeutung von C-Programmtext
> nichts mit C zu tun hat?

Der entscheidende Punkt waere, dass das gar kein 'C-Programmtext'
ist, denn er hat keine durch die C-Norm definierte Bedeutung. Das er
aufgrund von oberflaechlichen Aehnlichkeiten fuer einen gehalten
werden koennte, waere ein anderer Aspekt.

Um ein altes Beispiel von mir wiederzuverwerten: In natuerlichen
Sprachen gibt es sogenannte 'unikale Morpheme', das sind
bedeutungstragende Wortteile, die ausschliesslich in einer Weise mit
anderen Morphemen kombiniert werden koennen. Ein Beispiel fuer ein
unikales Morphem in Deutschen waere 'schorn', welches ausschliesslich
in der Kombination 'schorn-stein' vorkommen kann. Jemand, der des
Deutschen nicht ausreichend maechtig ist, koennte jetzt folgendes
annehmen: Es gibt das Wort 'Schornstein' und das Wort
'Backstein'. Weiterhin gibt es das Wort 'Backofen'. Ergo: 'Schornofen'
bezeichnet den Ofen, der zu einem bestimmten Schornstein gehoert.
Das stimmt zwar nicht, aber das kann man nicht aus den 'formellen'
Wortbildungsregeln ableiten, sondern nur durch Kenntnis der Semantik.

0 new messages