Czy sizeof (jakiś wskaźnik) zawsze wynosi cztery?


227

Na przykład: sizeof(char*)zwraca 4. Jak nie int*, long long*wszystko, co próbowałem. Czy są jakieś wyjątki od tego?


51
Po co to zaznaczać? Dobre pytanie dla każdego początkującego.
Martin York

2
Podejrzewam, że kryje się w tym inne pytanie: „Co to jest sizeof?” lub może być „Dlaczego sizeof <jakikolwiek wskaźnik> == 4? Co jest takiego specjalnego w 4?”. Czy mam rację?

2
To zależy od twojej platformy. Większość implementacji ma ten sam rozmiar dla każdego rodzaju wskaźnika na konkretnej platformie.
phoeagon

Odpowiedzi:


194

To gwarancja, którą otrzymujesz sizeof(char) == 1. Nie ma innych gwarancji, w tym żadnej gwarancji tego sizeof(int *) == sizeof(double *).

W praktyce wskaźniki będą miały rozmiar 2 w systemie 16-bitowym (jeśli można go znaleźć), 4 w systemie 32-bitowym i 8 w systemie 64-bitowym, ale nic nie można zyskać polegając na danym rozmiar.


96
I 3 bajty w systemie 24-bitowym. Tak, pracowałem nad jednym. Witamy w świecie urządzeń osadzonych.
dwj

30
Pracowałem również na systemach 16-bitowych z 20-bitowymi wskaźnikami. Powinienem sprawdzić, jaki rozmiar zwraca w tej sprawie ...
Sędzia Maygarden,

5
@monjardin: IIRC, 8086 był taki. Był 16-bitowy adres i 4-bitowy rejestr segmentu. Uważam, że normalny wskaźnik „BLISKO” miał 16 bitów, a wskaźnik zadeklarowany jako „FAR” miał więcej, prawdopodobnie 24, choć nie jestem pewien.
rmeador

18
inną gwarancją jest to, że sizeof (char *) == sizeof (void *), ponieważ muszą mieć te same reprezentacje (obiekt [rozmiar] i wartość [zestaw bitów odpowiednich dla ich wartości] reprezentacja)
Johannes Schaub - litb

7
Ponieważ pytanie wymaga wyjątków, należy zauważyć, że niestatyczne wskaźniki funkcji składowych są często innego rozmiaru niż normalne wskaźniki, a także różnią się w zależności od platformy, typu itp. Inne niż +1.
John5342

36

Nawet na zwykłej 32-bitowej platformie x86 możesz uzyskać różne rozmiary wskaźników, wypróbuj to na przykład:

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;
}

W Visual C ++ 2008 dostaję 4, 12 i 8 dla rozmiarów funkcji wskaźniki do członka.

Raymond Chen mówił o tym tutaj .


4
Wskaźniki do funkcji składowych są prawdziwym bólem. Szkoda, że ​​nie wszystkie kompilatory lubią kompilator Digital Mars C ++, który zwraca 4 we wszystkich przypadkach.
dalle

gcc 4.72 wydrukuj wszystkie 8 ... Czy jest to niezdefiniowane w standardzie c ++?
Gob00st,

2
@ Gob00st: Jedyną zdefiniowaną rzeczą jest to, że char wynosi 1. Inne typy mogą mieć dowolny rozmiar odpowiedni dla tego kompilatora. Nie ma wymogu spójności między tymi typami wskaźników.
Zaćmienie

ok dzięki. Nic dziwnego, że gcc i VC mają inną implementację.
Gob00st

5
@Eclipse tak jest: char <= krótki <= int <= długi <= długi długi
Cole Johnson

30

Kolejny wyjątek od już opublikowanej listy. Na platformach 32-bitowych wskaźniki mogą zająć 6, a nie 4 bajty:

#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;
}

Jeśli skompilujesz ten program z Open Watcom i uruchomisz go, otrzymasz 6, ponieważ dalekie obsługiwane wskaźniki składają się z 32-bitowego przesunięcia i 16-bitowych wartości segmentów


5
Nie segment, ale selektor - nie jest to część adresu pamięci, ale wpis indeksu w LDT lub GDT i ma pewne flagi dostępu
Roee Shenberg

