Ist die Größe von (ein Zeiger) immer gleich vier?

227

Zum Beispiel: sizeof(char*)zurück 4. Wie hat int*, long long*alles, was ich versucht habe. Gibt es hiervon Ausnahmen?

Joel
quelle
51
Warum das notieren? Gute Frage für jeden Anfänger.
Martin York
2
Ich vermute, dass sich in dieser eine andere Frage versteckt: "Was ist sizeof?" oder kann sein "Warum ist sizeof <irgendein Zeiger> == 4? Was ist das Besondere an 4?". Habe ich recht?
2
Nun, es hängt von Ihrer Plattform ab. Die meisten Implementierungen haben für jede Art von Zeiger auf einer bestimmten Plattform dieselbe Größe.
Phoeagon

Antworten:

194

Die Garantie, die Sie erhalten, ist die sizeof(char) == 1. Es gibt keine anderen Garantien, einschließlich keiner Garantie dafür sizeof(int *) == sizeof(double *).

In der Praxis haben Zeiger auf einem 16-Bit-System (falls vorhanden) die Größe 2, auf einem 32-Bit-System die Größe 4 und auf einem 64-Bit-System die Größe 8, aber es gibt nichts zu gewinnen, wenn man sich auf ein bestimmtes System verlässt Größe.

David Thornley
quelle
96
Und 3 Bytes auf einem 24-Bit-System. Ja, ich habe an einem gearbeitet. Willkommen in der Welt der eingebetteten Geräte.
Dwj
30
Ich habe auch an 16-Bit-Systemen mit 20-Bit-Zeigern gearbeitet. Ich sollte mal sehen, wie groß die Rendite in diesem Fall ist ...
Richter Maygarden,
5
@monjardin: IIRC, der 8086 war so. Es gab eine 16-Bit-Adresse und ein 4-Bit-Segmentregister. Ich glaube, ein normaler "NEAR" -Zeiger war 16 Bit und ein als "FAR" deklarierter Zeiger war mehr, wahrscheinlich 24, obwohl ich nicht sicher bin.
Rmeador
18
Eine weitere Garantie ist, dass sizeof (char *) == sizeof (void *), da sie die gleichen Darstellungen haben müssen (Objekt [Größe] und Wert [Satz von Bits, die für ihren Wert relevant sind])
Johannes Schaub - litb
7
Da in der Frage nach Ausnahmen gefragt wird, sollte beachtet werden, dass nicht statische Elementfunktionszeiger häufig eine andere Größe als normale Zeiger haben und auch je nach Plattform, Typ usw. variieren. Abgesehen von +1.
John5342
36

Selbst auf einer einfachen x86 32-Bit-Plattform können Sie eine Vielzahl von Zeigergrößen erhalten. Probieren Sie dies als Beispiel aus:

struct A {};

struct B : virtual public A {};

struct C {};

struct D : public A, public C {};

int main()
{
    cout << "A:" << sizeof(void (A::*)()) << endl;
    cout << "B:" << sizeof(void (B::*)()) << endl;
    cout << "D:" << sizeof(void (D::*)()) << endl;
}

Unter Visual C ++ 2008 erhalte ich 4, 12 und 8 für die Größe der Zeiger-auf-Mitglied-Funktion.

Raymond Chen hat hier darüber gesprochen .

Finsternis
quelle
4
Zeiger auf Mitgliedsfunktionen sind ein echtes Problem. Es ist bedauerlich, dass nicht alle Compiler den Digital Mars C ++ - Compiler mögen, der in allen Fällen 4 zurückgibt.
dalle
gcc 4.72 print all 8 ... Ist dies im C ++ - Standard undefiniert?
Gob00st
2
@ Gob00st: Das einzige, was definiert ist, ist, dass char 1 ist. Andere Typen können jede Größe haben, die für diesen Compiler relevant ist. Es besteht keine Anforderung an die Konsistenz zwischen diesen Zeigertypen.
Eclipse
OK danke. Dann ist es kein Wunder, dass gcc & VC unterschiedliche Implementierungen haben.
Gob00st
5
@ Eclipse ja gibt es: char <= kurz <= int <= lang <= lang lang
Cole Johnson
30

