Neden Artış İşaretçiler?


25

Daha yeni C ++ öğrenmeye başladım ve çoğu insan olarak (okuduğum şeye göre) işaretçilerle mücadele ediyorum.

Geleneksel anlamda değil, ne olduklarını ve neden kullanıldıklarını ve nasıl faydalı olduklarını anlayabiliyorum, ancak işaretçilerin artmasının ne kadar yararlı olacağını anlayamıyorum, herhangi biri bir göstergenin artırılmasının ne kadar etkili olduğunu açıklayabilir. yararlı kavram ve deyimsel C ++?

Bu soru Bjarne Stroustrup tarafından A Tour of C ++ kitabını okumaya başladıktan sonra geldi , çünkü bu kitabı tavsiye ettim, çünkü Java’ya oldukça aşinaydım ve Reddit’teki çocuklar bana iyi bir “değiştirme” kitabı olacağını söylediler. .


11
Bir işaretçi sadece bir tekrarlayıcıdır
Charles Salvia

1
Okumaması gerekenleri okuyan bilgisayar virüsleri yazmak için en çok kullanılan araçlardan biridir. Ayrıca, uygulamalardaki en yaygın güvenlik açığı durumlarından biridir (bir kişi, olması gereken alanın üstüne bir işaretçi eklediğinde, onu okur veya yazar)> HeartBleed hatasını görün.
Sam

1
@ vasile İşaretçiler hakkında kötü olan budur .
Cruncher

4
C ++ ile ilgili güzel / kötü şey, bir segfault çağırmadan önce çok daha fazlasını yapmanıza olanak sağlamasıdır. Genellikle başka bir işlemin belleğine, sistem belleğine veya korumalı uygulama belleğine erişmeye çalışırken bir segfault elde edersiniz. Olağan uygulama sayfalarında herhangi bir erişime sistem tarafından izin verilir ve makul sınırlamaları zorlamak için programcı / derleyici / dil kullanımına bağlıdır. C ++ hemen hemen ne istersen yapmanı sağlar. Openssl'a gelince, kendi hafıza yöneticisine sahip - bu doğru değil. Sadece varsayılan C ++ bellek erişim mekanizmalarına sahiptir.
Sam

1
@ INdek: Sadece erişmeye çalıştığınız hafıza korumalı ise bir segfault alırsınız. Çoğu işletim sistemi sayfa düzeyinde koruma sağlar, böylece işaretçinizin başlatıldığı sayfadaki her şeye genellikle erişebilirsiniz. İşletim sistemi bir 4K sayfa boyutu kullanıyorsa, bu makul miktarda veridir. İşaretçiniz öbek içinde bir yerden başlarsa, ne kadar verilere erişebildiğinizin kimsenin tahmininde bulunmamasıdır.
TMN 16

Yanıtlar:


46

Bir diziniz olduğunda, dizinin bir öğesine işaret edecek bir işaretçi ayarlayabilirsiniz:

int a[10];
int *p = &a[0];

Buraya pilk elemana puan aolduğunu a[0]. Şimdi işaretçiyi bir sonraki öğeye işaret edecek şekilde artırabilirsiniz:

p++;

Şimdi pikinci öğeye işaret ediyor a[1]. Elemanı kullanarak buradan erişebilirsiniz *p. Bu, dizideki öğelere erişmek için bir tamsayı dizin değişkeni kullanmanız gereken Java'dan farklıdır.

Bu işaretçi etmez C bir işaretçi ++ artırım olmayan bir dizinin bir öğeye bir yer tanımsız davranış .


23
Evet, C ++ ile bir dizinin sınırları dışındaki erişim gibi programlama hatalarından kaçınmak sizin sorumluluğunuzdadır.
Greg Hewgill

9
Hayır, bir dizi öğesi dışındaki herhangi bir şeye işaret eden bir işaretçiyi artırmak , tanımsız davranıştır. Ancak, eğer düşük seviyeli ve taşınabilir olmayan bir şey yapıyorsanız, imleci arttırmak genellikle ne olursa olsun, bellekteki bir sonraki şeye erişmekten başka bir şey değildir.
Greg Hewgill

4
Dizi olarak değerlendirilen veya ele alınabilecek birkaç şey vardır; bir metin dizisi aslında bir karakter dizisidir. Bazı durumlarda, uzun bir int bir bayt dizisi olarak kabul edilir, ancak bu kolayca başınızı belaya sokabilir.
AMADANON A.Ş.

6
Bu size tipi söyler , ancak davranış 5.7 Katkı operatörleri [expr.add] içinde açıklanmıştır. Spesifik olarak, 5.7 / 5, dizinin dışında bir yerden bir yere gitmenin UB olduğunu söylüyor.
İşe yaramaz

4
Son paragraf şudur: Hem işaretçi işlenen hem de sonuç, aynı dizi nesnesinin öğelerine işaret ediyorsa, değerlendirme bir taşma yapmaz; Aksi takdirde davranış tanımsızdır . Yani sonuç ne dizide ne de sondan bir geçmişse, UB alırsınız.
İşe yaramaz

37

İşaretçi semantik (Alexander Stepanov en kapalı dayalı C ++ standart kitaplığının arkasında tasarım felsefesinin temel bir yönünü yansıtmaktadır çünkü işaretçileri artırma, C ++ biçimini olduğu STL )

Buradaki önemli kavram, STL'nin kaplar, algoritmalar ve yineleyiciler etrafında tasarlanmasıdır. İşaretçiler basitçe yineleyicilerdir .