1
Dlaczego w x86 istnieją segmenty i odsunięcia, gdy przestrzeń adresowa jest płaska?
phuclv

@ LưuVĩnhPhúc Ponieważ oszczędza miejsce na bardzo częsty przypadek bliskich wskaźników, które można zakodować krócej.
Christopher Creutzig

1
@ChristopherCreutzig, co oznacza, że ​​segmenty są używane do rozszerzania przestrzeni adresowej, takiej jak PAE?
phuclv

@ LưuVĩnhPhúc Dawno już nie robiłem montażu na czymkolwiek 32-bitowym. Wydaje mi się, że pamiętam, że możesz zaoszczędzić miejsce na wskaźniki wskazujące w pobliżu posiadanego kodu. Ponadto nie wszystkie architektury 32-bitowe - z pewnością nie wszystkie oparte na architekturze x86 - używają płaskiego modelu pamięci. Zobacz np. Tenouk.com/Bufferoverflowc/Bufferoverflow1a.html , aby uzyskać więcej dyskusji na ten temat, chociaż, jak powiedziałem, minęło trochę czasu i nie mogę za nic poręczyć.
Christopher Creutzig

24

jeśli kompilujesz dla komputera 64-bitowego, może to być 8.


2
Chociaż zwykle tak jest, niekoniecznie jest to prawda. Na przykład, jeśli kompilujesz na komputerze 64-bitowym, w którym rozmiar słowa to 64-bity, to rozmiarof (char *) prawdopodobnie będzie wynosił 1. Nie wspominając o bardziej egzotycznych typach wskaźników nawet w popularnych komputerach, takich jak Eclipse i dmityugov pisać.
Kaz Dragon

@KazDragon, sizeof(char*)==1? Jesteś pewny? Nie masz na myśli size(char)==1?
Aaron McDaid

3
@AaronMcDaid Naprawdę miałem na myśli sizeof (char *). sizeof (char) jest zawsze 1. Ale jeśli twoje słowo maszynowe ma 64 bity, a twoje środowisko programistyczne jest zaimplementowane w taki sposób, że CHAR_BITS = 64, możliwe jest, że wskaźnik mieści się w tej samej przestrzeni co znak i dlatego też być 1.
Kaz Dragon


1
@KazDragon Buduję (bardzo powoli, kiedy nie zwlekam) maszynę z 16-bitowymi słowami i bez adresowania bajtów. Mimo to i tak nie można uruchomić C.
user253751,

17

Technicznie rzecz biorąc, standard C gwarantuje tylko, że sizeof (char) == 1, a reszta zależy od implementacji. Ale na nowoczesnych architekturach x86 (np. Układach Intel / AMD) jest to dość przewidywalne.

Prawdopodobnie słyszałeś procesory opisane jako 16-bitowe, 32-bitowe, 64-bitowe itp. Zazwyczaj oznacza to, że procesor używa bitów N do liczb całkowitych. Ponieważ wskaźniki przechowują adresy pamięci, a adresy pamięci są liczbami całkowitymi, to skutecznie informuje, ile bitów zostanie wykorzystanych na wskaźniki. sizeof jest zwykle mierzony w bajtach, więc kod skompilowany dla procesorów 32-bitowych zgłasza rozmiar wskaźników na 4 (32 bity / 8 bitów na bajt), a kod dla 64-bitowych procesorów zgłasza rozmiar wskaźników na 8 (64 bity / 8 bitów na bajt). Stąd bierze się ograniczenie 4 GB pamięci RAM dla procesorów 32-bitowych - jeśli każdy adres pamięci odpowiada bajtowi, do adresowania większej ilości pamięci potrzebne są liczby całkowite większe niż 32-bitowe.


„Prawdopodobnie słyszałeś procesory opisane jako 16-bitowe, 32-bitowe, 64-bitowe itp. Zazwyczaj oznacza to, że procesor używa bitów N do liczb całkowitych.” -> Mam komputer 64-bitowy, ale sizeof (int) wynosi 4 bajty. Jeśli twoje stwierdzenie jest prawdziwe, jak to możliwe ?!
Sangeeth Saravanaraj