Nur eine weitere Ausnahme von der bereits veröffentlichten Liste. Auf 32-Bit-Plattformen können Zeiger 6, nicht 4 Bytes benötigen:

#include <stdio.h>
#include <stdlib.h>

int main() {
    char far* ptr; // note that this is a far pointer
    printf( "%d\n", sizeof( ptr));
    return EXIT_SUCCESS;
}

Wenn Sie dieses Programm mit Open Watcom kompilieren und ausführen, erhalten Sie 6, da die von ihm unterstützten Fernzeiger aus 32-Bit-Offset- und 16-Bit-Segmentwerten bestehen

dmityugov
quelle
5
Kein Segment, sondern Selektor - es ist kein Teil der Speicheradresse, sondern ein Indexeintrag im LDT oder GDT und hat einige Zugriffsflags
Roee Shenberg
1
Warum gibt es in x86 Segmente und Offsets, während der Adressraum flach ist?
Phuclv
@ LưuVĩnhPhúc Weil es Platz für den sehr häufigen Fall von Near-Zeigern spart, die kürzer codiert werden können.
Christopher Creutzig
1
@ChristopherCreutzig bedeutet das, dass die Segmente zur Erweiterung des Adressraums wie PAE verwendet werden?
Phuclv
@ LưuVĩnhPhúc Es ist lange her, dass ich alles mit 32 Bit zusammengebaut habe. Der Teil, an den ich mich zu erinnern scheine, ist, dass Sie Platz für Zeiger sparen können, die auf den Code zeigen, den Sie haben. Außerdem verwenden nicht alle 32-Bit-Architekturen - sicherlich nicht alle, die auf x86 basieren - ein Flat-Memory-Modell. Weitere Informationen hierzu finden Sie beispielsweise unter tenouk.com/Bufferoverflowc/Bufferoverflow1a.html , obwohl es, wie gesagt, eine Weile her ist und ich für nichts bürgen kann.
Christopher Creutzig
24

Wenn Sie für einen 64-Bit-Computer kompilieren, kann dies 8 sein.

FryGuy
quelle
2
Dies ist normalerweise der Fall, aber nicht unbedingt der Fall. Wenn Sie beispielsweise auf einem 64-Bit-Computer kompilieren, dessen Wortgröße 64-Bit beträgt, ist sizeof (char *) wahrscheinlich 1. Ganz zu schweigen von den exotischeren Zeigertypen in selbst gängigen Computern wie Eclipse und dmityugov schreiben.
Kaz Dragon
@ KazDragon , sizeof(char*)==1? Bist du sicher? Meinst du nicht size(char)==1?
Aaron McDaid
3
@ AaronMcDaid Ich meinte in der Tat sizeof (char *). sizeof (char) ist immer 1. Wenn Ihr Maschinenwort jedoch 64-Bit ist und Ihre Entwicklungsumgebung so implementiert ist, dass CHAR_BITS = 64 ist, ist es möglich, dass ein Zeiger in den gleichen Bereich wie ein char passt und dies daher tut auch sein 1.
Kaz Dragon
es ist nicht wahr in x32-abi sites.google.com/site/x32abi
phuclv
1
@KazDragon Ich baue (sehr langsam, wenn ich nicht zögere) eine Maschine mit 16-Bit-Wörtern und ohne Byteadressierung. Obwohl es sowieso nicht C laufen kann.
user253751
17

Technisch gesehen garantiert der C-Standard nur die Größe von (char) == 1, und der Rest hängt von der Implementierung ab. Auf modernen x86-Architekturen (z. B. Intel / AMD-Chips) ist dies jedoch ziemlich vorhersehbar.

