Sabit işaretçilerin anlamı nedir?


149

Değerleri sabitlemek için işaretçilerden bahsetmiyorum, ama işaretçileri kendileri sabitlemek.

Çok temel şeylerin ötesinde C ve C ++ öğreniyorum ve bugüne kadar işaretçilerin işlevlere değerden geçtiğini anladım, bu da mantıklı. Bu, bir fonksiyonun içinde, kopyalanan işaretçiyi, arayandan orijinal işaretçiyi etkilemeden başka bir değere işaret edebileceğim anlamına gelir.

Öyleyse şöyle bir işlev başlığına sahip olmanın anlamı nedir:

void foo(int* const ptr);

Böyle bir işlevin içinde, başka bir şeye ptr noktası veremezsiniz, çünkü bu sabittir ve değiştirilmesini istemezsiniz, ancak böyle bir işlevdir:

void foo(int* ptr);

İşi de yapar! çünkü imleç yine de kopyalanır ve kopyayı değiştirseniz bile çağıran imleç etkilenmez. Peki sabitin avantajı nedir?


29
Derleme zamanında işaretçinin başka bir şeye işaret edecek şekilde değiştirilemeyeceğini ve değiştirilmemesi gerektiğini garanti etmek isterseniz ne olur?
Platinum Azure

25
Herhangi bir constparametre ile aynı nokta .
David Heffernan

2
@PlatinumAzure, birkaç yıllık deneyim programlamam var, ancak yazılım uygulamalarım eksik. Bu soruyu yaptığım için memnunum çünkü derleyicinin kendi mantık hatalarımı göstermesi gerektiğini hiç hissetmedim. Soruyu gönderdiğim anda ilk tepkim "işaretçinin değiştirilip değiştirilmeyeceğini neden umursamalıyım? Arayanı etkilemez" idi. Tüm cevapları okuyana kadar umursam gerektiğini anladım çünkü değiştirmeye çalışırsam, ben ve kodlama mantığım yanlış (ya da yıldız işareti eksik gibi bir yazım hatası vardı) ve derleyici yapmazsa fark etmemiş olabilirim Söyle bana.
R. Ruiz.

