class Address {
int i ;
char b;
string c;
public:
void showMap ( void ) ;
};
void Address :: showMap ( void ) {
cout << "address of int :" << &i << endl ;
cout << "address of char :" << &b << endl ;
cout << "address of string :" << &c << endl ;
}
Die Ausgabe ist:
address of int : something
address of char : // nothing, blank area, that is nothing displayed
address of string : something
Warum?
Eine andere interessante Sache: Wenn int, char, string in public ist, dann ist die Ausgabe
... int : something
... char :
... string : something_2
something_2 - something
ist immer gleich 8. Warum? (nicht 9)
Wenn Sie die Adresse von b übernehmen, erhalten Sie char *
. operator<<
interpretiert das als C-String und versucht, anstelle der Adresse eine Zeichenfolge zu drucken.
versuchen Sie stattdessen cout << "address of char :" << (void *) &b << endl
.
[EDIT] Wie Tomek kommentiert, ist static_cast
eine geeignetere Methode, die verwendet werden kann. Dies ist eine sicherere Alternative. Hier ist eine Version, die es anstelle des C-Stils verwendet:
cout << "address of char :" << static_cast<void *>(&b) << endl;
Es gibt zwei Fragen:
Druckzeiger drucken die Adresse für den int*
und den string*
, aber den Inhalt für char*
nicht, da in operator<<
eine spezielle Überladung vorliegt. Wenn Sie die Adresse wünschen, verwenden Sie: static_cast<const void *>(&c);
int
und der string
8
Auf Ihrer Plattform ist sizeof(int)
4
und sizeof(char)
ist 1
. Sie sollten sich also wirklich fragen, warum 8
nicht 5
. Der Grund dafür ist, dass der String an einer 4-Byte-Grenze ausgerichtet ist. Maschinen arbeiten mit Wörtern statt mit Bytes und arbeiten schneller, wenn Wörter hier nicht einige Bytes und einige Bytes "aufgeteilt" werden. Dies wird alsAusrichtungbezeichnet.
Ihr System stimmt wahrscheinlich mit 4-Byte-Grenzen überein. Wenn Sie ein 64-Bit-System mit 64-Bit-Ganzzahlen hätten, wäre der Unterschied 16.
(Hinweis: 64-Bit-System bezieht sich im Allgemeinen auf die Größe eines Zeigers, nicht auf ein Int.) Ein 64-Bit-System mit einem 4-Byte-Int würde also immer noch eine Differenz von 8 als 4 + 1 = 5 aufweisen, aber auf 8 runden Wenn sizeof (int) 8 ist, dann ist 8 + 1 = 9, aber es wird auf 16 gerundet.
Wenn Sie die Adresse eines Zeichens in einen Ostream streamen, interpretiert es diese als Adresse des ersten Zeichens einer ASCIIZ-Zeichenfolge "C-style" und versucht, die vermutete Zeichenfolge zu drucken. Sie haben kein NUL-Abschlusszeichen, so dass die Ausgabe so lange versucht, aus dem Speicher zu lesen, bis eine gefunden wird oder das Betriebssystem heruntergefahren wird, um zu versuchen, von einer ungültigen Adresse zu lesen. Der gesamte gescannte Müll wird an Ihre Ausgabe gesendet.
Sie können wahrscheinlich die gewünschte Adresse anzeigen lassen, indem Sie sie wie in (void*)&b
umwandeln.
Betrachten Sie die Offsets in der Struktur: Sie haben beobachtet, dass der String an Offset 8 platziert ist. Dies liegt wahrscheinlich daran, dass Sie 32-Bit-Ints haben, dann ein 8-Bit-Zeichen, und der Compiler wählt dann 3 weitere 8-Bit-Zeichen aus String-Objekt wird an einer 32-Bit-Word-Grenze ausgerichtet. Viele CPUs/Speicherarchitekturen benötigen Zeiger, Ints usw., um sich an Grenzen der Word-Größe zu befinden, um effiziente Operationen an ihnen durchzuführen. Andernfalls müssten viele weitere Vorgänge ausgeführt werden, um mehrere Werte aus dem Speicher zu lesen und zu kombinieren, bevor die Werte verwendet werden können in einer Operation. Abhängig von Ihrem System kann es sein, dass jedes Klassenobjekt an einer Word-Grenze beginnen muss, oder es kann sein, dass std::string
insbesondere mit einem size_t, einem Zeiger oder einem anderen Typ beginnt, der eine solche Ausrichtung erfordert.
Wenn Sie einen char*
an std::ostream
übergeben, wird die Zeichenfolge im C-Stil (dh Char Array, char*
) gedruckt, auf die sie verweist.
Denken Sie daran, dass "hello"
ein char*
ist.
Die Adresse von char wird als nicht endende Zeichenfolge behandelt und zeigt den Inhalt dieser Adresse an, die wahrscheinlich undefiniert ist, in diesem Fall jedoch eine leere Zeichenfolge. Wenn Sie die Zeiger auf void *
setzen, erhalten Sie die gewünschten Ergebnisse.
Der Unterschied zwischen etwas2 und etwas 8 ist auf die Ausrichtung und die Fähigkeit des Compilers zurückzuführen, selbst zu entscheiden, wo im Stack die Variablen deklariert werden.
Für das zweite Problem - der Compiler füllt standardmäßig Strukturmitglieder auf. Das Standard-Pad enthält die sizeof(int)
, 4 Byte (bei den meisten Architekturen). Aus diesem Grund nimmt eine int
, gefolgt von einer char
, 8 Bytes in der Struktur ein, sodass das string
-Member am Offset 8 ist.
Um das Auffüllen zu deaktivieren, verwenden Sie #pragma pack(x)
, wobei x die Pad-Größe in Byte ist.
Deine Syntax sollte sein
cout << (void*) &b
hrnt hat recht mit dem Grund für das Leerzeichen: &b
hat den Typ char*
und wird daher bis zum ersten Null-Byte als Zeichenfolge gedruckt. Vermutlich ist b
0. Wenn Sie b
beispielsweise auf 'A' setzen, sollten Sie davon ausgehen, dass der Ausdruck eine Zeichenfolge ist, die mit 'A' beginnt und bis zum nächsten Null-Byte mit Müll fortgesetzt wird. Verwenden Sie static_cast<void*>(&b)
, um es als Adresse zu drucken.
Für Ihre zweite Frage ist &c - &i
8, da die Größe eines int 4 ist, das Zeichen 1 ist und die Zeichenfolge an der nächsten 8-Byte-Grenze beginnt (Sie befinden sich wahrscheinlich auf einem 64-Bit-System). Jeder Typ hat eine bestimmte Ausrichtung, und C++ richtet die Felder in der Struktur entsprechend aus, wobei die Auffüllung entsprechend hinzugefügt wird. (Die Faustregel besagt, dass ein primitives Feld der Größe N an einem Vielfachen von N ausgerichtet ist.) Insbesondere können Sie nach char
drei weitere b
-Felder hinzufügen, ohne die Adresse &c
zu beeinflussen.