Neden işaretçiler kullanılır? [kapalı]


358

Bunun gerçekten temel bir soru olduğunu biliyorum, ancak birkaç projeyi üst düzey dillerle kodladıktan sonra bazı temel C ++ programlamalarına başladım.

Temelde üç sorum var:

  1. Normal değişkenler üzerinde neden işaretçiler kullanılır?
  2. İşaretçileri ne zaman ve nerede kullanmalıyım?
  3. Dizili işaretçileri nasıl kullanıyorsunuz?

5
Bu soruda daha önce tartışıldı Bu yardımcı olur umut!
Doug T.

4
Kitapların listesi için bkz. Stackoverflow.com/questions/388242/… . Java'dan sonra, Accelerated C ++ 'ı çok kullanışlı buldum .
Jabba

163
İşaretçiler kullanıyoruz çünkü birisine evinize bir adres vermek, evinizin bir kopyasını herkese vermekten daha kolaydır.
Rishi Dua

14
@RishiDua Bu, şimdiye kadar karşılaştığım bir işaretçinin en iyi açıklamasıdır. Bunun için teşekkür ederim, anlayışımı arttırdı :)
Josh

2
İşaretçiler ayrıca birden fazla değer / nesne döndürmek istediğinizde de kullanılabilir.
Gaurav

Yanıtlar:


173
  • Normal değişkenler üzerinde neden işaretçiler kullanılır?

Kısa cevap: Yapma. ;-) İşaretçiler başka hiçbir şey kullanamayacağınız yerlerde kullanılmalıdır. Bunun nedeni, uygun işlevsellik, eksik veri türlerinin olmaması veya saf performans için olmasıdır. Daha fazlası aşağıda ...

  • İşaretçileri ne zaman ve nerede kullanmalıyım?

Kısa cevap: Başka bir şey kullanamayacağınız yer. C'de, dize gibi karmaşık veri türleri için herhangi bir desteğiniz yoktur. Bir değişkene "referans olarak" bir işleve iletmenin de bir yolu yoktur. İşaretçileri kullanmanız gereken yer burası. Ayrıca neredeyse her şeye, bağlantılı listelere, yapı üyelerine vb. Ama burada buna girmeyelim.

  • Dizili işaretçileri nasıl kullanıyorsunuz?

Az çaba ve kafa karışıklığı ile. ;-) int ve char gibi basit veri türlerinden bahsedersek, dizi ve işaretçi arasında çok az fark vardır. Bu bildirimler çok benzer (ancak aynı değil - örneğin, sizeoffarklı değerler döndürür):

char* a = "Hello";
char a[] = "Hello";

Dizideki herhangi bir öğeye böyle ulaşabilirsiniz

printf("Second char is: %c", a[1]);

Dizi 0 ile başladığından beri Dizin 1. :-)

Veya bunu aynı şekilde yapabilirsiniz

printf("Second char is: %c", *(a+1));

İşaretçiye işleç (*), printf'e bir karakter yazdırmak istediğimizi söylediğimiz için gereklidir. * Olmadan, bellek adresinin kendisinin karakter temsili yazdırılır. Şimdi karakterin kendisini kullanıyoruz. % C yerine% s kullansaydık, printf'den 'a' artı bir (yukarıda bu örnekte) ile gösterilen bellek adresinin içeriğini yazdırmasını isterdik ve * önünde:

printf("Second char is: %s", (a+1)); /* WRONG */

Ancak bu sadece ikinci karakteri basmakla kalmaz, bunun yerine bir boş karakter (\ 0) bulunana kadar bir sonraki bellek adreslerindeki tüm karakterleri basar. İşte bu noktada işler tehlikeli olmaya başlar. Yanlışlıkla% s biçimlendiricisine sahip bir char işaretçisi yerine type tamsayı değişkenini yazdırmayı denerseniz ne olur?

char* a = "Hello";
int b = 120;
printf("Second char is: %s", b);