3
R.Ruiz @. Kuşkusuz, en deneyimli kişi bile ekstra doğruluk garantisi ile yapabilirdi. Yazılım mühendisliği biraz daha fazla boşluğun kabul edilebildiği bir mühendislik biçimi olduğundan (rastgele HTTP 502'ler, yavaş bağlantılar, ara sıra yüklenemeyen görüntüler olağanüstü durumlar değildir, ancak uçakta başarısız olan bir motor kabul edilemez ve ciddi olabilir), bazen insanlar aceleyle acele ediyor. Yazma birimi testlerini haklı çıkartan aynı argüman, - constdoğruluk garantilerinin kullanımını açıklayacaktır . Sadece kodumuzun kuşkusuz doğru olduğundan emin olmamızı sağlar.
Platinum Azure

Yanıtlar:


207

const çok önemli bir C ++ konseptinin peşinde koşmanız gereken bir araçtır:

Derleyicinin ne demek istediğinizi zorlamasını sağlayarak hataları çalışma zamanı yerine derleme zamanında bulun.

İşlevselliği değiştirmese de eklemek const, yapmak istemediğiniz şeyleri yaparken derleyici hatası oluşturur. Aşağıdaki yazım hatasını düşünün:

void foo(int* ptr)
{
    ptr = 0;// oops, I meant *ptr = 0
}

Bunu kullanırsanız int* const, değeri olarak değiştirdiğiniz için bu bir derleyici hatası oluşturur ptr. Sözdizimi ile kısıtlama eklemek genel olarak iyi bir şeydir. Sadece çok ileriye götürmeyin - verdiğiniz örnek, çoğu insanın kullanmadığı bir durumdur const.


8
Teşekkür ederim, bu beni ikna eden bir cevap. Const, derleyicinin kendi atama hatalarınız hakkında sizi uyarması için koydunuz. Örneğiniz bu konsepti göstermek için mükemmel çünkü işaretçilerle sık karşılaşılan bir hatadır. Senden sonra!
R. Ruiz.

25
"Derleyicinin sana yardım etmesine yardım et" normalde bunun için zikrettiğim mantra.
Flekso

1
1, ama burada argueable ilginç bir durumdur: stackoverflow.com/questions/6305906/...
Raedwald

3
"Değişkenleri sınıf dışında kullanamayacağınız zaman neden özelleştirin?" sorusuna verdiğiniz yanıtla aynıdır.
Lee Louviere

bu biraz konu dışı olabilir, ama bu sabit işaretçi bellekte nerede oturuyor? Ben tüm const belleğin küresel kısmında oturmak biliyorum, ama bir func var olarak geçirilen bir const% 100 emin değilim. teşekkürler ve bu konu dışı ise üzgünüm
Tomer

77

Ben sadece const argümanlar kullanarak bir noktaya yapmak çünkü bu daha derleyici kontrolleri sağlar: yanlışlıkla fonksiyon içinde bir argüman değeri yeniden atarsanız, derleyici beni ısırır.

Nadiren değişkenleri yeniden kullanıyorum, yeni değerleri tutmak için yeni değişkenler oluşturmak daha temiz, bu yüzden aslında tüm değişken bildirimlerim const( constkodun çalışmasını engelleyecek döngü değişkenleri gibi bazı durumlar hariç ).

Bunun bir işlev tanımında sadece anlamlı olduğunu unutmayın . Kullanıcının gördüğü deklarasyona ait değildir . Ve kullanıcı constfonksiyonun içindeki parametreler için kullanıp kullanmadığımı umursamıyor .

Misal:

// foo.h
int frob(int x);
// foo.cpp
int frob(int const x) {
   MyConfigType const config = get_the_config();
   return x * config.scaling;
}

Hem bağımsız değişkenin hem de yerel değişkenin nasıl olduğuna dikkat edin const . Her ikisi de gerekli değildir, ancak biraz daha büyük işlevlerle, bu beni tekrar tekrar hata yapmaktan kurtardı.


17
+1Başka bir constşeyden fanatik. Ancak derleyicilerimin bana havlamasını tercih ediyorum. Çok fazla hata yapıyorum ve kötü ısırırlardı.
sbi

2
+1, " constiyi bir neden olmadığı sürece" stratejisini şiddetle tavsiye ederim . Yine de bazı istisnalar var, örneğin Kopyala ve takas et
Flekso

2
Yeni bir dil tasarlıyorsam, bildirilen nesneler varsayılan olarak salt okunur ("const") olur. Bir nesneyi yazılabilir yapmak için özel bir sözdizimine, belki de "var" anahtar kelimesine ihtiyacınız vardır.
Keith Thompson

@ Keith “tabii ki” demeye cazip geldim. Bu noktada her şey aptalca. Ancak maalesef çoğu dil tasarımcısı bizimle aynı fikirde değil gibi görünüyor ... Aslında, zaten çok şey yayınladım (şimdi silinmiş bir soru olan “En tartışmalı programlama fikriniz nedir?”).
Konrad Rudolph

1
@KubaOber Bunun nasıl bir karamsarlık olduğunu anlamıyorum. Daha sonra işlevin gövdesi içinde geçirilenleri değiştirmeniz gerektiğini fark ederseniz, sadece constargümandan bırakın ve tercihen nedenini yorumlayın. Bu küçük ekstra çalışma, constvarsayılan olarak tüm argümanları işaretlemeyi ve kendinizi yaratan tüm olası hatalara açmanızı haklı çıkarmaz .
underscore_d

20

Sorunuz daha genel bir şeye değiniyor: İşlev argümanları sabit mi olmalı?

Değer bağımsız değişkenlerinin sabitliği (işaretçiniz gibi) bir uygulama ayrıntısıdır ve yok değil fonksiyon tanımlaması parçasını oluşturur. Bu, işlevinizin her zaman şu olduğu anlamına gelir:

void foo(T);

Tamamen Function-scope argüman değişkenini değişken veya sabit bir şekilde kullanmak isteyip istemediğini işlevin uygulayıcısı belirler :

// implementation 1
void foo(T const x)
{
  // I won't touch x
  T y = x;
  // ...
}

// implementation 2
void foo(T x)
{
  // l33t coding skillz
  while (*x-- = zap()) { /* ... */ }
}

Bu nedenle, hiçbir zaman constbildirim (başlık) koymamak ve değişkeni değiştirmek istemiyorsanız veya değiştirmeniz gerekmiyorsa tanım (uygulama) içine koymak için basit kuralı izleyin .


5
Buna katılıyorum - ancak beyanı ve tanımı farklılaştırma fikrinden biraz rahatsızım. Bunun dışındaki şeyler için const, tanım ve (prototip kısmı) tanım genel olarak aynı olacaktır.
Keith Thompson

@ KeithThompson: Eğer istemiyorsan bunu yapmak zorunda değilsin. Sadece constbeyanda bulunmayın. Uygulamaya niteleyiciyi eklemek tamamen size bağlıdır.
Kerrek SB

1
Söylediğim gibi, constbeyanı değil tanımın yapılmasının mantıklı olduğunu kabul ediyorum . Dilde bir aksaklık gibi geliyor, bu, beyanı ve tanımı özdeş hale getirmenin mantıklı olduğu tek durum. (Elbette C'nin tek kusuru yok.)
Keith Thompson

16

Üst düzey sabit niteleyici bildirimlerde atılır, bu nedenle sorudaki bildirimler tam olarak aynı işlevi bildirir. Öte yandan, tanımda (uygulamada) derleyici, işaretçiyi const olarak işaretlerseniz, işlevin gövdesi içinde değiştirilmediğini doğrular.


Bunu bilmiyordum. Yani void foo (int * const ptr) void foo (int * t ptr) ile aşırı yüklemeye çalışırsam derleyici hatası alırım. Teşekkürler!
R. Ruiz.

R.Ruiz @. Yavaşlamanız geçersiz (fo * int ptr) ve tanımınız geçersiz (fo * const ptr) ise, derleyici hatası ALMAZSINIZ.
Trevor Hickey

1
@TrevorHickey Yapacaklar ... ama düşündükleri için değil. int* t ptrbir sözdizimi hatasıdır. Bu olmadan, ikisi aşırı yükleme amaçları için aynıdır.
underscore_d

14

Haklısın, arayan için kesinlikle fark etmez. Ancak fonksiyonun yazarı için bir güvenlik ağı olabilir "tamam, bu noktayı yanlış şeye işaret etmememden emin olmalıyım". Çok kullanışlı değil ama işe yaramaz.

Temelde int const the_answer = 42programınızda bir ile aynıdır .


1
Nereye işaret ettikleri önemli, yığına ayrılmış bir işaretçi mi? İşlev bunu bozamaz. İşaretçi işaretçilerle çalışırken salt okunur işaretçiler kullanmak çok daha mantıklıdır. Öyle değil int const olarak aynı şey! Bunu sadece bu yanıltıcı ifade için küçümseyeceğim. const intve int consteşdeğerdir const int*ve int* constiki farklı anlamı vardır!
Lundin

1
@lundin Fonksiyonun büyük olduğunu ve içinde başka şeyler olduğunu varsayalım. Yanlışlıkla biri başka bir şeye işaret edebilir (fonksiyonun yığını üzerinde). Bu arayan için sorunlu değil ama kesinlikle arayan için olabilir. İfadelerimde yanıltıcı bir şey görmüyorum: " işlevin yazarı için bir güvenlik ağı olabilir".
cnicutar

1
@Lundin int constBölüm hakkında; Türünü bir süredir const'tan önce (doğal olmayan gibi görünüyor) koyuyorum ve farklılıkların farkındayım. Bu makale faydalı olabilir. Ancak bu stile geçmek için kendimin biraz farklı nedenleri vardı.
cnicutar

1
Tamam. Sadece sen ve ben başka birinin bu yazıyı okuduğu durumda kendime uzun bir cevap yazdım. Ayrıca int const neden kötü stil ve const int iyi bir stil olduğunu bir mantık dahil.
Lundin

Lundin, ben de okuyorum! Ben const int olsa daha iyi bir stil olduğunu kabul ediyorum. @cnicutar, Lundin'e ilk yanıtınız bu soruyu gönderdiğimde aradığım şeydi. Yani sorun arayan (akılsızca düşündüğüm gibi) değil, ama const arayan için bir güvence. Bu fikri seviyorum, teşekkür ederim.
R. Ruiz.

14

İçin çok şey var constAnahtar kelimede oldukça karmaşık bir anahtar kelime. Genel olarak, programınıza çok fazla const eklemek iyi bir programlama uygulaması olarak kabul edilir, "const doğruluğu" için web'de arama yapın ve bu konuda bol miktarda bilgi bulacaksınız.

Const anahtar sözcüğü "tür niteleyicisi" olarak adlandırılır, diğerleri volatileve restrict. En azından uçucu, const ile aynı (kafa karıştırıcı) kuralları izler.


Her şeyden önce, const anahtar sözcüğü iki amaca hizmet eder. En belirgin olanı, verileri (ve işaretçileri) salt okunur hale getirerek kasıtlı veya kazayla yanlış kullanımdan korumaktır. Bir const değişkenini değiştirme girişimleri derleyici tarafından derleme zamanında tespit edilecektir.

Ancak, salt okunur belleğe sahip herhangi bir sistemde başka bir amaç da vardır, yani böyle bir belleğin içinde belirli bir değişkenin tahsis edilmesini sağlamak - örneğin EEPROM veya flash olabilir. Bunlar kalıcı anılar, NVM olarak bilinir. NVM'de tahsis edilen bir değişken elbette bir sabit değişkenin tüm kurallarına uymaya devam edecektir.

constAnahtar kelimeyi kullanmanın birkaç farklı yolu vardır :

Sabit bir değişken bildirin.

Bu,

const int X=1; or
int const X=1;

Bu iki form tamamen eşdeğerdir . İkinci stil kötü stil olarak kabul edilir ve kullanılmamalıdır.

İkinci satırın kötü stil olarak kabul edilmesinin nedeni, muhtemelen statik ve extern gibi "depolama sınıfı belirteçleri" de gerçek türden vb. Sonra bildirilebilmesidir int static. Ancak bunu depolama sınıfı belirteçleri için geçersiz kılan özellik olarak etiketlenir C komitesi tarafından (ISO 9899 N1539 taslağı, 6.11.5). Bu nedenle, tutarlılık uğruna, bu şekilde tip niteleyicileri de yazmamalıdır. Okuyucuyu bir şekilde karıştırmaktan başka bir amaca hizmet etmiyor.

Sabit bir değişkene bir işaretçi bildirin.

const int* ptr = &X;

Bu, 'X' içeriğinin değiştirilemeyeceği anlamına gelir. Bu, özellikle "sabit doğruluk" işlev parametrelerinin bir parçası olarak böyle işaretçiler bildirmenin normal yoludur. 'X' aslında const olarak bildirilmek zorunda olmadığından herhangi bir değişken olabilir. Başka bir deyişle, değişkeni her zaman "sabit" olarak ayarlayabilirsiniz. Teknik olarak, C ayrıca açık daktilolarla const'tan düz bir değişkene indirime izin verir, ancak bunu yapmak kötü programlama olarak kabul edilir ve derleyiciler genellikle buna karşı uyarılar verir.

Sabit bir işaretçi bildirme

int* const ptr = &X;

Bu, işaretçinin kendisinin sabit olduğu anlamına gelir . İşaret ettiği şeyi değiştirebilirsiniz, ancak işaretçinin kendisini değiştiremezsiniz. Bunun pek çok kullanımı yoktur, bir işaretçi-işaretçi (işaretçi-işaretçi) bir işleve parametre olarak iletilirken adresinin değiştirilmemesini sağlamak gibi birkaç kullanım alanı vardır. Çok okunamayan bir şey yazmanız gerekecek:

void func (int*const* ptrptr)

Birçok C programcısının const ve * alabildiğinden şüpheliyim. Biliyorum ben yapamam - Ben GCC ile kontrol etmek vardı. Bence bu yüzden iyi bir programlama uygulaması olarak görülse de, işaretçi-işaretçi için sözdizimini nadiren görüyorsunuz.

Sabit işaretçiler, işaretçi değişkeninin kendisinin salt okunur bellekte bildirilmesini sağlamak için de kullanılabilir; örneğin, bir tür işaretçi tabanlı arama tablosu bildirmek ve bunu NVM'ye atamak isteyebilirsiniz.

Ve elbette, diğer cevaplarla belirtildiği gibi, "sabit doğruluk" sağlamak için sabit işaretçiler de kullanılabilir.

Sabit verilere sabit bir işaretçi bildirin

const int* const ptr=&X;

Bu, yukarıda açıklanan iki işaretçi tipidir ve her ikisinin de tüm özellikleriyle birleştirilmiştir.

Salt okunur üye işlevi bildirme (C ++)

Bu C ++ etiketli olduğundan, bir sınıfın üye işlevlerini const olarak ilan edebilirsiniz. Bu, işlevin çağrıldığında sınıfın başka bir üyesini değiştirmesine izin verilmediği anlamına gelir; bu, her ikisi de sınıfın programcısının yanlışlıkla hata yapmasını önler, ancak üye işlevinin arayanına hiçbir şey karıştırmayacaklarını bildirir arayarak. Sözdizimi:

void MyClass::func (void) const;

8

... bugün işaretçilerin değerlere göre işlevlere geçtiğini fark ettim, bu da mantıklı.

(imo) gerçekten varsayılan olarak mantıklı değil. daha mantıklı varsayılan, yeniden atanamayan işaretçi ( int* const arg) olarak geçmektir . yani, argümanların örtülü olarak const olarak bildirilmesiyle geçirilen işaretçilerin tercih edilmesini tercih ederim.

Peki sabitin avantajı nedir?

avantajı, argümanın işaret ettiği adresi değiştirdiğinizde yeterince kolay ve bazen net olmamasıdır, böylece oldukça kolay bir şekilde olmadığında bir hata oluşturabilirsiniz. adresi değiştirmek atipiktir. amacınız adresi değiştirmekse yerel bir değişken oluşturmak daha açıktır. ham işaretçi manipülasyonu hataları tanıtmanın kolay bir yoludur.

bu nedenle, değişkenin işaret ettiği adresi değiştirmek istediğinizde, değiştirilemeyen adreslerden geçmek ve bir kopya oluşturmak (atipik durumlarda) daha açıktır:

void func(int* const arg) {
    int* a(arg);
    ...
    *a++ = value;
}

yerelin neredeyse ücretsiz olduğunu ve okunabilirliği geliştirirken hata olasılığını azaltır.

daha yüksek bir seviyede: bağımsız değişkeni bir dizi olarak işliyorsanız, argümanı bir kapsayıcı / koleksiyon olarak bildirmek istemciye genellikle daha açık ve daha az hata yapar.

genel olarak, değerlere, bağımsız değişkenlere ve adreslere const eklemek iyi bir fikirdir, çünkü derleyicinin mutlu bir şekilde uyguladığı yan etkileri her zaman fark etmezsiniz. bu nedenle, diğer bazı durumlarda kullanılan const kadar yararlıdır (örneğin, soru 'Neden const değerlerini bildirmeliyim?' sorusuna benzer). Neyse ki, yeniden atanamayan referanslarımız da var.


4
Bunun varsayılanı +1. C ++, çoğu dil gibi, yanlış bir yöne sahiptir. Bir constanahtar kelimeye sahip olmak yerine, bir anahtar kelimeye sahip olmalıdır mutable(iyi, ancak yanlış anlambilime sahip).
Konrad Rudolph

2
Varsayılan olarak const sahip ilginç bir fikir. Cevabınız için teşekkürler!
R. Ruiz.

6

Bellek eşlemeli aygıtlarınız olan tümleşik sistemler veya aygıt sürücüsü programlaması yaparsanız, işaretçinin yeniden atanmasını önlemek için (sabit bir donanım adresini işaret ettiğinden) her iki 'const' biçimi de kullanılır ve çevre birimi varsa kayıt o salt okunur bir donanım kayıt olduğunu gösterir sonra başka bir const çalışma zamanı yerine derleme zamanında çok sayıda hata algılar.

Salt okunur 16 bit çevresel çip kaydı aşağıdaki gibi görünebilir:

static const unsigned short *const peripheral = (unsigned short *)0xfe0000UL;

Daha sonra montaj diline başvurmadan donanım kaydını kolayca okuyabilirsiniz:

input_word = *peripheral;


5

int iVal = 10; int * const ipPtr = & iVal;

Tıpkı normal bir const değişkeni gibi, bildirimde bir değere bir const işaretçisi başlatılmalıdır ve değeri değiştirilemez.

Bu, sabit bir işaretçinin her zaman aynı değere işaret edeceği anlamına gelir. Yukarıdaki durumda, ipPtr her zaman iVal'in adresini gösterecektir. Bununla birlikte, işaret edilen değer hala sabit olmadığından, işaretçinin kaydının kaldırılmasıyla işaret edilen değerin değiştirilmesi mümkündür:

* ipPtr = 6; // izin verildi, çünkü pnPtr sabit olmayan bir int'i gösteriyor


5

Aynı soru başka herhangi bir tür hakkında da sorulabilir (sadece işaretçiler değil):

/* Why is n const? */
const char *expand(const int n) {
    if (n == 1) return "one";
    if (n == 2) return "two";
    if (n == 3) return "three";
    return "many";
}

LOL, haklısın. İşaretçiler vakası önce aklıma geldi çünkü özel olduklarını düşündüm, ama değerden geçen herhangi bir değişken gibi.
R. Ruiz.

5

Sorunuz gerçekten, herhangi bir değişkeni neden sadece bir işleve const pointer parametresi olarak tanımlamak değil. Burada aynı kural, işlev veya üye değişkeni veya yerel değişken için bir parametre ise, herhangi bir değişkeni sabit olarak tanımladığınızda geçerlidir.

Özel durumunuzda, yerel bir değişkeni const olarak bildirdiğinizde diğer birçok durumda olduğu gibi işlevsel olarak fark etmez, ancak bu değişkeni değiştiremeyeceğiniz bir kısıtlama getirir.


yorumunuz sorumun ne kadar nafile olduğunu fark etmenizi sağlıyor çünkü haklısınız: const olarak başka bir parametreye sahip olmakla aynı şey. Yararsız görünebilir, ancak programcının bu kısıtlamaya sahip olmasına ve hatalardan kaçınmasına yardımcı olmak için orada
R. Ruiz.

4

Bir işleve bir sabit işaretçiyi iletmek, zaten değere göre aktarılacağı için çok mantıklı değildir. Genel dil tasarımının izin verdiği şeylerden sadece bir tanesi. Sadece mantıklı olmadığı için yasaklamak sadece dili belirleyecektir. Daha büyük.

Eğer bir fonksiyonun içindeyseniz, bu tabii ki başka bir durumdur. İşaret ettiği şeyi değiştiremeyen bir işaretçiye sahip olmak, kodu daha net yapan bir iddiadır.


4

Bir avantaj, derleyicinin bu işaretçinin değişemeyeceğini bilerek işlev içinde daha agresif optimizasyonlar gerçekleştirebileceğidir.

Ayrıca, örneğin; bu işaretçiyi sabit olmayan bir işaretçi başvurusunu kabul eden bir alt işleve geçirme (ve bu nedenle işaretçiyi şu şekilde değiştirebilir void f(int *&p)), ancak bu durumda yararlılığın biraz sınırlı olduğunu kabul ediyorum.


Optimizasyonlar için +1. En azından kitaplar bunun doğru olduğunu söylüyor. Ne istiyorum tam olarak bu optimizasyonların ne olduğunu anlamak için
R. Ruiz.

4

Bir sabit işaretçinin oldukça uygulanabilir olduğu bir örnek, bu şekilde gösterilebilir. İçinde dinamik bir dizi olan bir sınıfınız olduğunu ve kullanıcının diziye erişimini, ancak işaretçiyi değiştirme hakları vermeden geçirmek istediğinizi düşünün. Düşünmek:

#include <new>
#include <string.h>

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const GetArray(){ return Array; }
};

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' '; //You can still modify the chars in the array, user has access
    Temp.GetArray()[1] = ' '; 
    printf("%s\n",Temp.GetArray());
}

