Sizeof (bazı işaretçiler) her zaman dörde eşit mi?


227

Örneğin: sizeof(char*)As gelmez 4. döndürür int*, long long*ben denedim her şey o. Bunun istisnası var mı?


51
Bunu neden işaretlemelisiniz? Yeni başlayanlar için iyi bir soru.
Martin York

2
Bu soruda başka bir sorunun saklandığından şüpheleniyorum: "Boyut nedir?" veya "sizeof <herhangi bir işaretçi> == 4? 4 hakkında özel olan nedir?" olabilir. Haklı mıyım?

2
Peki, bu platformunuza bağlıdır. Çoğu uygulama, belirli bir platformdaki her tür işaretçi için aynı boyutu paylaşır.
phoeagon

Yanıtlar:


194

Aldığınız garanti şudur sizeof(char) == 1. Bunun garantisi de dahil olmak üzere başka garanti yoktur sizeof(int *) == sizeof(double *).

Pratikte, işaretçiler 16 bitlik bir sistemde 2 boyutunda (bir tane bulabilirseniz), 32 bitlik bir sistemde 4 ve 64 bitlik bir sistemde 8 olacaktır, ancak belirli bir verilere dayanarak kazanılacak hiçbir şey yoktur. boyut.


96
Ve 24 bit sistemde 3 bayt. Evet, üzerinde çalıştım. Tümleşik cihazların dünyasına hoş geldiniz.
dwj

30
20 bit işaretçilerle 16 bit sistemler üzerinde de çalıştım. Bu durumda hangi
boyutun

5
@monjardin: IIRC, 8086 böyleydi. 16 bit adres ve 4 bit segment kaydı vardı. Ben emin değilim ama normal bir "YAKIN" işaretçi 16 bit ve "FAR" olarak ilan bir işaretçi daha, muhtemelen 24 olduğunu düşünüyorum.
rmeador

18
bir başka garanti de sizeof (char *) == sizeof (void *), çünkü aynı temsillere sahip olmaları gerekir (nesne [boyut] ve değer [değerleriyle ilgili bit kümesi] gösterimi)
Johannes Schaub - litb

7
Soru istisnalar istediğinden, statik olmayan üye işlev işaretleyicilerinin genellikle normal işaretçilerden farklı bir boyutta olduğu ve aynı zamanda platforma, türe vb. Göre değiştiğine dikkat edilmelidir.
John5342

36

Düz bir x86 32 bit platformda bile, çeşitli işaretçi boyutları elde edebilirsiniz, bunu bir örnek için deneyin:

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

Visual C ++ 2008 altında, işaretçiler-üye-işlevinin boyutları için 4, 12 ve 8 olsun.

Raymond Chen burada bunun hakkında konuştu .


4
Üye işlevlerine işaretçiler gerçek bir acıdır. Her derleyicinin bunu her durumda 4 döndüren Digital Mars C ++ derleyicisi gibi yapması talihsiz bir durumdur.
dalle

gcc 4.72 tümünü yazdır 8 ... Bu c ++ standardında tanımlanmamış mı?
Gob00st

2
@ Gob00st: Tanımlanan tek şey karakterin 1 olmasıdır. Diğer türler, bu derleyicinin boyutu ne olursa olsun diğer türler olabilir. Bu işaretçi türleri arasında tutarlılık gerekmemektedir.
Tutulma

tamam teşekkürler. Sonra şaşkınlık gcc & VC'nin farklı uygulamaları var.
Gob00st

5
@Eclipse evet var: char <= kısa <= int <= uzun <= uzun uzun
Cole Johnson

30

Zaten gönderilen listeye bir istisna daha. 32 bit platformlarda, işaretçiler 4 değil , 6 bayt alabilir:

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

Bu programı Open Watcom ile derlerseniz ve çalıştırırsanız, 6 elde edersiniz, çünkü desteklediği uzak işaretçiler 32 bit ofset ve 16 bit segment değerlerinden oluşur


5
Segment değil, seçici değil - bellek adresinin bir parçası değil, LDT veya
GDT'de

1
Adres alanı düzken neden x86'da segmentler ve ofsetler var?
phuclv

