printf Formatierungen und Pointer
-
Hallo, ich habe die Aufgabe zu den Ausgaben, die folgender Code ausgibt einige Erklärungen abzugeben und bin etwas verwundert über das, was rauskommt.
Hier erstmal der Code:#include <stdio.h> main () { 1 int bar = 0x41 ; 2 int *foo = &bar ; 3 printf ("%p\n" , foo ) ; 4 foo++; 5 printf ("%x\n" , *foo ) ; 6 printf ("%p\n" , foo ) ; 7 printf ("%p\n" , &foo ) ; 8 printf ("%d\n" , bar ) ; }
Aufgabe ist:
Welche Ausgabe liefert das Programm? Welches Format haben die angezeigten Werte?
Wie sind die konkreten Werte zu erklären? Was beobachten Sie bei der mehrmaligen Ausführung
des Programms?Meine Gedanken:
zunächst wird also mal eine int Variable mit dem Wert 0x41 agngelegt, was auch erklärt, warum der letzte printf Befehl die Ausgabe 65 erzeugt.
Es wird eine int pointervariable erzeugt, die durch die Initialisierung direkt auf die Adresse von bar zeigt. Die Adresse, auf die foo verweist wird dann ausgegeben. (Ich hatte da mal noch zu Testzwecken ein
printf ("%p\n" , &bar ) ; vor das foo++; eingefügt und zwei gleiche Ausgaben in hexadezimaler Schreibweise erhalten, was mir sinnvoll vorkam.Was mir komisch vorkommt sind die drei Ausgaben, die nach dem foo++; erzeugt werden (also in 5,6,7). In 5 wird fast dieselbe Ausgabe erzeugt, wie in 3, aber es ist ein Teil vorne abgeschnitten und auf der letzten Stelle wurden 4 addiert(so sieht es aus).
In 6 und 7 wird das gleiche ausgegeben, aber wieder als vollständige hexzahl so wie in 3 (aber auch mit den +4 auf der letzten Stelle).Hier mal die Ausgaben einiger hintereinander gestarteter Programmausführungen:
0x7ffff37df714
f37df718
0x7ffff37df718
0x7ffff37df718
65
--------------
0x7fffae835bc4
ae835bc8
0x7fffae835bc8
0x7fffae835bc8
65
--------------
0x7fff219a5e94
219a5e98
0x7fff219a5e98
0x7fff219a5e98
65Mich irritiert es, dass in 5,6,7 quasi gleiche Adressen ausgegeben werden.
Ich habe leichte Vorkenntnisse in anderen Sprachen (Java, Matlab) aber noch nie intensiver mit Pointern zu tun gehabt.Wäre dankbar für Aufklärung.
Viele Grüße Wolf
-
Hier wird sich massiv auf undefiniertes Verhalten verlassen. Kein tolles Lehrbeispiel
.
Was passiert: Mit foo++ wird ja der Zeiger erhöht, das heißt, er würde dann auf den Integer hinter bar zeigen. Das erklärt die Ausgabe in Zeile 6, sofern man annimmt, dass ein Integer eben 4 Byte groß ist (was oft, aber nicht immer(!), der Fall ist). Bis hierhin war alles noch regulär. In Zeile 5 wird aber ausgegeben, wo drauf foo nach dem Erhöhen zeigt. Hinter bar ist aber kein weiterer Integer, weil bar kein Teil eines Arrays ist. Was nun aber passiert ist, dass bei manchen(!) C-Implementierungen folgendes der Fall ist, auf das sich hier verlassen wird:
1. Pointer sind so groß wie Integer. Eine sehr gewagt Annahme, das ist auf vielen Systemen nicht der Fall.
2. Die Variablen liegen so hintereinander im Speicher, wie sie deklariert wurden. Eine noch viel gewagtere Annahme als Annahme 1.
Bei dir ist dies aber anscheinend beides der Fall. Das heißt, foo zeigt auf sich selber. Das erklärt dann die Ausgaben 5 und 7.
-
Ahhhhh!
Das macht die ganze Sache wesentlich verständlicher!
Vielen Dank!
-
Hmm..jetzt muss ich doch nochmal nachhaken, weil ich eben zu wenig über die Details nachgedacht habe.
Du sagst "Das erklärt die Ausgabe in Zeile 6, sofern man annimmt, dass ein Integer eben 4 Byte groß ist "; bedeutet das, dass hier sozusagen pro Adresse 1 byte Speicherplatz zur Verfügung stünde?
Weiterhin ergibt sich eine Frage bzgl. der Ausgabeformatierungen mit %p und %x.
Ich habe jetzt auf einem anderen Board gelesen, dass %x zur Ausgabe einer Hexadezimalzahl gebraucht wird und %p zur Ausgabe eines Pointers. Wie erklärt sich dann der Punkt, an dem die "echte" Adresse in 5 abgeschnitten wird? Meine Erklärung: Wenn wir davon ausgehen, dass ein Int 4 Byte bekommt, dann wäre eine 32-stellige Binärzahl repräsentierbar durch eine 8-stellige Hexzahl und genau diese 8 hinteren Stellen ist das, was mir mit %x ausgegeben wird. Ist das korrekt?
-
Wolfone schrieb:
Du sagst "Das erklärt die Ausgabe in Zeile 6, sofern man annimmt, dass ein Integer eben 4 Byte groß ist "; bedeutet das, dass hier sozusagen pro Adresse 1 byte Speicherplatz zur Verfügung stünde?
Ja.
Du kannst jedes Byte adressieren.
Wie groß das Byte dann ist, ist eine andere Sache. (Die sind bei C mindestens 8 Bit, können aber auch mehr haben. z.B auf DSP)Wolfone schrieb:
Ich habe jetzt auf einem anderen Board gelesen, dass %x zur Ausgabe einer Hexadezimalzahl gebraucht wird und %p zur Ausgabe eines Pointers. Wie erklärt sich dann der Punkt, an dem die "echte" Adresse in 5 abgeschnitten wird? Meine Erklärung: Wenn wir davon ausgehen, dass ein Int 4 Byte bekommt, dann wäre eine 32-stellige Binärzahl repräsentierbar durch eine 8-stellige Hexzahl und genau diese 8 hinteren Stellen ist das, was mir mit %x ausgegeben wird. Ist das korrekt?
Deine Pointer scheinen größer als 32-Bit zu sein. (sonst würden bei %p auch nur max 8 Stellen kommen)
Dass du die unteren 4 Byte zu sehen bekommst, hängt auch mit der Endianness von deinem System zusammen.
-
Es ist auch vom Compiler und dessen Einstellung abhängig.
Hier mal von meiner Maschine x86_64, int: 4 bytes, pointer: 8 bytes
Bei clang, tcc sowie gcc -O3 kommt sowas raus0x7fff7f6a0bdc 0 0x7fff7f6a0be0 0x7fff7f6a0bd0 65
und bei gcc -O0 sowas
0x7fff84446b14 84446b18 0x7fff84446b18 0x7fff84446b18 65
ok welches von beiden soll ich nehmen und ist es überhaupt richtig x86_64 für die Beantwortung zu verwenden?
-
Erstmal Danke ihr beiden! Da scheint es ja wirklich diverse nicht triviale Abhängigkeiten zu geben.
Also ich kann zumindest mal dazu sagen, dass die Ausgaben von mir auf einem 64bit linuxbasierten Betriebssystem (Ubuntu) erstellt wurden.
(Es war zur Beantwortung ein beliebiges linuxbasiertes System vorausgesetzt)gary, wie erklärst du dir denn in deiner ersten Beispielausgabe die 0 in der zweiten Zeile? Ich kann mir zu dieser späten Stunde nicht auf Anhieb erklären, wie die Formatierung gerade dieses eine Zeichen übrig lässt.
-
Wolfone schrieb:
gary, wie erklärst du dir denn in deiner ersten Beispielausgabe die 0 in der zweiten Zeile? Ich kann mir zu dieser späten Stunde nicht auf Anhieb erklären, wie die Formatierung gerade dieses eine Zeichen übrig lässt.
Die ohnehin schon gewagte Annahme, dass die Variablen in Deklarationsreihenfolge im Speicher stehen, gilt überhaupt gar nicht mehr, wenn Optimierungen eingeschaltet sind. Mit eingeschalteten Optimierungen werden viele einfache Variablen vollständig weg optimiert. Da das Programmverhalten undefiniert ist, wird dabei auch keine Rücksicht da drauf genommen, dass das Verhalten des unoptimierten Programms vom optimierten Programm reproduziert wird.
Hier liegt foo an einer niedrigeren Adresse als bar. Hinter bar steht anscheinend zufällig eine 0.
-
Hm...hört sich logisch an. Vielen Dank nochmal allen