Hangi üretir:

Girdi verileri veri
koyma

Ama bunu denersek:

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' ';
    Temp.GetArray()[1] = ' ';
    printf("%s\n",Temp.GetArray());
    Temp.GetArray() = NULL; //Bwuahahahaa attempt to set it to null
}

Biz:

hata: atamanın sol işleneni olarak lvalue gerekli // Drat tekrar başarısız oldu!

Bu yüzden dizinin içeriğini değiştirebiliriz, ancak dizinin işaretçisini değiştiremeyiz. İşaretçiyi kullanıcıya geri gönderirken tutarlı bir duruma sahip olduğundan emin olmak istiyorsanız iyi. Ancak bir yakalama var:

int main()
{
    TestA Temp;
    printf("%s\n",Temp.GetArray());
    Temp.GetArray()[0] = ' ';
    Temp.GetArray()[1] = ' ';
    printf("%s\n",Temp.GetArray());
    delete [] Temp.GetArray(); //Bwuahaha this actually works!
}

İmlecin kendisini değiştiremesek bile işaretçinin bellek referansını silebiliriz.

Bu nedenle, bellek referansının her zaman bir şeye işaret etmesini istiyorsanız (IE, şu anda bir referansın nasıl çalıştığına benzer şekilde asla değiştirilmez), o zaman oldukça uygulanabilir. Kullanıcının tam erişime sahip olmasını ve üzerinde değişiklik yapmasını istiyorsanız, sabit olmayan sizin içindir.