Bu, bellek adresi 120'de bulunan her şeyi basar ve boş bir karakter bulunana kadar yazdırmaya devam eder. Bu printf deyimini gerçekleştirmek yanlış ve yasadışıdır, ancak bir işaretçi aslında birçok ortamda int türünde olduğundan, muhtemelen yine de işe yarayacaktır. Bunun yerine sprintf () kullanırsanız ve bu şekilde çok uzun bir "char dizisi" atadığınızda, yalnızca belirli bir sınırlı alana ayrılmış başka bir değişkene atabileceğiniz sorunları düşünün. Büyük olasılıkla bellekte başka bir şeyin üzerine yazmak ve programınızın çökmesine neden olursunuz (şanslıysanız).

Oh, ve eğer bildirirken char dizisine / işaretçisine bir dize değeri atamazsanız, bir değer vermeden önce ona yeterli miktarda bellek ayırmanız GEREKİR. Malloc, calloc veya benzerlerini kullanma. Bu, dizinizde yalnızca bir öğeyi / tek bir bellek adresini işaret etmek için bildirdiğiniz için. İşte birkaç örnek:

char* x;
/* Allocate 6 bytes of memory for me and point x to the first of them. */
x = (char*) malloc(6);
x[0] = 'H';
x[1] = 'e';
x[2] = 'l';
x[3] = 'l';
x[4] = 'o';
x[5] = '\0';
printf("String \"%s\" at address: %d\n", x, x);
/* Delete the allocation (reservation) of the memory. */
/* The char pointer x is still pointing to this address in memory though! */
free(x);
/* Same as malloc but here the allocated space is filled with null characters!*/
x = (char *) calloc(6, sizeof(x));
x[0] = 'H';
x[1] = 'e';
x[2] = 'l';
x[3] = 'l';
x[4] = 'o';
x[5] = '\0';
printf("String \"%s\" at address: %d\n", x, x);
/* And delete the allocation again... */
free(x);
/* We can set the size at declaration time as well */
char xx[6];
xx[0] = 'H';
xx[1] = 'e';
xx[2] = 'l';
xx[3] = 'l';
xx[4] = 'o';
xx[5] = '\0';
printf("String \"%s\" at address: %d\n", xx, xx);

Ayrılan belleğin bir boş () işlemini yaptıktan sonra x değişkenini kullanmaya devam edebileceğinizi, ancak orada ne olduğunu bilmediğinizi unutmayın. Ayrıca iki printf () işlevinin size farklı adresler verebileceğine de dikkat edin, çünkü ikinci bellek tahsisinin birinciyle aynı alanda gerçekleştirileceğinin garantisi yoktur.


8
İçinde 120 olan örnek yanlış. Hata yok% c değil% s kullanıyor; yalnızca x küçük harfini basar. Ayrıca, bir işaretçinin int türü olduğu iddianız yanlış ve işaretçilerle deneyimsiz bir C programcısına vermek için çok kötü bir tavsiye.
R .. GitHub BUZA YARDIMCI DURDUR

13
-1 İyi başladınız ama ilk örnek yanlış. Hayır, aynı değil. İlk durumda abir işaretçi ve ikinci durumda abir dizi. Zaten bahsetmiş miydim? Aynı şey değil! Kendinizi kontrol edin: Sizeof (a) 'yı karşılaştırın, bir diziye yeni bir adres atamayı deneyin. Çalışmaz.
sellibitze

42
char* a = "Hello";ve char a[] = "Hello";aynı değil, oldukça farklılar. Biri bir işaretçi diğerini bir dizi bildirir. Bir deneyin sizeofve farkı görün.
Patrick Schlüter

12
Msgstr "Bu printf bildirimini yapmak yanlış veya yasadışı değil". Bu çok yanlış. Bu printf deyiminin tanımlanmamış bir davranışı var. Birçok uygulamada erişim ihlaline neden olacaktır. İşaretçiler aslında int türünde değil, aslında işaretçilerdir (ve başka bir şey değildir).
Mankarse

19
-1, burada çok fazla gerçek var yanlış ve ben sadece upvotes miktarını veya kabul edilen cevap durumunu hak etmediğini söyleyeceğim.
asveikau

50

İşaretçilerin kullanılmasının bir nedeni, bir değişkenin veya nesnenin çağrılan bir işlevde değiştirilebilmesidir.