Sie haben wahrscheinlich Prozessoren gehört, die als 16-Bit, 32-Bit, 64-Bit usw. beschrieben wurden. Dies bedeutet normalerweise, dass der Prozessor N-Bit für Ganzzahlen verwendet. Da Zeiger Speicheradressen speichern und Speicheradressen Ganzzahlen sind, gibt dies effektiv an, wie viele Bits für Zeiger verwendet werden sollen. sizeof wird normalerweise in Bytes gemessen, sodass für 32-Bit-Prozessoren kompilierter Code eine Zeigergröße von 4 (32 Bit / 8 Bit pro Byte) und Code für 64-Bit-Prozessoren eine Größe von 8 Zeigern angibt (64 Bit / 8 Bit pro Byte). Hier liegt die Beschränkung von 4 GB RAM für 32-Bit-Prozessoren. Wenn jede Speicheradresse einem Byte entspricht, benötigen Sie Ganzzahlen, die größer als 32 Bit sind, um mehr Speicher zu adressieren.

Joseph Garvin
quelle
"Sie haben wahrscheinlich Prozessoren gehört, die als 16-Bit, 32-Bit, 64-Bit usw. beschrieben wurden. Dies bedeutet normalerweise, dass der Prozessor N-Bit für Ganzzahlen verwendet." -> Ich habe einen 64-Bit-Computer, aber die Größe von (int) beträgt 4 Bytes. Wenn Ihre Aussage wahr ist, wie könnte dies möglich sein?!
Sangeeth Saravanaraj
6
@SangeethSaravanaraj: Aus Gründen der Abwärtskompatibilität mit 32-Bit-Code haben sie beschlossen, dass int weiterhin 4 Byte beträgt, und Sie müssen sich für die Verwendung des 8-Byte-Typs anmelden, indem Sie 'long' angeben. lang ist eigentlich die native Wortgröße auf x86-64. Eine Möglichkeit, dies zu sehen, besteht darin, dass Compiler normalerweise Ihre Strukturen auffüllen, um sie wortausgerichtet zu machen (obwohl es möglicherweise Architekturen gibt, bei denen Wortgröße und Ausrichtung nicht miteinander zusammenhängen). Wenn Sie also eine Struktur mit einem int (32-Bit) erstellen, und rufen Sie sizeof () auf, wenn Sie 8 zurückbekommen, wissen Sie, dass es sie auf 64-Bit-Wortgröße auffüllt.
Joseph Garvin
@SangeethSaravanaraj: Beachten Sie, dass theoretisch die native Wortgröße der CPU und das, was der Compiler für 'int' entscheidet, beliebig unterschiedlich sein können. Es war nur die Konvention, dass 'int' die native Wortgröße ist, bevor x86-64 auftauchte Es ist lang, die Abwärtskompatibilität zu vereinfachen.
Joseph Garvin
Danke für die Erklärung! :)
Sangeeth Saravanaraj
7

Die Größe des Zeigers hängt im Wesentlichen von der Architektur des Systems ab, in dem er implementiert ist. Beispielsweise beträgt die Größe eines Zeigers in 32-Bit 4 Byte (32 Bit) und 8 Byte (64 Bit) in 64-Bit-Maschinen. Die Bittypen in einer Maschine sind nichts anderes als eine Speicheradresse, die sie haben kann. 32-Bit-Maschinen können einen 2^32Adressraum haben und 64-Bit-Maschinen können bis zu 2^64Adressräume haben. Ein Zeiger (eine Variable, die auf einen Speicherort zeigt) sollte also auf eine der Speicheradressen ( 2^32 for 32 bit and 2^64 for 64 bit) verweisen können , die eine Maschine enthält.

Aus diesem Grund beträgt die Größe eines Zeigers in einer 32-Bit-Maschine 4 Byte und in einer 64-Bit-Maschine 8 Byte.

Rndp13
quelle
6

Zusätzlich zu den 16/32/64 Bitunterschieden können noch seltsamere Dinge auftreten.

Es gab Maschinen, bei denen sizeof (int *) einen Wert hat, wahrscheinlich 4, bei denen sizeof (char *) jedoch größer ist. Maschinen, die natürlich Wörter anstelle von Bytes adressieren, müssen Zeichenzeiger "erweitern", um anzugeben, welchen Teil des Wortes Sie wirklich möchten, um den C / C ++ - Standard ordnungsgemäß zu implementieren.