6
@SangeethSaravanaraj: Aby zachować zgodność wsteczną z kodem 32-bitowym, zdecydowali, że int nadal będzie mieć 4 bajty i wymagają włączenia wyrażenia typu 8-bajtowego poprzez określenie „long”. long jest faktycznie rodzimym rozmiarem słowa na x86-64. Jednym ze sposobów na to jest to, że zwykle kompilatory wypełniają struktury, aby dopasować je do słów (chociaż mogą istnieć architektury, w których rozmiar słowa i wyrównanie nie są ze sobą powiązane), więc jeśli utworzysz strukturę z int (32-bitową), i wywołaj na nim sizeof (), jeśli odzyskasz 8, wiesz, że uzupełnia je do rozmiaru słowa 64-bitowego.
Joseph Garvin

@SangeethSaravanaraj: Zauważ, że teoretycznie rozmiar natywnego procesora i to, co kompilator decyduje o „int”, może być dowolnie inny, po prostu konwencja, aby „int” była rodzimym rozmiarem, zanim pojawiło się x86-64, gdzie jest długo, aby ułatwić kompatybilność wsteczną
Joseph Garvin

Dziękuję za wyjaśnienie! :)
Sangeeth Saravanaraj,

7

Rozmiar wskaźnika zasadniczo zależy od architektury systemu, w którym jest on implementowany. Na przykład rozmiar wskaźnika w 32 bitach wynosi 4 bajty (32 bity) i 8 bajtów (64 bity) na komputerach 64-bitowych. Typy bitów w maszynie to tylko adres pamięci, który może mieć. Maszyny 32-bitowe mogą mieć 2^32przestrzeń adresową, a maszyny 64-bitowe mogą mieć 2^64przestrzenie adresowe. Tak więc wskaźnik (zmienna wskazująca lokalizację pamięci) powinien być w stanie wskazać dowolny adres pamięci (2^32 for 32 bit and 2^64 for 64 bit ) przechowywany przez maszynę.

Z tego powodu widzimy, że rozmiar wskaźnika to 4 bajty w maszynie 32-bitowej i 8 bajtów w maszynie 64-bitowej.


6

Oprócz różnic 16/32/64 bitów mogą wystąpić nawet dziwniejsze rzeczy.

Istnieją maszyny, w których sizeof (int *) będzie miała jedną wartość, prawdopodobnie 4, ale w których sizeof (char *) jest większy. Maszyny, które naturalnie adresują słowa zamiast bajtów, muszą „uzupełnić” wskaźniki znaków, aby określić, jakiej części słowa naprawdę chcesz, aby poprawnie wdrożyć standard C / C ++.

Jest to obecnie bardzo niezwykłe, ponieważ projektanci sprzętu nauczyli się wartości adresowalności bajtów.


4
Kompilator C dla maszyn wektorowych Cray, takich jak T90, robi coś podobnego. Adresy sprzętowe mają 8 bajtów i wskazują 8-bajtowe słowa. void*i char*są obsługiwane w oprogramowaniu i są powiększone o 3-bitowe przesunięcie w obrębie słowa - ale ponieważ tak naprawdę nie ma 64-bitowej przestrzeni adresowej, przesunięcie jest przechowywane w 3-bitowych rzędach 64-bitowych wyższego rzędu słowo. Tak char*i int*są tej samej wielkości, ale mają różne reprezentacje wewnętrzne - i kod, który zakłada, że wskaźniki są „naprawdę” tylko całkowitymi może nie źle.
Keith Thompson,

5

Wskaźniki 8-bitowe i 16-bitowe są używane w większości mikrokontrolerów niskoprofilowych. Oznacza to, że każda pralka, kuchenka mikrofalowa, lodówka, starsze telewizory, a nawet samochody.

Można powiedzieć, że nie mają one nic wspólnego z programowaniem w świecie rzeczywistym. Ale oto jeden przykład z prawdziwego świata: Arduino z ram 1-2-2k (w zależności od układu) z 2 bajtowymi wskaźnikami.

Jest to najnowsze, tanie, dostępne dla wszystkich i warte kodowania.


4

Oprócz tego, co ludzie mówili o systemach 64-bitowych (lub cokolwiek innego), istnieją inne rodzaje wskaźników niż wskaźnik do obiektu.