C ++ 'da referansları kullanmak işaretçilerden daha iyi bir uygulamadır. Referanslar aslında işaretçiler olsa da, C ++ bir dereceye kadar gerçeği gizler ve sanki değerden geçiyormuşsunuz gibi görünür. Bu, çağrı fonksiyonunun değeri alma şeklini, onu geçirme anlamını değiştirmek zorunda kalmadan değiştirmeyi kolaylaştırır.

Aşağıdaki örnekleri düşünün:

Referansları kullanma:

public void doSomething()
{
    int i = 10;
    doSomethingElse(i);  // passes i by references since doSomethingElse() receives it
                         // by reference, but the syntax makes it appear as if i is passed
                         // by value
}

public void doSomethingElse(int& i)  // receives i as a reference
{
    cout << i << endl;
}

İşaretçileri kullanma:

public void doSomething()
{
    int i = 10;
    doSomethingElse(&i);
}

public void doSomethingElse(int* i)
{
    cout << *i << endl;
}

16
Boş bir referansı geçemeyeceğiniz için referansların daha güvenli olduğunu belirtmek iyi bir fikir olabilir.
SpoonMeiser

26
Evet, muhtemelen referans kullanmanın en büyük avantajı budur. Gösterdiğiniz için teşekkürler. Hiçbir ceza amaçlanan :)
trshiv

2
Mutlaka olabilir null başvuru geçmektedir. Boş gösterici geçmek kadar kolay değil.
n0rd

1
@ n0rd ile aynı fikirde. Eğer boş bir referansı geçemeyeceğinizi düşünüyorsanız yanılıyorsunuz. Sarkan bir referansı null referanstan geçirmek daha kolaydır, ancak sonuçta her ikisini de yeterince kolay yapabilirsiniz. Referanslar, bir mühendisin kendini ayağından vurmasını engelleyen gümüş bir kurşun değildir. Canlı izle .
WhozCraig

2
@ n0rd: Bunu açıkça tanımlanmamış bir davranış.
Daniel Kamil Kozar

42
  1. İşaretçiler birden çok yerden bellekte aynı alana başvurmanıza olanak tanır. Bu, belleği bir konumdan güncelleyebileceğiniz ve değişikliğin programınızdaki başka bir konumdan görülebileceği anlamına gelir. Ayrıca veri yapılarınızdaki bileşenleri paylaşarak yerden tasarruf edersiniz.
  2. İşaretçileri, adresi almanız ve bellekteki belirli bir noktaya iletmeniz gereken herhangi bir yerde kullanmalısınız. Dizilerde gezinmek için işaretçiler de kullanabilirsiniz:
  3. Dizi, belirli bir türle ayrılmış bitişik bir bellek bloğudur. Dizinin adı, dizinin başlangıç ​​noktasının değerini içerir. 1 eklediğinizde, bu sizi ikinci noktaya götürür. Bu, diziye erişmek için açık bir sayaca sahip olmadan diziyi aşağı kaydıran bir işaretçiyi artıran döngüler yazmanıza olanak tanır.

C dilinde bir örnek:

char hello[] = "hello";

char *p = hello;

while (*p)
{
    *p += 1; // increase the character by one

    p += 1; // move to the next spot
}

printf(hello);

baskılar

ifmmp

çünkü her karakter için değeri alır ve birer birer artırır.


because it takes the value for each character and increments it by one. ASCII Temsilinde mi, Nasıl?
Eduardo S.

28

İşaretçiler, başka bir değişkene dolaylı başvuru almanın bir yoludur. Bir değişkenin değerini tutmak yerine size adresini söylerler . Bu özellikle dizilerle uğraşırken kullanışlıdır, çünkü bir dizideki ilk öğeye bir işaretçi (adresi) kullanarak işaretçiyi (bir sonraki adres konumuna) artırarak bir sonraki öğeyi hızlı bir şekilde bulabilirsiniz.

Okuduğum işaretçiler ve işaretçi aritmetiğinin en iyi açıklaması K & R'nin C Programlama Dili . C ++ öğrenmeye başlamak için iyi bir kitap C ++ Primer'dır .


1
teşekkür ederim! Son olarak, yığını kullanmanın yararları hakkında pratik bir açıklama! dizide konumlandırılacak bir işaretçi de @ işaretçisine göre @ ve değerlerine erişme performansını artırır mı?

Bu muhtemelen okuduğum en iyi açıklama. insanlar "aşırı karmaşık" şeyler bir yolu var :)
Detilium

