En katı anlamda, bu Tanımsız Davranış değil, uygulama tanımlıdır. Dolayısıyla, anaakım olmayan mimarileri desteklemeyi planlıyorsanız, tavsiye edilemez olsa da , muhtemelen bunu yapabilirsiniz.
Interjay tarafından verilen standart alıntı, UB'yi gösteren iyi bir tekliftir, ancak işaretçi-işaretçi aritmetiği ile ilgilentiği için (bence, biri açıkça UB'dir, diğeri değil). Doğrudan söz konusu operasyonla ilgili bir paragraf var:
[expr.post.incr] / [expr.pre.incr] İşlenen
[...] veya tamamen tanımlanmış bir nesne türüne işaretçi olacaktır.
Bir dakika, tamamen tanımlanmış bir nesne türü mü? Bu kadar? Gerçekten, yazın ? Yani bir nesneye ihtiyacınız yok mu?
İçerideki bir şeyin o kadar iyi tanımlanamayacağına dair bir ipucu bulmak gerçekten biraz okuma gerektirir. Çünkü şimdiye kadar, bunu yapmanıza izin verilmiş gibi görünüyor, kısıtlama yok.
[basic.compound] 3
ne tür bir işaretçi olabileceğine dair bir açıklama yapar ve diğer üçünden hiçbiri olmadığında, işleminizin sonucu açıkça 3.4'ün altına düşer: geçersiz işaretçi .
Ancak geçersiz bir işaretçiye sahip olmanıza izin verilmediğini söylemez. Aksine, işaretçilerin düzenli olarak geçersiz hale geldiği bazı çok yaygın, normal koşulları (örneğin depolama süresi sonu) listeler. Görünüşe göre bu izin verilebilir bir şey. Ve gerçekten:
[basic.stc] 4
Geçersiz bir işaretçi değeri aracılığıyla aktarma ve geçersiz bir işaretçi değerini bir ayırma işlevine aktarma tanımsız davranışa sahiptir. Geçersiz bir işaretçi değerinin başka herhangi bir şekilde kullanılması, uygulama tanımlı davranışa sahiptir.
Orada bir "başka" yapıyoruz, bu yüzden Tanımlanmamış Davranış değil, uygulama tanımlı, dolayısıyla genel olarak izin verilebilir (uygulama açıkça farklı bir şey söylemedikçe).
Ne yazık ki, bu hikayenin sonu değil. Net sonuç buradan daha fazla değişmese de, daha kafa karıştırıcı hale gelir, "işaretçi" için daha uzun süre arama yaparsınız:
[basic.compound]
Nesne işaretçisi türünün geçerli bir değeri, bellekteki bir baytın adresini veya boş bir işaretçiyi temsil eder . T tipi bir nesnenin A [...] adresinde bulunması durumunda, değerin nasıl elde edildiğine bakılmaksızın o nesneyi işaret ettiği söylenir .
[Not: Örneğin, bir dizinin sonunu geçen adresin, dizinin o adreste bulunabilecek öğe türünün alakasız bir nesnesine işaret ettiği düşünülür. [...]].
Oku: Tamam, kimin umurunda! Bir işaretçi bellekte bir yere işaret ettiği sürece iyiyim?
[basic.stc.dynamic.safety] Bir işaretçi değeri güvenli bir şekilde oluşturulmuş bir işaretçi [blah blah]
Şu şekilde okuyun: Tamam, güvenli bir şekilde türetilmiş, her neyse. Bunun ne olduğunu açıklamıyor, aslında buna ihtiyacım olduğunu da söylemiyor. Güvenli bir şekilde türetilmiş-halt. Görünüşe göre hala güvenli bir şekilde türetilmemiş işaretçiler olabilir. Sanırım onları silme işleminin böyle iyi bir fikir olmayacağını, ancak onlara sahip olmasına izin verilebilir. Aksini söylemiyor.
Bir uygulama işaretçi güvenliğini gevşetmiş olabilir, bu durumda bir işaretçi değerinin geçerliliği, güvenli bir şekilde türetilen işaretçi değeri olup olmadığına bağlı değildir.
Oh, bu yüzden önemli değil, sadece düşündüğüm şey. Ama bekleyin ... "olmayabilir"? Bu da olabilir . Nasıl bilebilirim?
Alternatif olarak, bir uygulama katı işaretçi güvenliğine sahip olabilir; bu durumda, güvenli bir şekilde türetilmiş işaretçi değeri olmayan bir işaretçi değeri, başvurulan tam nesne dinamik depolama süresine sahip olmadığı ve daha önce erişilebilir olduğu bildirilmedikçe geçersiz bir işaretçi değeridir.
Bekle, bu yüzden declare_reachable()
her işaretçiyi aramam gerekiyor mu? Nasıl bilebilirim?
Şimdi, intptr_t
güvenli bir şekilde türetilmiş işaretçinin tamsayı temsilini vererek, iyi tanımlanmış bir biçime dönüştürebilirsiniz . Tabii ki, bir tamsayı olmak, onu istediğiniz gibi artırmak tamamen meşru ve iyi tanımlanmıştır.
Ve evet, intptr_t
arka kısmı iyi tanımlanmış bir işaretçiye dönüştürebilirsiniz . Sadece orijinal değer değil, artık güvenli bir şekilde türetilmiş bir işaretçiniz olduğu garanti edilmez. Yine de, genel olarak, standardın mektubuna, uygulama tanımlı olmakla birlikte, bu% 100 meşru bir şeydir:
[expr.reinterpret.cast] 5
Bir integral türü veya numaralandırma türü değeri açıkça bir işaretçiye dönüştürülebilir. Bir işaretçi yeterli büyüklükte [...] tamsayıya ve aynı işaretçi türü [...] orijinal değerine dönüştürülür; işaretçiler ve tamsayılar arasındaki eşlemeler aksi takdirde uygulama tarafından tanımlanır.
Yakalayış
İşaretçiler sıradan tamsayılardır, yalnızca işaretçiler olarak kullanılırsınız. Ah, bu doğru olsaydı!
Ne yazık ki, bunun hiç de doğru olmadığı mimariler var ve sadece geçersiz bir işaretçi oluşturmak (kaydı silme değil, sadece işaretçi kaydında bulundurmak) bir tuzağa neden olacak.
Yani bu, "tanımlanmış uygulamanın" temelidir. Bu ve bir işaretçiyi istediğiniz zaman artırmanın, tabii ki istediğiniz gibi , standartın ele almak istemediği taşmaya neden olabileceği gerçeği . Uygulama adres alanının sonu taşma konumu ile çakışmayabilir ve belirli bir mimarideki işaretçiler için taşma gibi bir şey olup olmadığını bile bilmiyorsunuzdur. Sonuçta, olası faydalarla herhangi bir ilişkide değil, kabus gibi bir karmaşa.
Öte yandan bir geçmiş nesne koşulu ile başa çıkmak kolaydır: Uygulama, hiçbir nesnenin hiç tahsis edilmediğinden emin olmalıdır, böylece adres alanındaki son bayt işgal edilir. Bu, garantinin faydalı ve önemsiz olması nedeniyle iyi tanımlanmıştır.