Wskaźnik do elementu może mieć prawie dowolny rozmiar, w zależności od sposobu implementacji przez kompilator: niekoniecznie mają one jednakowy rozmiar. Wypróbuj wskaźnik do elementu klasy POD, a następnie wskaźnik do elementu dziedziczony z jednej z klas podstawowych klasy z wieloma bazami. Co za zabawa.


3

Z tego co pamiętam, opiera się na wielkości adresu pamięci. Tak więc w systemie z 32-bitowym schematem adresu sizeof zwróci 4, ponieważ to 4 bajty.


4
Nie ma takiego wymogu. Nie ma nawet wymogu, że sizeof (unsigned int) == sizeof (podpisany int). Rozmiar wskaźnika do wartości int zawsze będzie z definicji wynosił sizeof (int *), a char sizeof (char *) itd. Poleganie na jakimkolwiek innym założeniu jest złym pomysłem na przenośność.
Mihai Limbășan

Ach, rozumiem teraz. Dzięki za informację.
Czy Mc

1
Może nadal zwracać 2, jeśli CHAR_BIT wynosi 16. sizeof () liczy się w liczbie znaków, a nie w oktetach.
MSalters

5
@Mihai: W C ++ sizeof (unsigned int) == sizeof (signed int)ten wymóg znajduje się w 3.9.1 / 3. „Dla każdego standardu ze znakiem typów istnieje odpowiadający (ale inne) stosuje się standardowe unsigned całkowitą: unsigned char, unsigned short int, unsigned int, unsigned long inti unsigned long long int, z których każdy zajmuje tę samą ilość miejsca i ma takie same wymagania wyrównania, co odpowiadający ze znakiem Typ
Ben Voigt,

3

Ogólnie rzecz biorąc, sizeof (właściwie wszystko) zmieni się podczas kompilacji na różnych platformach. Na platformie 32-bitowej wskaźniki są zawsze tego samego rozmiaru. Na innych platformach (64-bitowy oczywisty przykład) może się to zmienić.



3

Rozmiar wskaźnika i liczby int wynosi 2 bajty w kompilatorze Turbo C na 32-bitowym komputerze z systemem Windows.

Zatem rozmiar wskaźnika zależy od kompilatora. Ale generalnie większość kompilatorów jest implementowana do obsługi 4-bajtowej zmiennej wskaźnika w 32-bitowej i 8-bajtowej zmiennej wskaźnika w 64-bitowej maszynie).

Dlatego rozmiar wskaźnika nie jest taki sam na wszystkich komputerach.


2

Rozmiar wskaźnika wynosi 4 bajty, ponieważ kompilujesz się w architekturze 32-bitowej. Jak wskazał FryGuy, w architekturze 64-bitowej zobaczyłbyś 8.


2

W Win64 (Cygwin GCC 5.4) zobaczmy następujący przykład:

Najpierw przetestuj następującą strukturę:

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

struct test_struc{
    char a, b;
};

Kod testowy znajduje się poniżej:

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;    

Dane wyjściowe są poniżej:

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

Widać, że w 64-bitowym, sizeof(pointer)jest 8.


1

Wskaźnik jest tylko kontenerem na adres. Na maszynie 32-bitowej zakres adresów wynosi 32 bity, więc wskaźnik zawsze będzie wynosił 4 bajty. Na komputerze 64-bitowym, jeśli masz zakres adresów 64 bitów, wskaźnik będzie wynosił 8 bajtów.


1
Na 32-bitowej maszynie z 32-bitowymi bajtami sizeof (char *) może wynosić 1.
Robert Gamble

„... z 32-bitowymi bajtami”. Nie wiedziałem, że takie rzeczy istnieją ... wyobraź sobie, że.
Ed S.

1
Na 32-bitowej kaczce sizeof (char *) zwraca PI
Adriano Varoli Piazza

0

Dla kompletności i historycznego zainteresowania w świecie 64-bitowym istniały różne konwencje platform dotyczące rozmiarów długich i długich typów, nazwanych LLP64 i LP64, głównie między systemami typu Unix a Windows. Stary standard o nazwie ILP64 również miał szerokość int = 64-bit.

Microsoft utrzymywał LLP64, gdzie longlong = 64 bit szerokości, ale długo pozostawał na 32, dla łatwiejszego przenoszenia.

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

Źródło: https://stackoverflow.com/a/384672/48026

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.