23

Bunu da cevaplayayım.

İşaretçiler referanslara benzer. Başka bir deyişle, bunlar kopya değil, orijinal değere başvurmanın bir yoludur.

Her şeyden önce , tipik olarak çok fazla işaretçi kullanmanız gereken bir yer , gömülü donanımla uğraşırken . Belki bir dijital IO pininin durumunu değiştirmelisiniz. Belki bir kesmeyi işliyorsunuzdur ve belirli bir yerde bir değer depolamanız gerekir. Resmi aldınız. Ancak, donanımla doğrudan ilgilenmiyorsanız ve sadece hangi türlerin kullanılacağını merak ediyorsanız, okumaya devam edin.

Neden normal değişkenlerin aksine işaretçiler kullanılır? Sınıflar, yapılar ve diziler gibi karmaşık türlerle uğraşırken cevap daha açık hale gelir. Normal bir değişken kullanacak olsaydınız, bir kopya oluşturabilirsiniz (derleyiciler bunu önlemek için yeterince akıllıdır ve C ++ 11 de yardımcı olur, ancak şimdilik bu tartışmadan uzak kalacağız).

Orijinal değeri değiştirmek isterseniz ne olur? Bunun gibi bir şey kullanabilirsiniz:

MyType a; //let's ignore what MyType actually is right now.
a = modify(a); 

Bu işe yarayacak ve neden işaretçiler kullandığınızı tam olarak bilmiyorsanız, onları kullanmamalısınız. "Muhtemelen daha hızlılar" nedenine dikkat edin. Kendi testlerinizi yapın ve gerçekten daha hızlılarsa bunları kullanın.

Ancak, diyelim ki bellek ayırmanız gereken bir problemi çözüyorsunuz. Belleği ayırdığınızda, yeniden yerleştirmeniz gerekir. Bellek ayırma başarılı olabilir veya olmayabilir. Bu, işaretçilerin yararlı olduğu yerdir - ayırdığınız nesnenin varlığını test etmenize ve işaretçinin referansını alarak belleğin ayrılan nesneye erişmenize izin verir.

MyType *p = NULL; //empty pointer
if(p)
{
    //we never reach here, because the pointer points to nothing
}
//now, let's allocate some memory
p = new MyType[50000];
if(p) //if the memory was allocated, this test will pass
{
    //we can do something with our allocated array
    for(size_t i=0; i!=50000; i++)
    {
        MyType &v = *(p+i); //get a reference to the ith object
        //do something with it
        //...
    }
    delete[] p; //we're done. de-allocate the memory
}

İşaretçileri neden kullanmanızın anahtarı budur - referanslar, başvurduğunuz öğenin zaten var olduğunu varsayar . İşaretçi göstermez.

İşaretçileri kullanmanızın (veya en azından onlarla uğraşmak zorunda kalmanın) diğer bir nedeni, referanslardan önce var olan bir veri türüdür. Bu nedenle, daha iyi olduklarını bildiğiniz şeyleri yapmak için kitaplıklar kullanırsanız, bu kitaplıkların birçoğunun, etrafta ne kadar süredir bulundukları nedeniyle (çok fazla) bunlardan C ++ 'dan önce yazılmıştır).

Herhangi bir kütüphane kullanmadıysanız, kodunuzu işaretçilerden uzak duracak şekilde tasarlayabilirsiniz, ancak işaretçilerin dilin temel türlerinden biri olduğu göz önüne alındığında, bunları ne kadar rahat kullanırsanız, o kadar çok taşınabilir C ++ becerileriniz olurdu.

Sürdürülebilirlik açısından, işaretçileri kullandığınızda, ya geçerliliklerini test etmeniz ve geçerli olmadıklarında davayı ele almanız gerektiğini veya sadece geçerli olduğunu varsaymanız ve varsayım bozulduğunda program çökebilir veya kötüleşir. Başka bir deyişle, işaretçilerle seçiminiz, bir şey bozulduğunda kod karmaşıklığı veya daha fazla bakım çabası getirmektir ve bellek bozulma gibi işaretçilerin getirdiği tüm hata sınıflarına ait bir hatayı izlemeye çalışıyorsunuz.

