size_t ve uintptr_t karşılaştırması


246

C standardı size_t, herhangi bir dizi dizinini tutabilen bir tür olduğunu garanti eder . Bu, mantıksal olarak size_therhangi bir işaretçi türünü tutabilmesi gerektiği anlamına gelir . Google'da bulduğum bazı sitelerde bunun yasal olduğunu ve / veya her zaman çalışması gerektiğini okudum:

void *v = malloc(10);
size_t s = (size_t) v;

Böylece C99'da, standart , işaretçileri tutabilen garanti edilen imzalı ve imzasız tipler intptr_tve uintptr_ttürlerini tanıttı :

uintptr_t p = (size_t) v;

Peki size_tve ile arasındaki fark uintptr_tnedir? Her ikisi de imzasızdır ve her ikisi de herhangi bir işaretçi türünü tutabilmelidir, bu nedenle işlevsel olarak aynı görünürler. Netlik dışında a yerine kullanmak için gerçek bir zorlayıcı neden var mı uintptr_t(ya da daha iyisi, a void *) size_t? Alanın sadece dahili işlevlerle ele alınacağı opak bir yapıda, bunu yapmamak için herhangi bir neden var mı?

Aynı şekilde, ptrdiff_tişaretçi farklılıklarını tutabilen ve bu nedenle herhangi bir işaretçiyi en fazla tutabilen imzalı bir tür olmuştur, peki nasıl farklıdır intptr_t?

Bu türlerin tümü temelde aynı işlevin önemsiz derecede farklı sürümlerini sunmuyor mu? Değilse, neden? Biriyle diğerini yapamayacağım ne yapamam? Öyleyse, C99 neden dile temel olarak iki gereksiz tür ekledi?

Onlar mevcut sorun için geçerli değil, ama onlar "doğru" cevap merkezi olacak gizlice bir şüphe var gibi, onları belirtmekten çekinmeyin, işlev işaretçileri göz ardı etmeye hazırım.

Yanıtlar:


236

size_therhangi bir dizi dizinini tutabilen bir türdür. Bu, mantıksal olarak size_t öğesinin herhangi bir işaretçi türünü tutabilmesi gerektiği anlamına gelir

Şart değil! Örneğin, bölümlere ayrılmış 16 bit mimarilerin günlerine geri dönün: bir dizi tek bir bölümle sınırlı olabilir (bu nedenle 16 bit size_tolur) AMA birden çok bölümünüz olabilir (bu nedenle 32 bitlik bir intptr_ttür gerekli olacaktır segment ve içindeki ofset). Bu şeylerin tekdüze adreslenmemiş bölümlenmemiş mimarilerin garip geldiğini biliyorum, ancak standart "2009'da normal olandan" daha geniş bir çeşitlilik sağlaması GEREKİR!


6
Bu, aynı sonuca atladı sayısız kişiyle birlikte arasındaki farkı açıklar size_tve uintptr_tama ne hakkında ptrdiff_tve intptr_t- Bunlardan ikisi hemen hemen her platformda aynı aralıktaki değerler depolamak mümkün olmaz? Özellikle ptrdiff_timzalı işaretçi boyutunda bir tamsayı türünün amacına hizmet ediyorsa, neden hem işaretli hem de işaretsiz işaretçi boyutu tamsayı türlerine sahip olmalısınız.
Chris Lutz

8
Anahtar kelime " hemen hemen her platformda", @Chris. Bir uygulama, işaretçileri 0xf000-0xffff aralığıyla sınırlamakta serbesttir - bu 16bit intptr_t, ancak yalnızca 12/13-bit ptrdiff_t gerektirir.
paxdiablo

29
@Chris, sadece aynı dizideki işaretçiler için farklarını almak iyi tanımlanmış. Yani, tam olarak aynı bölümlenmiş 16 bit mimarilerde (dizi tek bir segment içinde yaşamalıdır, ancak iki farklı dizi farklı segmentlerde olabilir) işaretçiler 4 bayt olmalı, ancak işaretçi farklılıkları 2 bayt olabilir!
Alex Martelli

6
@AlexMartelli: İşaretçi farklılıklarının pozitif veya negatif olabilmesi dışında. Standart gerektirirsize_t en az 16 bit, ancak ptrdiff_ten az 17 bit olmalıdır (pratikte muhtemelen en az 32 bit olacaktır).
Keith Thompson