@ LưuVĩnhPhúc Çünkü daha kısa kodlanabilen çok yakın işaretçiler için yer tasarrufu sağlar.
Christopher Creutzig

1
@ChristopherCreutzig, segmentlerin PAE gibi adres alanını genişletmek için kullanıldığı anlamına mı geliyor?
phuclv

@ LưuVĩnhPhúc Uzun süredir 32 bitlik bir şeyde montaj yaptım. Hatırladığım kısım, sahip olduğunuz koda yakın işaretçiler için yerden tasarruf edebileceğinizdir. Ayrıca, 32 bit mimarilerin hepsi - kesinlikle x86 tabanlı değil - düz bellek modeli kullanmaz. Bununla ilgili daha fazla tartışma için , örneğin, tenouk.com/Bufferoverflowc/Bufferoverflow1a.html adresine bakın, ancak söylediğim gibi, bir süredir oldu ve hiçbir şey için kefil olamam.
Christopher Creutzig

24

64 bit bir makine için derliyorsanız, 8 olabilir.


2
Bu genellikle böyle olsa da, mutlaka doğru değildir. Örneğin, kelime boyutunun 64 bit olduğu 64 bitlik bir makinede derliyorsanız, muhtemelen sizeof (char *) 1 olacaktır. Eclipse ve dmityugov yazma.
Kaz Ejderha

@KazDragon, sizeof(char*)==1? Emin misiniz? Bunu mu demek istediniz size(char)==1?
Aaron McDaid

3
@AaronMcDaid Gerçekten sizeof (char *) demek istedim. sizeof (char) her zaman 1'dir. Ancak makine kelimeniz 64 bit ise ve geliştirme ortamınız CHAR_BITS = 64 olacak şekilde uygulanırsa, bir işaretçi karakterle aynı alana sığar ve dolayısıyla ayrıca 1
Kaz Ejderha


1
@KazDragon 16 bitlik kelimelerle ve bayt adreslemesiz bir makine yapıyorum (ertelemediğinde çok yavaş). Yine de C çalışamaz rağmen.
user253751

17

Teknik olarak konuşursak, C standardı sadece sizeof (char) == 1 olduğunu garanti eder ve gerisi uygulamaya bağlıdır. Ancak modern x86 mimarilerinde (örn. Intel / AMD yongaları) oldukça tahmin edilebilir.

Muhtemelen 16 bit, 32 bit, 64 bit vb. Olarak tanımlanan işlemcileri duymuşsunuzdur. Bu genellikle işlemcinin tamsayılar için N-bitleri kullandığı anlamına gelir. İşaretçiler bellek adreslerini depoladığından ve bellek adresleri tamsayı olduğundan, işaretçiler için kaç bit kullanılacağını etkin bir şekilde anlatır. sizeof genellikle bayt cinsinden ölçülür, bu nedenle 32 bit işlemciler için derlenen kod, işaretçi boyutunu 4 olarak bildirir (bayt başına 32 bit / 8 bit) ve 64 bit işlemciler için kod, işaretçi boyutunu 8 olarak bildirir (Bayt başına 64 bit / 8 bit). Bu, 32 bit işlemciler için 4GB RAM sınırlamasından kaynaklanır - her bellek adresi bir bayta karşılık geliyorsa, daha fazla belleği adreslemek için 32 bitten daha büyük tamsayılara ihtiyacınız vardır.


"Muhtemelen 16-bit, 32-bit, 64-bit, vb. Olarak tanımlanan işlemcileri duymuşsunuzdur. Bu genellikle işlemcinin tamsayılar için N-bitleri kullandığı anlamına gelir." 64-bit makinem var ama sizeof (int) 4 bayt. İfadeniz doğruysa, bu nasıl mümkün olabilir ?!
Sangeeth Saravanaraj

6
@SangeethSaravanaraj: 32 bit kodla geriye dönük uyumluluk için int'in 4 bayt olmaya devam etmesine karar verdiler ve 'uzun' belirterek 8 bayt tipini kullanmayı seçmenizi istediler. long, aslında x86-64'teki yerel kelime boyutudur. Bunu görmenin bir yolu, tipik olarak derleyicilerin sözcükleri hizalamak için yapılarınızı doldurmasıdır (ancak kelime boyutu ve hizalamanın ilişkisiz olduğu mimariler olabilir), bu nedenle içinde int (32 bit) bulunan bir yapı yaparsanız, ve sizeof () 'i çağırın, eğer 8'i geri alırsanız, bunların 64 bit sözcük boyutuna doldurduğunu bilirsiniz.
Joseph Garvin