Bu nedenle, tüm kodunuzu kontrol ederseniz, işaretçilerden uzak durun ve bunun yerine referanslar kullanın, mümkün olduğunda sabit tutun. Bu sizi nesnelerinizin yaşam sürelerini düşünmeye zorlayacak ve kodunuzun daha kolay anlaşılmasını sağlayacaktır.

Sadece bu farkı hatırlayın: Bir referans aslında geçerli bir işaretçi. İşaretçi her zaman geçerli değildir.

Geçersiz bir referans oluşturmanın imkansız olduğunu mu söylüyorum? Hayır. Bu tamamen mümkün, çünkü C ++ neredeyse her şeyi yapmanıza izin veriyor. İstemeden yapmak daha zordur ve kaç tane hatanın istemeden olduğuna şaşıracaksınız :)


Bellek eşlemeli G / Ç için, temel olarak işaretçilerin kullanımından kaçınabileceğiniz güzel sarmalayıcı sınıfları yazabilirsiniz.
einpoklum

13

İşte C'nin birçok özelliğinin neden mantıklı olduğuna biraz farklı, ancak anlayışlı bir bakış: http://steve.yegge.googlepages.com/tour-de-babel#C

Temel olarak, standart CPU mimarisi bir Von Neumann mimarisidir ve böyle bir makinede bellekteki bir veri öğesinin konumuna atıfta bulunabilmek ve onunla aritmetik yapabilmek çok faydalıdır. Montaj dilinin herhangi bir çeşidini biliyorsanız, bunun düşük seviyede ne kadar önemli olduğunu hızlı bir şekilde göreceksiniz.

C ++, işaretçileri biraz kafa karıştırıcı yapar, çünkü bazen bunları sizin için yönetir ve etkilerini "referanslar" biçiminde gizler. Düz C kullanırsanız, işaretçi ihtiyacı çok daha açıktır: referansla arama yapmanın başka bir yolu yoktur, bir dizeyi saklamanın en iyi yolu, bir dizi üzerinden yineleme yapmanın en iyi yolu vb.


12

İşaretçilerin bir kullanımı (başkalarının yayınlarında zaten yer alan şeylerden bahsetmeyeceğim) ayırmadığınız belleğe erişmektir. Bu, PC programlaması için çok yararlı değildir, ancak bellek eşlemeli donanım cihazlarına erişmek için gömülü programlamada kullanılır.

DOS'un eski günlerinde, aşağıdakilere bir işaretçi bildirerek video kartının video belleğine doğrudan erişebiliyordunuz:

unsigned char *pVideoMemory = (unsigned char *)0xA0000000;

Birçok yerleşik cihaz hala bu tekniği kullanmaktadır.


Bir işaretçi kullanmak için bir neden değil - uzunluğu da tutan açıklık benzeri bir yapı - çok daha uygundur. Bugünlerde öyle gsl::spanve yakında olacak std::span.
einpoklum

10

Büyük ölçüde işaretçiler dizilerdir (C / C ++ 'da) - bellekteki adreslerdir ve istenirse bir dizi gibi erişilebilirler ("normal" durumlarda).

Bir öğenin adresi oldukları için küçükler: sadece bir adresin alanını kaplıyorlar. Küçük oldukları için onları bir işleve göndermek ucuzdur. Ve sonra bu işlevin bir kopyadan ziyade gerçek öğe üzerinde çalışmasına izin veriyorlar.

Dinamik depolama ayırma yapmak istiyorsanız (örneğin, bağlantılı bir liste için), işaretçileri kullanmalısınız, çünkü bunlar yığından bellek almanın tek yoludur.


8
İşaretçilerin diziler olduğunu söylemek yanıltıcıdır. Gerçekten, dizi adları dizinin ilk öğesine sabittir. Sadece bir dizi olduğu gibi rastgele bir noktaya erişebildiğiniz için ... erişim ihlali alabilirsiniz :)
rmeador

2
İşaretçiler diziler değildir. Comp.lang.c SSS bölüm 6'yı okuyun .
Keith Thompson

İşaretçiler gerçekten diziler değildir. Ayrıca, ham dizilerde çok fazla kullanım yok - kesinlikle C ++ 11 ve sonrasında değil std::array.
einpoklum

