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.