Elbette, işaretçileri artırma (ya da toplama / çıkarma) işaretçisi C'ye geri döner. Bir çok C-string manipülasyon algoritması sadece pointer aritmetiği kullanılarak yazılabilir. Aşağıdaki kodu göz önünde bulundurun:

char string1[4] = "abc";
char string2[4];
char* src = string1;
char* dest = string2;
while ((*dest++ = *src++));

Bu kod, boş sonlandırılmış bir C dizesini kopyalamak için işaretçi aritmetik kullanır. Döngü, boş değerle karşılaştığında otomatik olarak sona erer.

C ++ ile işaretçi anlambilimi, yineleyiciler kavramına genelleştirilir . Çoğu standart C ++ konteyner üzerinden erişilebilir yineleyicileri sağlamak beginve endeleman fonksiyonlarının. Tekrarlayıcılar işaretçiler gibi davranır, böylece artırılabilir, değişkenleştirilebilir ve bazen azaltılabilir veya ilerletilebilir.

Bir yineleme yapmak için şunu std::stringsöyleriz:

std::string s = "abcdef";
std::string::iterator it = s.begin();
for (; it != s.end(); ++it) std::cout << *it;

Yineleyiciyi, tıpkı bir göstergeyi düz bir C-string'e arttırdığımız gibi yükseltiriz. Bu kavramın güçlü olmasının nedeni , gerekli kavram gereksinimlerini karşılayan herhangi bir yineleyici için çalışacak işlevleri yazmak için şablonları kullanabilmenizdir . Ve bu STL’nin gücü:

std::string s1 = "abcdef";
std::vector<char> buf;
std::copy(s1.begin(), s1.end(), std::back_inserter(buf));

Bu kod bir dizgiyi vektöre kopyalar. copyFonksiyonu ile çalışacak bir şablon herhangi (düz işaretçileri içerir) destekler artan bu yineleyici. Aynı copyişlevi düz bir C-string'de kullanabiliriz:

   const char* s1 = "abcdef";
   std::vector<char> buf;
   std::copy(s1, s1 + std::strlen(s1), std::back_inserter(buf));

Biz kullanabilirsiniz copy, bir de std::mapya bir std::setya herhangi özel konteyner destekleri Yineleyicilerin söyledi.

İşaretçilerin belirli bir yineleyici türü olduğuna dikkat edin: rasgele erişim yineleyicisi , bunlar artırıcı, azaltmalı ve +ve -işleci ile ilerletmeyi destekler . Diğer yineleyici türleri yalnızca işaretçi anlambiliminin bir alt kümesini destekler : çift ​​yönlü yineleyici en azından artırma ve azaltma işlemlerini destekler; bir ileri yineleyici en azından artışı destekler. (Tüm yineleyici türleri kayıttan kaldırmayı destekler.) copyİşlev, en azından artışı destekleyen bir yineleyici gerektirir.

Farklı yineleyici kavramları okuyabilirsiniz burada .

Bu nedenle, işaretçileri artırmak, bir C dizisini yinelemenin ya da bir C dizisindeki öğelere / ofsetlere erişmenin deyimsel bir C ++ yoludur.


3
İlk örnekte olduğu gibi işaretçiler kullansam da, bir yineleyici olarak hiç düşünmedim, şimdi çok mantıklı geliyor.
dydydyes

1
"Döngü, boş değerle karşılaştığında otomatik olarak sonlandırılır." Bu korkunç bir deyimdir.
Charles Wood

9
@CharlesWood, o zaman sanırım C'yi korkutucu bulmalısın
Siler

7
@CharlesWood: Alternatif, dize uzunluğunu döngü denetim değişkeni olarak kullanmaktır; bu, dize iki kez geçiş yapmak anlamına gelir (uzunluğu belirlemek için bir kez ve karakterleri kopyalamak için bir kez). 1MHz PDP-7 ile çalışıyorsanız, bu gerçekten eklemeye başlayabilir.
TMN 16

3
@ INdek: her şeyden önce, C ve C ++, kırılma değişikliklerini ortaya koymak için her ne pahasına olursa olsun kaçınmaya çalışır - ve dize değişmezlerinin varsayılan davranışını değiştirmenin bir değişiklik olacağını söyleyebilirim. Fakat en önemlisi, sıfır sonlandırılmış dizeler sadece bir kuraldır (dizgi değişmezlerinin varsayılan olarak sıfır sonlandırılması ve kütüphane işlevlerinin beklediği gerçeği ile takip edilmesi kolaydır), kimse sizi C'de sayılan dizgileri kullanmanızı engellemez - aslında, birkaç C kütüphanesi bunları kullanır (bakınız örn. OLE's BSTR).
Matteo Italia

16

İşaretçi aritmetik C. işaretçi Aritmetik olduğu için bu normal deyim çünkü C olduğu C ++ olan birleştiriciye .

"Arttırma yazmacının", "sabit değer 1 yükle ve kayıt listesine ekle" den daha hızlı olduğu birçok sistem vardır. Dahası, oldukça az sayıda sistem, B kayıt defterinde belirtilen adresten "DWORD A'yı A'ya yüklemenizi, sonra tek bir komutta B (bordordum) sizqu 'sini eklemenizi" sağlar. Bugünlerde bir optimizasyon derleyicisinin bunu sizin için çözmesini bekleyebilirsiniz, ancak bu gerçekten 1973'te bir seçenek değildi.

Bu, temel olarak C dizilerinin sınırlar kontrol edilmemesi ve C dizelerinin içlerinde gömülü bir boyuta sahip olmamalarının nedeni aynıdır: dil, her baytın ve her komutun sayıldığı bir sistemde geliştirilmiştir.

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.