Düz bellek modeli (temelde her şey) ile yapılan uygulamalarda, uintptr_t
Just Work'e yayınlanacak.
(Ancak, işaretçiler karşılaştırmaları 64 bit x 86'da imzalanmalı veya imzalanmamalı mı? C'de UB olan nesnelerin dışında işaretçiler oluşturma sorunları da dahil olmak üzere, işaretleyicilere imzalı olarak davranılıp davranılmayacağını tartışmak için bkz.)
Ancak düz olmayan bellek modelleri olan sistemler mevcuttur ve bunlar hakkında düşünmek, C ++ <
vs. için farklı özelliklere sahip olan mevcut durumu açıklamaya yardımcı olabilir std::less
.
Alanına bir kısmı <
C UB (ya da C ++ olarak gözden geçirilmesinde, en azından belirtilmemiş) olan ayrı nesneler için işaretçiler düz olmayan bellek modelleri dahil olmak üzere garip makineler için sağlamaktır.
İyi bilinen bir örnek, işaretçilerin segmentli olduğu x86-16 gerçek modudur: ofset, üzerinden 20 bit doğrusal adres oluşturur (segment << 4) + offset
. Aynı doğrusal adres, birden çok farklı seg: off kombinasyonu ile temsil edilebilir.
std::less
Garip ISA'larda işaretçiler üzerinde C ++ pahalı olabilir , örneğin bir segmenti "normalleştirin": ofset <= 15 olması için x86-16'da ofset. Ancak, bunu uygulamanın taşınabilir bir yolu yoktur . Bir uintptr_t
(veya bir işaretçi nesnesinin nesne gösterimi) normalleştirmek için gereken manipülasyon uygulamaya özgüdür.
Ancak C ++ ' std::less
nin pahalı olması gereken sistemlerde bile , <
olması gerekmez. Örneğin, bir nesnenin bir segment içine sığdığı bir "büyük" bellek modeli varsayarsak <
, ofset kısmını karşılaştırabilir ve hatta segment kısmı ile uğraşmayabilir. (Aynı nesnenin içindeki işaretçiler aynı segmente sahiptir ve aksi takdirde C'deki UB'dir. C ++ 17 yalnızca "belirtilmemiş" olarak değiştirilmiştir, bu da normalleştirmeyi atlamaya ve sadece ofsetleri karşılaştırmaya izin verebilir.) nesnenin her zaman aynı seg
değeri kullanır, asla normalleştirmez. Bu, bir ABI'nin "büyük" bellek modelinin aksine "büyük" olmasını gerektirmesini beklersiniz. ( Yorumlardaki tartışmaya bakın ).
(Böyle bir bellek modeli, maksimum 64kiB nesne boyutuna sahip olabilir, ancak bu kadar çok sayıda maksimum nesne için yer olan çok daha büyük bir maksimum toplam adres alanı olabilir. ISO C, uygulamaların, nesne boyutunda, maksimum değer (imzasız) size_t
, SIZE_MAX
örneğin düz bellek model sistemlerinde bile, GNU C maksimum nesne boyutunu sınırlar, PTRDIFF_MAX
böylece boyut hesaplaması imzalı taşmayı göz ardı edebilir.) Bu yanıta ve tartışmaya yorumlarda bakın.
Bir segmentten daha büyük nesnelere izin vermek istiyorsanız, p++
bir dizi boyunca döngü gerçekleştirirken veya indeksleme / işaretçi aritmetiği yaparken işaretçinin ofset kısmının taşması konusunda endişelenmesi gereken "büyük" bir bellek modeline ihtiyacınız vardır . Bu, her yerde daha yavaş kodlara yol açar, ancak muhtemelen p < q
farklı nesnelere işaretçiler için işe yarayacağı anlamına gelir , çünkü "büyük" bir bellek modelini hedefleyen bir uygulama normalde tüm işaretçileri her zaman normalleştirmeyi seçer. Bkz uzak ve büyük işaretçileri, yakın nelerdir? - x86 gerçek modu için bazı gerçek C derleyicileri, aksi belirtilmedikçe tüm işaretçilerin varsayılan olarak "dev" olarak ayarlandığı "dev" model için derleme seçeneğine sahipti.
x86 gerçek mod segmentasyonu mümkün olan tek düz bellek modeli değildir, C / C ++ uygulamaları tarafından nasıl ele alındığını göstermek için sadece yararlı bir somut örnektir. Gerçek hayatta uygulamalar, ISO C'yi işaretçiler far
ve near
işaretçi kavramıyla genişleterek , programcıların bazı ortak veri segmentlerine göre sadece 16 bitlik ofset parçasını depolamak / geçirmek için ne zaman uzaklaşabileceklerini seçmelerine olanak tanır.
Ancak saf bir ISO C uygulaması, küçük bir bellek modeli (16 bit işaretçilerle aynı 64kiB'deki kod hariç her şey) veya tüm işaretçiler 32 bit olacak şekilde büyük veya çok büyük arasında seçim yapmak zorunda kalacaktır. Bazı döngüler, yalnızca ofset kısmını artırarak optimize edebilir, ancak işaretçi nesneleri daha küçük olacak şekilde optimize edilemez.
Herhangi bir uygulama için sihirli manipülasyonun ne olduğunu biliyorsan, onu saf C'de uygulayabilirsin . Sorun, farklı sistemlerin farklı adresleme kullanması ve ayrıntıların herhangi bir taşınabilir makro tarafından parametreleştirilmemesidir.
Ya da olmayabilir: özel bir segment tablosundan ya da adresin segment bölümünün bir dizin olduğu bir gerçek dizin yerine gerçek mod yerine x86 korumalı mod gibi bir şeyin aranmasını içerebilir. Korumalı modda kısmen çakışan segmentler oluşturabilirsiniz ve adreslerin segment seçici bölümleri karşılık gelen segment temel adresleriyle aynı sırada sipariş edilmeyebilir. GDT ve / veya LDT, işleminizdeki okunabilir sayfalarla eşlenmemişse, x86 korumalı modda bir seg: off işaretçisinden doğrusal adres almak bir sistem çağrısı içerebilir.
(Tabii ki x86 için ana işletim sistemleri düz bir bellek modeli kullanır, böylece segment tabanı her zaman 0 olur (iş parçacığı fs
veya yerel segment kullanımı hariç gs
) ve işaretçi olarak yalnızca 32 bit veya 64 bit "ofset" kısmı kullanılır .)
Çeşitli belirli platformlar için manuel olarak kod ekleyebilirsiniz, örneğin varsayılan olarak düz olduğunu varsayalım veya #ifdef
x86 gerçek modunu algılayacak ve uintptr_t
16 bit yarıya bölünecek bir şey seg -= off>>4; off &= 0xf;
daha sonra 32 bit bir sayıya geri birleştirebilirsiniz.