Düzenle:

OkArz001 GetArray () bir sağ-değer işlenen olduğu için atama yapamadığına dair yorumda bulunduktan sonra, yorumu tamamen doğrudur, ancak işaretçiye bir referans döndürmeniz durumunda yukarıdakiler hala geçerlidir (GetArray'ın referans gönderme), örneğin:

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const &GetArray(){ return Array; } //Note & reference operator
        char * &GetNonConstArray(){ return Array; } //Note non-const
};

int main()
{
    TestA Temp;
    Temp.GetArray() = NULL; //Returns error
    Temp.GetNonConstArray() = NULL; //Returns no error
}

İlk hata ile sonuçlanır:

hata: salt okunur konumun atanması 'Temp.TestA :: GetArray ()'

Ancak ikincisi, altındaki potansiyel sonuçlara rağmen nadiren gerçekleşecek.

Açıkçası, 'neden bir işaretçiye bir referans döndürmek istersiniz' sorusu ortaya çıkacak? Doğrudan söz konusu orijinal işaretçiye bellek (veya veri) atamanız gereken nadir durumlar vardır (örneğin, kendi malloc / free veya new / free front-end'inizi oluşturmak), ancak bu durumlarda const olmayan bir referanstır . Bir const pointer bir referans onu garanti bir durum rastlamak değil (belki iade türleri yerine beyan const başvuru değişkenleri olarak?).

Bir sabit işaretçi alan bir işleve sahip olduğumuzu düşünün (buna karşı olmayan):

class TestA
{
    private:
        char *Array;
    public:
        TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
        ~TestA(){if(Array != NULL){ delete [] Array;} }

        char * const &GetArray(){ return Array; }

        void ModifyArrayConst(char * const Data)
        {
            Data[1]; //This is okay, this refers to Data[1]
            Data--; //Produces an error. Don't want to Decrement that.
            printf("Const: %c\n",Data[1]);
        }

        void ModifyArrayNonConst(char * Data)
        {
            Data--; //Argh noo what are you doing?!
            Data[1]; //This is actually the same as 'Data[0]' because it's relative to Data's position
            printf("NonConst: %c\n",Data[1]);
        }
};

int main()
{
    TestA Temp;
    Temp.ModifyArrayNonConst("ABCD");
    Temp.ModifyArrayConst("ABCD");
}

Sabit hata böylece mesaj üretir:

hata: salt okunur parametrenin azalması 'Veri'

Bu, yorumlarda belirtilen sorunlara neden olmak istemedikçe, muhtemelen bunu yapmak istemediğimiz için iyidir. Const işlevindeki azalmayı düzenlersek, aşağıdakiler gerçekleşir:

NonConst: Bir Yapısı:
B

Açıkça, A 'Veri [1]' olsa da, 'Veri [0]' olarak kabul edilir, çünkü NonConst işaretçisi azaltma işlemine izin verir. Const uygulandığında, başka bir kişinin yazdığı gibi, potansiyel hatayı oluşmadan yakalarız.

Bir diğer ana husus, bir const işaretçisinin sözde referans olarak kullanılabilmesidir, referansın işaret edeceği şey değiştirilemez (bir mucize, belki de bu nasıl uygulandıysa). Düşünmek:

int main()
{
    int A = 10;
    int * const B = &A;
    *B = 20; //This is permitted
    printf("%d\n",A);
    B = NULL; //This produces an error
}

Derlemeye çalışırken aşağıdaki hatayı üretir:

hata: salt okunur değişken 'B' ataması

A'ya sürekli bir referans isteniyorsa, muhtemelen kötü bir şeydir. Yorum yapılırsa B = NULL, derleyici mutlu bir şekilde değiştirmemize izin verir *Bve bu nedenle A. Ints ile kullanışlı görünmeyebilir, ancak geçebileceğiniz, atıfta bulunabilecek, değiştirilemeyen bir işaretçi istediğiniz tek bir grafik uygulamasının duruşu olup olmadığını düşünün. etrafında.

Kullanımı değişkendir (istenmeyen puntayı bahane edin), ancak doğru bir şekilde kullanıldığında, programlamaya yardımcı olmak için kutudaki başka bir araçtır.


2
Temp.GetArray() = NULLbaşarısız olur çünkü Temp.GetArray()const nitelikli olduğu için bir değerdir. Ayrıca, const-niteleyicinin tüm dönüş türlerinden çıkarıldığına inanıyorum.
Oscar Korz

@ okorz001: Test ettikten sonra gerçekten haklısınız. Ancak, işaretçinin kendisine bir başvuru döndürürseniz, yukarıdakiler geçerlidir. Yazımı buna göre düzenleyeceğim.
SSight3

3

Hiçbir zaman sabit olmalarını istemeyeceğiniz işaretçiler hakkında özel bir şey yoktur. Sınıf üyesi sabit intdeğerlerine sahip olabileceğiniz gibi , benzer nedenlerle sabit işaretçiler de kullanabilirsiniz: Kimsenin işaret edilen şeyi değiştirmediğinden emin olmak istiyorsunuz. C ++ bunu biraz ele alır, ancak işaretçi davranışı C'den miras alınır.


3

Bu, kodun işlev gövdesi içindeki işaretçiyi artırmasını veya azaltmasını önleyeceğine inanıyorum.


3

Herhangi bir değişkeni bildirme türleri-
(1) Sabit değişken bildirme.
DataType const varibleName;

 int const x;
    x=4; //you can assign its value only One time
(2) Sabit bir değişkene bir işaretçi bildirin
const dataType* PointerVaribleName=&X;
 const int* ptr = &X;
     //Here pointer variable refer contents of 'X' that is const Such that its cannot be modified
dataType* const PointerVaribleName=&X;
 int* const ptr = &X;
     //Here pointer variable itself is constant  Such that value of 'X'  can be modified But pointer can't be modified


Gerçek değerini değiştirmek istemediğimizde, bir değişken olarak bir işlev olarak sabit bir işaretçi değişkeni sunuyoruz
Pyadav
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.