@SangeethSaravanaraj: Teorik olarak CPU'nun yerel kelime boyutunun ve derleyicinin 'int' e karar vermesinin keyfi farklı olabileceğini, sadece x86-64 gelmeden önce 'int' kelimesinin yerel kelime boyutu olması için bir kural olduğunu unutmayın. geriye uyumluluk rahatlamak için uzun.
Joseph Garvin

Açıklama için teşekkürler! :)
Sangeeth Saravanaraj

7

İşaretçinin boyutu temel olarak uygulandığı sistemin mimarisine bağlıdır. Örneğin, 32 bitlik bir işaretçinin boyutu 64 bitlik makinelerde 4 bayt (32 bit) ve 8 bayttır (64 bit). Bir makinedeki bit tipleri, sahip olabileceği bellek adresinden başka bir şey değildir. 32 bit makinelerde 2^32adres alanı, 64 bit makinelerde adres alanı olabilir 2^64. Dolayısıyla, bir işaretçi (bir bellek konumuna işaret eden değişken 2^32 for 32 bit and 2^64 for 64 bit), makinelerin sahip olduğu bellek adreslerinden ( ) herhangi birini gösterebilmelidir .

Bu nedenle, işaretçinin boyutunun 32 bit makinede 4 bayt ve 64 bit makinede 8 bayt olduğunu görüyoruz.


6

16/32/64 bit farklarına ek olarak, daha tuhaf şeyler bile meydana gelebilir.

Sizeof (int *) 'nin bir değer, muhtemelen 4 olacağı, ancak sizeof (char *)' nın daha büyük olduğu makineler olmuştur. Bayt yerine kelimeleri doğal olarak adresleyen makinelerin, C / C ++ standardını düzgün bir şekilde uygulamak için kelimenin gerçekten hangi kısmını istediğinizi belirtmek için karakter işaretleyicilerini "artırması" gerekir.

Donanım tasarımcıları bayt adreslenebilirliğinin değerini öğrendiklerinden bu durum artık çok sıra dışı.


4
T90 gibi Cray vektör makineleri için C derleyicisi benzer bir şey yapar. Donanım adresleri 8 bayttır ve 8 baytlık kelimeleri işaret eder. void*ve char*yazılımda işlenir ve sözcük içinde 3 bitlik bir ofset ile zenginleştirilir - ancak aslında 64 bitlik bir adres alanı olmadığından, ofset 64 bitin yüksek dereceli 3 bitinde depolanır sözcüğü. Yani char*ve int*aynı boyuttadır, ancak farklı dahili temsillere sahiptir - ve işaretçilerin "gerçekten" olduğunu varsayan kod, sadece tamsayılar başarısız olabilir.
Keith Thompson

5

Çoğu düşük profilli mikrodenetleyicide 8 bit ve 16 bit işaretçiler kullanılır. Bu, her çamaşır makinesi, mikro buzdolabı, eski TV'ler ve hatta arabalar anlamına gelir.

Bunların gerçek dünya programlama ile ilgisi olmadığını söyleyebilirsiniz. Ama işte gerçek bir dünya örneği: 1-2 baytlık koçlu Arduino (çipe bağlı olarak) 2 bayt işaretçisi.

Son zamanlarda, ucuz, herkes için erişilebilir ve değer kodlamaya değer.


4

İnsanların 64 bit (ya da her ne olursa olsun) sistemleri hakkında söylediklerine ek olarak, işaretçi-nesne dışında başka işaretçiler de vardır.

Bir işaretçi-üye, derleyiciniz tarafından nasıl uygulandıklarına bağlı olarak hemen hemen her boyutta olabilir: mutlaka aynı boyutta bile olmayabilirler. Bir POD sınıfının işaretçi-üyesini ve ardından birden çok tabanlı bir sınıfın temel sınıflarından birinden devralınan bir işaretçi-üyeyi deneyin. Ne komik.