3
Nevermind segmentli mimariler, x86-64 gibi modern bir mimariye ne dersiniz? Bu mimarinin ilk uygulamaları size yalnızca 48 bit adreslenebilir alan sağlar, ancak işaretçilerin kendileri 64 bit veri türüdür. Makul bir şekilde ele alabileceğiniz en büyük bitişik bellek bloğu 48 bit olacaktır, bu yüzden SIZE_MAX2 ** 64 olmamasını hayal etmeliyim. Bu düz adresleme kullanıyor, dikkat edin; SIZE_MAXveri işaretçisinin aralığı ile aralığı arasında uyumsuzluk olması için segmentasyon gerekmez .
Andon M. Coleman

89

İfadenizle ilgili olarak:

"C standardı size_t, herhangi bir dizi dizinini tutabilen bir tür olduğunu garanti eder . Bu, mantıksal olarak size_therhangi bir işaretçi türünü tutabilmesi gerektiği anlamına gelir ."

Bu aslında bir yanlıştır (yanlış akıl yürütmeden kaynaklanan bir yanlış anlama) (a) . Sen olabilir düşünmek ikincisi eski izler ama aslında böyle değil.

İşaretçiler ve dizi dizinleri aynı şey değildir . Dizileri 65536 öğeyle sınırlayan, ancak işaretçilerin herhangi bir değeri büyük bir 128 bit adres alanına yönlendirmesine izin veren uygun bir uygulamayı öngörmek oldukça mantıklıdır.