Dies ist jetzt sehr ungewöhnlich, da Hardware-Designer den Wert der Byteadressierbarkeit gelernt haben.

Darron
quelle
4
Der C-Compiler für Cray-Vektormaschinen wie der T90 macht etwas Ähnliches. Hardwareadressen sind 8 Byte und zeigen auf 8-Byte-Wörter. void*und char*werden in Software behandelt und mit einem 3-Bit-Offset innerhalb des Wortes erweitert. Da jedoch kein 64-Bit-Adressraum vorhanden ist, wird der Offset in den höherwertigen 3 Bits des 64-Bit-Adressraums gespeichert Wort. Also char*und int*sind gleich groß, haben aber unterschiedliche interne Darstellungen - und Code, der davon ausgeht, dass Zeiger "wirklich" sind, nur ganze Zahlen können schlecht ausfallen.
Keith Thompson
5

8-Bit- und 16-Bit-Zeiger werden in den meisten Low-Profile-Mikrocontrollern verwendet. Das bedeutet jede Waschmaschine, jedes Mikro, jeden Kühlschrank, jeden älteren Fernseher und sogar jedes Auto.

Man könnte sagen, diese haben nichts mit realer Programmierung zu tun. Aber hier ist ein Beispiel aus der Praxis: Arduino mit 1-2-4k RAM (je nach Chip) mit 2 Byte Zeigern.

Es ist neu, billig, für jedermann zugänglich und es lohnt sich, dafür zu programmieren.

Kobor42
quelle
4

Zusätzlich zu dem, was die Leute über 64-Bit-Systeme (oder was auch immer) gesagt haben, gibt es andere Arten von Zeigern als Zeiger auf Objekt.

Ein Zeiger auf ein Mitglied kann fast jede Größe haben, je nachdem, wie sie von Ihrem Compiler implementiert werden: Sie sind nicht unbedingt alle gleich groß. Versuchen Sie es mit einem Zeiger auf ein Mitglied einer POD-Klasse und anschließend mit einem Zeiger auf ein Mitglied, das von einer der Basisklassen einer Klasse mit mehreren Basen geerbt wurde. Was für ein Spaß.

Steve Jessop
quelle
3

Soweit ich mich erinnere, basiert es auf der Größe einer Speicheradresse. Auf einem System mit einem 32-Bit-Adressschema gibt sizeof 4 zurück, da dies 4 Bytes sind.

Will Mc
quelle
4
Es gibt keine solche Anforderung. Es gibt nicht einmal eine Anforderung, dass sizeof (unsigned int) == sizeof (igned int) ist. Die Größe eines Zeigers auf ein int ist per Definition immer sizeof (int *), auf char sizeof (char *) usw. Es ist eine schlechte Idee für die Portabilität, sich auf eine andere Annahme zu verlassen.
Mihai Limbășan
Ah, ich verstehe jetzt. Danke für die Information.
Will Mc
1
Könnte immer noch 2 zurückgeben, wenn CHAR_BIT 16 ist. Sizeof () zählt in der Anzahl der Zeichen, nicht in Oktetten.
MSalters
5
@Mihai: In C ++ sizeof (unsigned int) == sizeof (signed int)ist diese Anforderung in 3.9.1 / 3 enthalten. „Für jede der Standard Integer - Typen signiert, existiert eine entsprechende (jedoch unterschiedlichen) -Standard unsigned integer Typ: unsigned char, unsigned short int, unsigned int, unsigned long int, und unsigned long long int, von denen jedes nimmt die gleiche Menge an Speicher und hat die gleiche Ausrichtung Anforderungen wie die Integer - Typ signiert entsprechende "
Ben Voigt
3

Im Allgemeinen ändert sich sizeof (so ziemlich alles), wenn Sie auf verschiedenen Plattformen kompilieren. Auf einer 32-Bit-Plattform haben Zeiger immer die gleiche Größe. Auf anderen Plattformen (64 Bit ist das offensichtliche Beispiel) kann sich dies ändern.