@einpoklum - işaretçiler referans işlemlerinde eşdeğer olacak şekilde dizilere yakındır ve diziler aracılığıyla yinelenir :) .... ayrıca - C ++ 11, 2008'de bu cevap yazıldığında serbest bırakılmasından 3 yıl sonra
warren

@warren: İşaretçiler diziler veya dizilere yakın değiller, sadece dizilere ayrılırlar. İnsanlara benzer olduklarını söylemek pedagojik olarak uygun değildir. Ayrıca, işaretçilerle aynı tipte olmayan ve boyut bilgilerini koruyan dizilere kesinlikle başvurabilirsiniz; buraya
einpoklum

9

İşaretçiler, tasarımı bir "düğümü" diğerine verimli bir şekilde bağlama veya zincirleme yeteneğini gerektiren birçok veri yapısında önemlidir. Bir işaretçi üzerinde "float" gibi normal bir veri türü "seçmezsiniz", sadece farklı amaçları vardır.

İşaretçiler, yüksek performans ve / veya kompakt bellek alanına ihtiyaç duyduğunuzda kullanışlıdır.

Dizinizdeki ilk öğenin adresi bir işaretçiye atanabilir. Bu daha sonra temelde ayrılan baytlara doğrudan erişmenizi sağlar. Bir dizinin asıl amacı bunu yapmanıza gerek kalmamasıdır.


9

Değişkenler üzerinde işaretçiler kullanmanın bir yolu, gereken yinelenen belleği ortadan kaldırmaktır. Örneğin, bazı büyük karmaşık nesneleriniz varsa, yaptığınız her başvuru için bu değişkeni işaret etmek üzere bir işaretçi kullanabilirsiniz. Bir değişkenle, her kopya için belleği çoğaltmanız gerekir.


Bu, işaretçileri değil, referansları (veya en çok - referans paketlerini) kullanmanın bir nedenidir.
einpoklum

6

C ++ 'da alt tip polimorfizm kullanmak istiyorsanız , işaretçiler kullanmanız gerekir . Bu yazıyı gör: C ++ Göstergesiz polimorfizm .

Gerçekten, düşündüğünüzde, bu mantıklı. Alt tür polimorfizmini kullandığınızda, sonuçta, gerçek sınıfın ne olduğunu bilmediğiniz için hangi sınıfın veya alt sınıfın yöntemi uygulayacağını önceden bilmezsiniz.

Bilinmeyen bir sınıftaki bir nesneyi barındıran bir değişkene sahip olma fikri, C ++ 'ın nesneler yığını üzerinde depolamak için varsayılan (işaretçi olmayan) moduyla uyumsuzdur; burada ayrılan alan miktarı doğrudan sınıfa karşılık gelir. Not: bir sınıfın 3 yerine 5 örnek alanı varsa, daha fazla alan ayrılması gerekir.


Bağımsız değişkenleri başvuru ile iletmek için '&' kullanıyorsanız, dolaylı işaretlerin (örn. İşaretçiler) yine de perde arkasında yer aldığını unutmayın. '&' Sadece (1) işaretçi sözdizimini kullanma zahmetini ortadan kaldıran ve (2) derleyicinin daha katı olmasını sağlar (boş göstergeleri yasaklamak gibi).


Hayır, yok olması işaretçileri kullanmak - Eğer başvuruları kullanabilirsiniz. Ve yazdığınızda "işaretçiler sahne arkasında yer alır" - bu anlamsız. gototalimatlar aynı zamanda perde arkasında da kullanılır - hedef makinenin talimatlarında. Bunları hala kullandığımızı iddia etmiyoruz.
einpoklum

@ einpoklum-reinstateMonica Hareket etmek istediğiniz bir dizi nesneniz varsa, her bir öğeyi geçici bir değişkene atayarak ve bu değişken üzerinde bir polimorfik yöntemi çağırırsanız, evet, bir referansı yeniden hatırlayamayacağınız için bir işaretçiye İHTİYACINIZ.
Sildoreth

Türetilmiş bir sınıfa temel sınıf başvurunuz varsa ve bu başvuruda sanal bir yöntem çağırırsanız, türetilmiş sınıfın geçersiz kılınması çağrılır. Yanlış mıyım?
einpoklum