C99, bir size_tdeğişkenin üst sınırının tarafından tanımlandığını SIZE_MAXve bunun 65535 kadar düşük olabileceğini belirtir (bakınız C99 TR3, 7.18.3, C11'de değişmedi). Modern sistemlerde bu aralıkla sınırlı olsaydı, işaretçiler oldukça sınırlı olurdu.

Uygulamada, muhtemelen varsayımınızın geçerli olduğunu göreceksiniz, ancak bunun nedeni standardın bunu garanti etmesi değildir. Çünkü aslında bunu garanti etmez .


(A) Bu arada bir tür kişisel saldırı değildir , sadece ifadelerinizin eleştirel düşünme bağlamında neden hatalı olduğunu belirtir. Örneğin, aşağıdaki mantık da geçersizdir:

Tüm yavrular sevimli. Bu şey çok tatlı. Bu nedenle bu şey bir köpek yavrusu olmalı.

Burada zekâ ya da başka bir şekilde köpek yavrusu, hiçbir şey ifade etmiyorum, iki gerçek sonuca yol açmaz, çünkü ilk iki cümle yavru olmayan sevimli şeylerin varlığına izin verir .

Bu, ilk ifadenize benzer, ikinciyi zorunlu kılmak zorunda değildir.


Aksine Alex Martelli için yapılan yorumlar söylediklerini tekrar yazın daha, sadece açıklama için teşekkür ama benim sorunun ikinci yarısını (yinelemek edeceğiz ptrdiff_tvs intptr_tparçası).
Chris Lutz

5
@Ivan, çoğu iletişimde olduğu gibi, bazı temel öğelerin ortak bir anlayışı olmalıdır. Eğer bu cevabı "alaycı eğlence" olarak görürseniz, bunun niyetimin yanlış anlaşıldığını garanti ederim. 'Mantıksal yanlışlık' yorumuma (başka bir olasılık göremiyorum) atıfta bulunduğunuzu varsayarsak, bu gerçek bir ifade anlamına geliyordu, OP pahasına yapılan bir açıklama değil. Yanlış anlama olasılığını en aza indirmek için somut bir iyileştirme önermek isterseniz (sadece genel bir şikayet değil), memnuniyetle düşünürüm.
paxdiablo

1
@ivan_pozdeev - bu iğrenç ve sert bir çift düzenleme ve paxdiablo'nun kimseye "eğlenmek" olduğuna dair hiçbir kanıt göremiyorum. Ben OP olsaydım, bu hemen geri rulo ....
eski nihilo

1
@Ivan, önerdiğiniz düzenlemelerden gerçekten memnun değildi, geri döndü ve istenmeyen suçları da gidermeye çalıştı. Sunabileceğiniz başka değişiklikleriniz varsa, tartışabilmemiz için bir sohbet başlatmanızı öneririm.
paxdiablo

1
@paxdiablo tamam, sanırım "bu aslında bir yanılgıdır" daha az koruyucu.
ivan_pozdeev

36

Segment kısıtlamaları, egzotik mimariler vb. İle akıl yürütme ile ilgili diğer tüm cevapların kendileri için bırakmasına izin vereceğim.

İsimlerdeki basit fark değil , uygun olanı doğru şey için kullanmak için yeterli değil mi?

Bir boyut saklıyorsanız, kullanın size_t. Bir işaretçi saklıyorsanız,intptr_t . Kodunuzu okuyan bir kişi anında "aha, bunun muhtemelen bayt cinsinden bir boyutudur" ve "oh, işte bir nedenle tamsayı olarak saklanan bir işaretçi değerinin" olduğunu bilir.

Aksi takdirde, her şey için kullanabilirsiniz unsigned long(veya bu modern zamanlarda unsigned long long). Boyut her şey değildir, tür adları programı tanımlamaya yardımcı olduğu için faydalı olan anlam taşır.


Katılıyorum, ama bir size_talanda bir işaretçi türü depolamayı içeren bir kesmek / hile (tabii ki açıkça belgelemek istiyorum) bir şey düşünüyordum .
Chris Lutz

@MarkAdler Standard, işaretçilerin tamsayı olarak temsil edilebilmesini gerektirmez: Herhangi bir işaretçi türü bir tamsayı tipine dönüştürülebilir. Daha önce belirtilenler dışında, sonuç uygulama tanımlıdır. Sonuç tamsayı türünde gösterilemiyorsa, davranış tanımsızdır. Sonuç, herhangi bir tamsayı türünün değer aralığında olmamalıdır. Bu nedenle, sadece void*, intptr_tve uintptr_tveri için bir işaretçi temsil edebilmek için garanti edilir.
Andrew Svietlichnyy

12

En büyük dizinin boyutunun bir işaretçiden daha küçük olması mümkündür. Bölümlenmiş mimarileri düşünün - işaretçiler 32 bit olabilir, ancak tek bir bölüm yalnızca 64KB'ye (örneğin eski gerçek mod 8086 mimarisi) hitap edebilir.

Bunlar artık masaüstü makinelerde yaygın olarak kullanılmasa da, C standardı küçük, özel mimarileri bile desteklemeyi amaçlamaktadır. Örneğin 8 veya 16 bit CPU'larla geliştirilmekte olan gömülü sistemler var.


Ancak, dizileri tıpkı diziler gibi dizine ekleyebilirsiniz, bu yüzden bunu size_tda ele alabilmelisiniz? Yoksa uzaktaki bir segmentteki dinamik diziler yine de kendi segmentlerindeki endeksleme ile mi sınırlı kalacak?
Chris Lutz

İndeksleme işaretçileri yalnızca işaret ettikleri dizinin boyutuyla teknik olarak desteklenir - bu nedenle bir dizi 64KB boyutuyla sınırlıysa, işaretçi aritmetiğinin desteklemesi gereken tek şey budur. Bununla birlikte, MS-DOS derleyicileri, uzak işaretçilerin (32 bit bölümlenmiş işaretçiler) tüm belleğe tek bir dizi olarak hitap edebilmeleri için manipüle edildiği 'büyük' ​​bir bellek modelini destekledi - ancak sahne arkasındaki işaretçilere yapılan aritemik oldukça çirkin - ofset 16 (veya bir şey) değerini aştığında, ofset 0'a geri sarıldı ve segment kısmı arttı.
Michael Burr

7
Ücretsiz olabilmemiz için ölen MS-DOS programcıları için en.wikipedia.org/wiki/C_memory_model#Memory_segmentation okuyun ve ağlayın.
Justicle

Daha da kötüsü, stdlib işlevi BÜYÜK anahtar kelimeyle ilgilenmedi. 16 bit her MS-Cı strda fonksiyonları ve Borland memişlevleri ( memset, memcpy, memmove). Bu, ofset taştığında belleğin bir kısmının üzerine yazabileceğiniz anlamına geliyordu, bu gömülü platformumuzda hata ayıklamak eğlenceliydi.
Patrick Schlüter

@Justicle: 8086 segmentli mimari C'de iyi desteklenmiyor, ancak 1MB adres alanının yeterli olduğu ancak 64K'lık bir mimarinin olmayacağı durumlarda daha verimli olan başka bir mimari bilmiyorum. Bazı modern JVM'ler, 32GB adres alanında nesne tabanı adresleri oluşturmak için 3 bitlik sol 32 bitlik nesne referanslarını kullanarak x86 gerçek modu gibi adresleme kullanır.
supercat

5

Daha iyi kod niyetlerini iletir hayal ediyorum (ve bu tüm tip isimleri için geçerli).

Örneğin, olsa bile unsigned shortve wchar_tkullanarak Windows (Bence) aynı boyutta wchar_tyerine unsigned shortgösteriler Bunun yerine sadece bazı keyfi sayısından daha geniş bir karakter saklamak için kullanacağı niyeti.


Sistemimde, - Ama burada bir fark var wchar_tbir çok daha büyük olduğunu unsigned shortarasındaki taşınabilirlik endişeleri ise hatalı olabilir ve ciddi (ve modern) taşınabilirliği endişe yaratacak diğeri için birini kullanarak bu yüzden size_tve uintptr_ttoprakların uzak içinde yalan görünüyor 1980-bir şey (orada tarihte karanlıkta rastgele bıçak)
Chris Lutz

Touché! Ama sonra tekrar, size_tve uintptr_thala isimlerinde kullanımları ima etti.
dreamlax

Onlar yaparlar ve bunun için netliğin ötesinde bir motivasyon olup olmadığını bilmek istedim. Ve ortaya çıkıyor.
Chris Lutz

3

Hem geriye hem de ileriye bakarak ve çeşitli oddball mimarilerinin manzaraya dağıldığını hatırlatarak, mevcut tüm sistemleri sarmaya ve gelecekteki tüm sistemleri sağlamaya çalıştıklarından eminim.

Öyleyse, işlerin ortaya çıkış şekli, şu ana kadar pek fazla türe ihtiyacımız olmadı.

Ancak, oldukça yaygın bir paradigma olan LP64'te bile, sistem çağrısı arabirimi için size_t ve ssize_t'e ihtiyacımız vardı. Tam 64-bit tip kullanmanın pahalı olduğu ve 4GB'den büyük ancak hala 64-bit işaretçileri olan I / O op'larında punt yapmak isteyebilecekleri daha kısıtlı bir miras veya gelecek sistemi düşünülebilir.

Bence merak etmelisiniz: neyin geliştirilebileceğini, gelecekte nelerin gelebileceğini. (Belki 128 bit dağıtılmış sistem internet çapında işaretçiler, ancak bir sistem çağrısında 64 bit veya belki de bir "eski" 32 bit sınırı. :-) Eski sistemlerin yeni C derleyicileri alabileceği görüntü. .

Ayrıca, etrafta neler olduğuna bakın. Milyonlarca 286 gerçek mod bellek modelinin yanı sıra, CDC 60 bit kelime / 18 bit işaretçi ana karelerine ne dersiniz? Cray serisine ne dersin? Normal ILP64, LP64, LLP64'ü boş verin. (Microsoft'un her zaman LLP64 ile iddialı olduğunu düşündüm, P64 olmalıydı.) Kesinlikle tüm üsleri örtmeye çalışan bir komite hayal edebiliyorum ...


-9
int main(){
  int a[4]={0,1,5,3};
  int a0 = a[0];
  int a1 = *(a+1);
  int a2 = *(2+a);
  int a3 = 3[a];
  return a2;
}

İntptr_t ifadesinin her zaman size_t yerine koyması gerektiğini ve bunun tersini yapmak anlamına gelir.


10
Bütün bunlar, C'nin belirli bir sözdizimi tuhaflığıdır. Dizi indeksleme, x [y] 'nin * (x + y)' e eşdeğer olması ve bir + 3 ve 3 + a'nın tür ve değer bakımından aynı olması nedeniyle tanımlanır. 3 [a] veya bir [3] kullanın.
Fred Nurk
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.