3

Hatırladığım kadarıyla, bir bellek adresinin boyutuna dayanıyor. 32 bit adres şemasına sahip bir sistemde, sizeof 4 döndürür, çünkü bu 4 bayttır.


4
Böyle bir gereklilik yoktur. Hatta sizeof (unsigned int) == sizeof (işaretli int) şartı aranmaz. Bir int işaretçisinin boyutu her zaman tanım gereği sizeof (int *), bir char sizeof (char *) vb. Olacaktır. Başka bir varsayımlara güvenmek taşınabilirlik için kötü bir fikirdir.
Mihai Limbășan

Ah, şimdi görüyorum. Bilgi için teşekkürler.
Will Mc

1
CHAR_BIT 16 ise hala 2 döndürülebilir. Sekizlik değil, karakter sayısı sizeof () sayılır.
MSalters

5
@Mihai: C ++ 'da sizeof (unsigned int) == sizeof (signed int), bu gereksinim 3.9.1 / 3'te bulunur. "Tip tamsayıdır imzalanmış standart her biri için, karşılık gelen bir (fakat farklı), standart, işaretsiz tamsayı tipi vardır: unsigned char, unsigned short int, unsigned int, unsigned long int, ve unsigned long long int, bunların her biri aynı miktarda depolama alanını kaplar ve tip tamsayıdır imza karşılık gelen aynı hizalama gereksinimleri vardır "
Ben Voigt

3

Genel olarak, farklı platformlarda derlediğinizde sizeof (hemen hemen her şey) değişecektir. 32 bit platformda, işaretçiler her zaman aynı boyuttadır. Diğer platformlarda (64 bit açık bir örnektir) bu değişebilir.


3

Hayır, işaretçinin boyutu mimariye bağlı olarak değişebilir. Çok sayıda istisna vardır.


3

İşaretçi ve int boyutu, Windows 32 bit makinedeki Turbo C derleyicisinde 2 bayttır.

Böylece işaretçinin boyutu derleyiciye özgüdür. Ancak genellikle derleyicilerin çoğu 32 bitte 4 bayt işaretçi değişkenini ve 64 bit makinede 8 bayt işaretçi değişkenini desteklemek için uygulanır).

Dolayısıyla, işaretçinin boyutu tüm makinelerde aynı değildir.


2

İşaretçinizin boyutunun 4 bayt olmasının nedeni, 32 bit mimari için derlemenizdir. FryGuy'un işaret ettiği gibi, 64 bit bir mimaride 8 görürsünüz.


2

In Win64 (Cygwin GCC 5.4) , en örnek aşağıda görelim:

İlk olarak, aşağıdaki yapıyı test edin:

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

struct test_struc{
    char a, b;
};

Test kodu aşağıdadır:

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;    

Çıktı aşağıdadır:

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

Bunu 64 bit sizeof(pointer)olarak görebilirsiniz 8.


1

İşaretçi, bir adres için yalnızca bir kaptır. 32 bitlik bir makinede, adres aralığınız 32 bittir, bu nedenle bir işaretçi her zaman 4 bayt olacaktır. 64 bitlik bir makinede 64 bitlik bir adres aralığınız vardı, bir işaretçi 8 bayt olacaktır.


1
32 bit baytlı 32 bit bir makinede, sizeof (char *) 1 olabilir.
Robert Gamble

"... 32 bit bayt ile". Böyle şeylerin var olduğunu bilmiyordum ... süslü.
Ed S.

1
32 bitlik bir ördek üzerinde, sizeof (char *) PI döndürür
Adriano Varoli Piazza

0

Tamlık ve tarihi ilgi için, 64bit dünyasında, esas olarak Unix tipi sistemler ve Windows arasında LLP64 ve LP64 adlı uzun ve uzun uzun tiplerin boyutları üzerinde farklı platform sözleşmeleri vardı. ILP64 adında eski bir standart da int = 64-bit genişliğinde olmuştur.

Microsoft, LLP64'ü uzun uzunluk = 64 bit genişlikte, ancak uzun süre 32'de kaldı, daha kolay taşıma için.

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

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.