@ einpoklum-reinstateMonica Doğru. Ancak hangi nesneye başvurulduğunu değiştiremezsiniz. Dolayısıyla, bu nesnelerin bir listesi / kümesi / dizisi üzerinde döngü yapıyorsanız, bir referans değişkeni çalışmaz.
Sildoreth

5

Çünkü büyük nesneleri her yere kopyalamak zaman ve hafıza harcıyor.


4
Ve işaretçiler buna nasıl yardımcı olur? Ben Java veya .Net gelen bir kişi yığını ve yığın arasındaki farkı bilmiyor düşünüyorum, bu yüzden bu cevap oldukça işe yaramaz ..
Mats Fredriksson

Referans ile geçebilirsiniz. Bu kopyalamayı engelleyecektir.
Martin York

1
@MatsFredriksson - Büyük bir veri yapısını iletmek (kopyalamak) ve sonucu tekrar kopyalamak yerine, RAM'in neresinde olduğunu işaret edip doğrudan değiştirirsiniz.
John U

Büyük nesneleri kopyalamayın. Kimse bunun için göstergelere ihtiyacınız olduğunu söylemedi.
einpoklum

5

İşte benim anwser, ve uzman olmaya söz vermeyeceğim, ama yazmaya çalıştığım kütüphanelerimden birinde harika işaretçiler buldum. Bu kütüphanede (OpenGL :-) içeren bir grafik API'sıdır), kendilerine aktarılan köşe nesneleriyle bir üçgen oluşturabilirsiniz. Draw yöntemi bu üçgen nesneleri alır ve iyi .. onları oluşturduğum tepe nesnelerine göre çizer. Pekala, sorun değil.

Ancak, bir tepe koordinatını değiştirirsem ne olur? Köşe sınıfında moveX () ile bir şey taşıyabilir mi? Tamam, şimdi üçgeni güncellemeliyim, daha fazla yöntem ekleyerek performans boşa harcanıyor çünkü bir tepe noktası her hareket ettiğinde üçgeni güncellemem gerekiyor. Hala büyük bir anlaşma değil, ama o kadar da büyük değil.

Şimdi, tonlarca köşe ve ton üçgen içeren bir ağım varsa ve ağ dönüyorsa ve hareket ediyorsa vb. Bu köşeleri kullanan her üçgeni ve muhtemelen sahnedeki her üçgeni güncellemem gerekecek çünkü hangilerinin hangi köşeleri kullandığını bilemezdim. Bu son derece bilgisayar yoğun ve eğer bir manzara üzerinde birkaç kafes varsa, oh tanrım! Başım belada, çünkü bu köşeler her zaman değiştiği için her üçgeni neredeyse her kareyi güncelliyorum!

İşaretçilerle, üçgenleri güncellemeniz gerekmez.

Üçgen sınıfı başına üç * Vertex nesnesi olsaydı, sadece yer tasarrufu yapmıyorum, çünkü bir milyon üçgenin kendileri büyük üç köşe nesnesi yok, aynı zamanda bu işaretçiler her zaman amaçladıkları Vertices'i işaret edecek köşelerin ne sıklıkta değiştiği. İşaretçiler hala aynı tepe noktasına işaret ettiğinden, üçgenler değişmez ve güncelleme işleminin gerçekleştirilmesi daha kolaydır. Sizi karıştırırsam, bundan şüphe etmem, uzman gibi davranmıyorum, sadece iki sentimi tartışmaya atıyorum.


4

C dilinde işaretçi ihtiyacı açıklanmaktadır burada

Temel fikir, dildeki birçok sınırlamanın (diziler, dizeler kullanmak ve işlevlerde birden çok değişkeni değiştirmek gibi) verilerin bellek konumlarıyla değiştirilerek kaldırılabileceğidir. Bu sınırlamaların üstesinden gelmek için C'ye işaretçiler getirildi.

Ayrıca, işaretçileri kullanarak, büyük veri türlerini (birçok alanı olan bir yapı gibi) bir işleve geçirdiğiniz durumlarda kodunuzu daha hızlı çalıştırabileceğiniz ve bellekten tasarruf edebileceğiniz de görülmektedir. Geçmeden önce bu tür veri türlerinin bir kopyasını oluşturmak zaman alacak ve bellek tüketecektir. Bu, programcıların büyük veri türleri için işaretçileri tercih etmelerinin bir başka nedenidir.