Sean Reilly
quelle
3

Nein, die Größe eines Zeigers kann je nach Architektur variieren. Es gibt zahlreiche Ausnahmen.

Richter Maygarden
quelle
3

Die Größe von Zeiger und int beträgt im Turbo C-Compiler auf einem 32-Bit-Windows-Computer 2 Byte.

Die Größe des Zeigers ist also compilerspezifisch. Im Allgemeinen sind die meisten Compiler jedoch so implementiert, dass sie 4-Byte-Zeigervariablen in 32-Bit- und 8-Byte-Zeigervariablen in 64-Bit-Maschinen unterstützen.

Die Größe des Zeigers ist also nicht bei allen Maschinen gleich.

finalemester.co.in
quelle
2

Der Grund, warum Ihr Zeiger 4 Byte groß ist, liegt darin, dass Sie für eine 32-Bit-Architektur kompilieren. Wie FryGuy betonte, würden Sie auf einer 64-Bit-Architektur 8 sehen.

Will Bickford
quelle
2

In Win64 (Cygwin GCC 5.4) sehen wir uns das folgende Beispiel an:

Testen Sie zunächst die folgende Struktur:

struct list_node{
    int a;
    list_node* prev;
    list_node* next;
};

struct test_struc{
    char a, b;
};

Der Testcode ist unten:

std::cout<<"sizeof(int):            "<<sizeof(int)<<std::endl;
std::cout<<"sizeof(int*):           "<<sizeof(int*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(double):         "<<sizeof(double)<<std::endl;
std::cout<<"sizeof(double*):        "<<sizeof(double*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(list_node):      "<<sizeof(list_node)<<std::endl;
std::cout<<"sizeof(list_node*):     "<<sizeof(list_node*)<<std::endl;
std::cout<<std::endl;

std::cout<<"sizeof(test_struc):     "<<sizeof(test_struc)<<std::endl;
std::cout<<"sizeof(test_struc*):    "<<sizeof(test_struc*)<<std::endl;    

Die Ausgabe ist unten:

sizeof(int):            4
sizeof(int*):           8

sizeof(double):         8
sizeof(double*):        8

sizeof(list_node):      24
sizeof(list_node*):     8

sizeof(test_struc):     2
sizeof(test_struc*):    8

Sie können sehen, dass in 64-Bit sizeof(pointer)ist 8.

Jayhello
quelle
1

Ein Zeiger ist nur ein Container für eine Adresse. Auf einem 32-Bit-Computer beträgt Ihr Adressbereich 32 Bit, sodass ein Zeiger immer 4 Byte lang ist. Auf einem 64-Bit-Computer mit einem Adressbereich von 64 Bit beträgt der Zeiger 8 Byte.

Ed S.
quelle
1
Auf einem 32-Bit-Computer mit 32-Bit-Bytes könnte sizeof (char *) 1 sein.
Robert Gamble
"... mit 32-Bit-Bytes". Ich wusste nicht, dass solche Dinge existieren ... stell dir das vor.
Ed S.
1
Auf einer 32-Bit-Ente gibt sizeof (char *) PI
Adriano Varoli Piazza
0

Der Vollständigkeit halber und aus historischen Gründen gab es in der 64-Bit-Welt unterschiedliche Plattformkonventionen für die Größen von langen und langen langen Typen mit den Namen LLP64 und LP64, hauptsächlich zwischen Unix-Systemen und Windows. Ein alter Standard namens ILP64 hat auch int = 64-Bit breit gemacht.

Microsoft hat LLP64 beibehalten, wobei longlong = 64 Bit breit ist, aber zur einfacheren Portierung lange bei 32 geblieben ist.

Type           ILP64   LP64   LLP64
char              8      8       8
short            16     16      16
int              64     32      32
long             64     64      32
long long        64     64      64
pointer          64     64      64

Quelle: https://stackoverflow.com/a/384672/48026

Hernán
quelle