Not: Örnek kodla ayrıntılı açıklama için lütfen verilen bağlantıya bakın .


Soru C ++ hakkında, bu cevap C hakkında.
einpoklum

hayır, soru c VE c ++ olarak etiketlenir. Belki c etiketi önemsizdir, ancak en başından beri buradadır.
Jean-François Fabre

3

Java ve C # 'da tüm nesne referansları işaretçilerdir, c ++ ile olan şey işaretçi işaretlediğiniz nokta üzerinde daha fazla kontrole sahip olmanızdır. Unutmayın büyük güç ile büyük sorumluluk gelir.


1

İkinci sorunuzla ilgili olarak, genellikle programlama sırasında işaretçiler kullanmanıza gerek yoktur, ancak bunun bir istisnası vardır ve bu, genel bir API yaptığınız zamandır.

C ++ ile ilgili sorun genellikle insanların işaretçileri değiştirmek için kullandıkları, kullandığınız araç setine çok bağımlıdır; bu, kaynak kodu üzerinde ihtiyacınız olan tüm denetime sahip olduğunuzda, ancak visual studio 2008 ile statik bir kitaplık derlerseniz, ve bir visual studio 2010'da kullanmaya çalışın çünkü yeni proje, geriye dönük olarak uyumlu olmayan yeni bir STL sürümü ile bağlantılı olduğu için bir ton bağlayıcı hatası alacaksınız. Bir DLL'yi derlerseniz ve insanların farklı bir araç setinde kullandıkları bir içe aktarma kütüphanesi verirseniz işler daha da kötüleşir, çünkü bu durumda programınız herhangi bir nedenden ötürü er ya da geç çökecektir.

Dolayısıyla, büyük veri kümelerini bir kütüphaneden diğerine taşımak amacıyla, başkalarını kullandığınız aynı araçları kullanmaya zorlamak istemiyorsanız, verileri kopyalaması gereken işleve bir diziye bir işaretçi vermeyi düşünebilirsiniz. . Bunun iyi tarafı, C stili bir dizi olması bile gerekmiyor, bir std :: vector kullanabilir ve örneğin ilk elementin & vector [0] adresini vererek işaretçiyi verebilir ve diziyi dahili olarak yönetmek için std :: vector.

C ++ işaretçiler kullanmak için başka bir iyi neden kitaplıkları ile ilgilidir, program çalışırken yüklenemez bir dll sahip düşünün, bu nedenle bir ithalat kitaplığı kullanırsanız bağımlılık tatmin olmaz ve program çöker. Örneğin, uygulamanızın yanında bir dll'de genel bir API verdiğinizde ve diğer uygulamalardan erişmek istediğinizde bu durum söz konusudur. Bu durumda, API'yi kullanmak için dll'yi konumundan yüklemeniz gerekir (genellikle bir kayıt defteri anahtarındadır) ve daha sonra DLL içindeki işlevleri çağırabilmeniz için bir işlev işaretçisi kullanmanız gerekir. Bazen API'yi yapan kişiler, bu işlemi otomatikleştirmek ve size gereken tüm işlev işaretleyicilerini vermek için yardımcı işlevler içeren bir .h dosyası verecek kadar güzeldir,


1
  • Bazı durumlarda, paylaşılan bir kitaplıktaki (.DLL veya .so) işlevleri kullanmak için işlev işaretçileri gerekir. Bu, çoğu zaman bir DLL arabiriminin sağlandığı diller arasında bir şeyler gerçekleştirmeyi içerir.
  • Derleyici yapımı
  • Fonksiyon işaretçilerinden oluşan bir dizi veya vektör veya dize haritanız olan bilimsel hesap makineleri yapmak mı?
  • Video belleğini doğrudan değiştirmeye çalışmak - kendi grafik paketinizi yapmak
  • API Oluşturma!
  • Veri yapıları - yaptığınız özel ağaçlar için düğüm bağlantı işaretçileri

İşaretçilerin birçok nedeni vardır. Çapraz dil uyumluluğunu korumak istiyorsanız, özellikle C adının karışmasını sağlamak DLL'lerde önemlidir.

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.