İnsanlar C işaretçileri hakkında neyi zor buluyor? [kapalı]


173

Burada yayınlanan soruların sayısından, insanların kafalarını işaretçiler ve işaretçi aritmetiği etrafında döndürürken bazı temel sorunları olduğu açıktır.

Nedenini merak ediyorum. Bana gerçekten büyük problemler vermediler (ilk olarak Neolitik'te öğrendiklerime rağmen). Bu sorulara daha iyi cevaplar yazabilmek için insanların neyi zor bulduğunu bilmek istiyorum.

Yani, işaretçilerle mücadele ediyorsanız ya da yakın zamanda ama birdenbire “anladınız”, size sorunlara neden olan işaretçilerin yönleri nelerdi?


54
Snarky, abartılı, gereksiz yere tartışmalı, bir doğruluk tanesi ile cevap verin: Sakatlandılar - zihinsel olarak sakatlandım, diyorum ki - bunlardan birini önce "etkileyici" üst düzey dillerden biri olarak söyleyerek. Daniel Boone'un tasarladığı Tanrı gibi çıplak metali programlayarak başladılar!
dmckee --- eski moderatör yavru kedi

3
bunun nedeni ... ve Programcı daha iyi olurdu olacak en iyi çabalarına rağmen bir tartışma içine gelişir.
dmckee --- eski moderatör yavru kedi

Bu tartışmacı ve özneldir, N sonuçları üretir, zorum senin kolay. Muhtemelen programcılar üzerinde çalışacaktır, ancak site beta sürümü olmadığı için bu soruları henüz oraya taşımıyoruz.
Sam Saffron

2
@Sam Saffron: Genelde bunun daha fazla programcı olduğunu kabul etsem de, bu tür bir soru, dürüst olmak gerekirse, insanlar "Bence bu kolay" ve "işaretçileri görmekten nefret ediyorum" olarak işaretlemek isteseydi kötü olmazdı. onlar.
jkerian

3
Birisi bunu getirmek zorunda: "Aya işaret eden bir parmak gibi. Parmağınıza konsantre olmayın ya da tüm bu göksel zaferi kaçıracaksınız" - Bruce Lee
mu 1

Yanıtlar:


86

İnsanların cevaplarında biraz fazla derinlere gittiğinden şüpheleniyorum. Zamanlama, gerçek CPU işlemleri veya montaj düzeyi bellek yönetimi hakkında bilgi sahibi olmak gerekmez.

Öğretirken, öğrencilerin en yaygın sorun kaynağı olduğunu anlamalarında aşağıdaki delikleri buldum:

  1. Yığın ve yığın depolama. Genel anlamda bile, kaç kişinin bunu anlamadığı çarpıcıdır.
  2. Yığın kareleri. Yerel değişkenler için yığının özel bir bölümünün genel konsepti, bir 'yığın' nedeni ile birlikte ... dönüş konumunu saklamak, istisna işleyicisi ayrıntıları ve önceki kayıtlar, biri denemeye kadar güvenle bırakılabilir derleyici oluşturmak.
  3. "Bellek bellektir bellektir" Döküm, yalnızca operatörlerin hangi sürümlerini veya derleyicinin belirli bir bellek yığını için ne kadar yer açtığını değiştirir. İnsanlar "ilkel değişken X'in gerçekte ne olduğu" hakkında konuştuğunda bu sorunla uğraştığınızı biliyorsunuz .

Öğrencilerimin çoğu, bir bellek hafızasının basitleştirilmiş bir çizimini, genellikle mevcut kapsamdaki yığının yerel değişkenler bölümünü anlayabildiler. Genellikle çeşitli yerlere açık kurgusal adresler vermek yardımcı oldu.

Özetle, işaretçileri anlamak istiyorsanız, değişkenleri ve aslında modern mimarilerde neler olduğunu anlamak zorunda olduğunuzu söylüyorum.


13
IMHO, yığın ve yığını anlamak, düşük seviyeli CPU detayları kadar gereksizdir. Yığın ve yığın uygulama ayrıntılarıdır. ISO C spesifikasyonunun "yığın" kelimesinden tek bir sözü yoktur ve K&R de yoktur.
sigjuice

4
@sigjuice: İtirazlarınız hem soru hem de cevabın noktasını kaçırıyor. A) K&R C bir anakronizmdir B) ISO C, işaretçilerle tek dil değildir, puanlarım 1 ve 2, C tabanlı olmayan bir dile karşı geliştirildi C) Mimarilerin% 95'i (dilleri değil) öbeği kullanıyor / stack sistemi, istisnaların ona göre açıklanması yeterince yaygındır. D) Sorunun amacı "insanlar neden işaretçileri anlamıyor", "ISO C'yi nasıl
açıklarım

9
@John Marchetti: Daha da ötesi ... "İnsanların işaretçilerle olan sorunu ile ilgili temel sorun nedir?" Sorusu göz önüne alındığında, işaretçiyle ilgili soruları soran insanların "Siz" ile çok etkilendiklerini düşünmüyorum gerçekten bir cevap olarak bilmeye gerek yok. Açıkçası katılmıyorlar. :)
jkerian

3
@jkerian Güncel olmayabilir, ancak K&R'nin işaretçileri açıklayan üç veya dört sayfası, uygulama ayrıntılarına ihtiyaç duymadan bunu yapar. Uygulama ayrıntıları bilgisi çeşitli nedenlerle yararlıdır, ancak IMHO, bir dilin temel yapılarını anlamak için bir ön koşul olmamalıdır.
sigjuice

3
"Genellikle çeşitli yerlere açık kurgusal adresler vermek yardımcı oldu." -> +1
fredoverflow

146

Onlarla çalışmaya ilk başladığımda, sahip olduğum en büyük sorun sözdizimiydi.

int* ip;
int * ip;
int *ip;

hepsi aynı.

fakat:

int* ip1, ip2;  //second one isn't a pointer!
int *ip1, *ip2;

Neden? çünkü bildirimin "işaretçi" kısmı türe değil değişkene aittir.

Ve sonra şeyi silmek için çok benzer bir gösterim kullanılır:

*ip = 4;  //sets the value of the thing pointed to by ip to '4'
x = ip;   //hey, that's not '4'!
x = *ip;  //ahh... there's that '4'

Gerçekten bir işaretçi almanız gerektiğinde ... o zaman bir ve işareti kullanırsınız!

int *ip = &x;

Tutarlılık için Yaşasın!

Daha sonra, görünüşe göre sadece gerizekalı olmak ve ne kadar zeki olduklarını kanıtlamak için, birçok kütüphane geliştiricisi işaretçilerden işaretçilere kadar kullanıyorlar ve eğer bu şeylerden bir dizi beklerse, neden sadece bir işaretçi de vermiyorsunuz? .

void foo(****ipppArr);

Bunu çağırmak için, işaretçiler dizinin ints işaretçiler için dizi adresi gerekir:

foo(&(***ipppArr));

Altı ay içinde, bu kodu korumak zorunda olduğumda, tüm bunların ne anlama geldiğini sıfırdan yeniden yazmaktan daha fazla zaman harcayacağım. (evet, muhtemelen bu sözdizimini yanlış anladım - C'de bir şey yaptığımdan beri bir süre geçti. Biraz özlüyorum, ama sonra biraz mazoşistim)


21
İlki hakkındaki yorumunuz, >> * ip = 4; // ip değerini '4' olarak ayarlar << yanlış. >> // olmalıdır, ip tarafından işaret edilen şeyin değerini '4' olarak ayarlar
aaaa bbbb

8
Çok fazla türün birbiri üzerine istiflenmesi, herhangi bir dilde kötü bir fikirdir. "Foo (& (*** ipppArr));" yazısını bulabilirsiniz. C tuhaf, ama "std :: map <std :: pair <int, int>, std :: pair <std :: vector <int>, std :: tuple <int, double, std :: list C ++ 'daki <int> >>> "de oldukça karmaşıktır. Bu, C ++ 'da C veya STL kaplarındaki işaretçilerin karmaşık olduğu anlamına gelmez. Bu, kodunuzun okuyucusu için anlaşılır olması için daha iyi tür tanımları kullanmanız gerektiği anlamına gelir.
Patrick

20
Sözdiziminin yanlış anlaşılmasının en çok oylanan cevap olduğuna içtenlikle inanamıyorum . Bu işaretçiler hakkında en kolay kısmı.
Jason

4
Bu cevabı okurken bile, bir kağıt almak ve resmi çizmek istedim. C'de hep resim çiziyordum.
Michael Easter

19
@Jason İnsanların çoğunun zor bulduklarından başka hangi nesnel zorluk ölçüsü var?
Rupert Madden-Abbott

52

İşaretçilerin doğru anlaşılması, altta yatan makinenin mimarisi hakkında bilgi gerektirir.

Bugün birçok programcı, makinelerinin nasıl çalıştığını bilmiyor, tıpkı araba kullanmayı bilen çoğu insanın motor hakkında hiçbir şey bilmediği gibi.


18
@dmckee: Yanlış mıyım? Bir segfault ile kaç tane Java programcısı ilgilenebilir?
Robert Harvey

5
Segfaultların çubuk kaydırma ile ilgisi var mı? - Bir Java Programcısı
Tom Anderson

6
@ Robert: Gerçek bir tamamlayıcı demekti. Bu, insanların duygularına zarar vermeden tartışılması zor bir konudur. Korkarım, yorumumdan kaçınmayı düşündüğünüz çatışmayı hızlandırdı. Mea cupla.
dmckee --- eski moderatör yavru kedi

30
Katılmıyorum; işaretçiler elde etmek için temeldeki mimariyi anlamanız gerekmez (yine de bir soyutlamadır).
jason

11
@ Jason: C'de bir işaretçi aslında bir bellek adresidir. Makine mimarisini anlamadan onlarla güvenli bir şekilde çalışmak imkansızdır. Bkz. En.wikipedia.org/wiki/Pointer_(computing ) ve boredzo.org/pointers/#definition
Robert Harvey

42

İşaretçilerle uğraşırken, kafası karışan insanlar iki kamptan birinde bulunurlar. Ben ikisinde de oldum.

array[]kalabalık

Bu, yukarı doğru işaretçi gösteriminden dizi gösterimine nasıl çevrileceğini bilmediği (veya hatta ilişkili olduklarını bile bilmeyen) kalabalık. Bir dizinin öğelerine erişmenin dört yolu şunlardır:

  1. dizi adıyla dizi gösterimi (dizine ekleme)
  2. işaretçi adıyla dizi gösterimi (dizin oluşturma)
  3. işaretçi adıyla işaretçi gösterimi (*)
  4. dizi adıyla işaretçi gösterimi (*)

 

int vals[5] = {10, 20, 30, 40, 50};
int *ptr;
ptr = vals;

array       element            pointer
notation    number     vals    notation

vals[0]     0          10      *(ptr + 0)
ptr[0]                         *(vals + 0)

vals[1]     1          20      *(ptr + 1)
ptr[1]                         *(vals + 1)

vals[2]     2          30      *(ptr + 2)
ptr[2]                         *(vals + 2)

vals[3]     3          40      *(ptr + 3)
ptr[3]                         *(vals + 3)

vals[4]     4          50      *(ptr + 4)
ptr[4]                         *(vals + 4)

Buradaki fikir, dizilere işaretçilerle erişmenin oldukça basit ve basit göründüğü , ancak bir şekilde çok karmaşık ve zeki şeylerin bu şekilde yapılabileceğidir. Bazıları tecrübesiz C / C ++ programcılarını deneyimsiz yeni başlayanlar bırakmaz.

reference to a pointerve pointer to a pointerkalabalık

Bu farkı açıklayan harika bir makale ve alıntı yapacağım ve bazı kodları çalacağım :)

Küçük bir örnek olarak, böyle bir şeyle karşılaşırsanız yazarın ne yapmak istediğini tam olarak görmek çok zor olabilir:

//function prototype
void func(int*& rpInt); // I mean, seriously, int*& ??

int main()
{
  int nvar=2;
  int* pvar=&nvar;
  func(pvar);
  ....
  return 0;
}

Veya, daha az bir ölçüde, böyle bir şey:

//function prototype
void func(int** ppInt);

int main()
{
  int nvar=2;
  int* pvar=&nvar;
  func(&pvar);
  ....
  return 0;
}

Günün sonunda, tüm bu anlamsızlarla gerçekten ne çözüyoruz? Hiçbir şey değil.

Şimdi ptr-to-ptr ve ref-to-ptr sözdizimini gördük. Birinin diğerine göre avantajları var mı? Korkarım hayır. Her ikisinden birinin kullanımı, bazı programcılar için sadece kişisel tercihlerdir. Ref-to-ptr kullanan bazıları sözdiziminin "daha temiz" olduğunu söylerken, ptr-to-ptr kullanan bazı kişiler, ptr-to-ptr sözdizimini kullananların okuduklarınızı daha net hale getirir.

Bu karmaşıklık ve genellikle işaretçilerin bir başka uyarısı ve yeni gelenlerin bir hatası olan referanslarla görünen (cesur görünen) değiştirilebilirlik, işaretçileri anlamayı zorlaştırır. Ayrıca, tamamlama uğruna, referanslara işaretçilerin sizi ilgilendiren kafa karıştırıcı nedenlerden dolayı C ve C ++ 'da yasadışı olduğunu anlamak da önemlidir.lvalue - rvaluesemantik.

Önceki bir cevabın belirttiği gibi, birçok kez sadece kullanarak hotshot programcılarına sahip olacaksınız. ******awesome_var->lol_im_so_clever() ve çoğumuz muhtemelen bu tür zulümleri yazmaktan suçluyuz, ancak bu iyi bir kod değil ve kesinlikle sürdürülebilir değil .

Bu cevabın umduğumdan daha uzun olduğu ortaya çıktı ...


5
Sanırım burada bir C sorusuna bir C ++ yanıtı vermiş olabilirsiniz ... en azından ikinci kısım.
Ekim'de

İşaretçiler için işaretçiler C için de geçerlidir: p
David Titarenco

1
Heh? Ben sadece diziler geçerken işaretçiler için işaretçiler görüyorum - ikinci örnek gerçekten iyi C kodu için geçerli değildir. Ayrıca, C'yi C ++ 'ın karmaşaya sürüklüyorsunuz - C'deki referanslar mevcut değil.
yeni123456

Birçok meşru durumda işaretçilerle işaretçilerle uğraşmak zorundasınız. Örneğin, bir işaretçi döndüren bir işleve işaret eden bir işlev işaretçisi ile uğraşırken. Başka bir örnek: değişken sayıda başka yapıya sahip olabilen bir yapı. Ve daha fazlası ...
David Titarenco

2
"referanslar için işaretçiler C yasadışı" - daha çok "yok" gibi :)
Kos

29

Referans materyallerinin ve öğretimi yapan kişilerin kişisel olarak kalitesini suçluyorum; C'deki (ancak özellikle işaretçilerdeki) çoğu kavram sadece kötü bir şekilde öğretilir . Kendi C kitabımı yazmakla tehdit ediyorum ( Dünyanın İhtiyacı Olan Son Şey C Programlama Dili Üzerine Başka Bir Kitap ), ama bunu yapmak için zamanım ya da sabrım yok. Bu yüzden burada takılıyor ve Standarttan rastgele alıntılar yapıyorum.

Ayrıca, C başlangıçta tasarlandığında, günlük işlerinizde bundan kaçınmanın bir yolu olmadığı için makine mimarisini oldukça ayrıntılı bir seviyede anladığınız varsayıldı (bellek çok sıkı ve işlemciler çok yavaştı) ne yazdığınızı nasıl etkilediğinizi anlamak zorundaydınız).


3
Evet. 'int foo = 5; int * pfoo = & foo; Bunun ne kadar yararlı olduğunu görüyor musunuz? Tamam, ilerliyoruz ... 'Kendi çift bağlantılı liste kütüphanemi yazana kadar gerçekten işaretçiler kullanmadım.
John Lopez

2
+1. CS100 öğrencilerine ders vermek için kullanıyorum ve sorunlarının çoğu anlaşılır bir şekilde işaretçilerin üzerinden geçerek çözüldü.
benzado

1
Tarihsel bağlam için +1. O zamandan sonra başladıktan sonra, bu hiç aklıma gelmedi.
Lumi

26

Joel Spolsky'nin sitesinde - The Perils of JavaSchools'ta işaretçilerin zor olduğu fikrini destekleyen harika bir makale var .

[Feragat - Ben kendi başına bir Java düşmanı değilim .]


2
@ Jason - bu doğrudur ama tartışmayı reddetmez.
Steve Townsend

4
Spolsky, JavaSchools'un insanların işaretçileri zor bulmasının nedeni olduğunu söylemiyor. Bilgisayar Bilimi derecelerine sahip işaretçi-okuma yazma bilmeyen insanlarla sonuçlandıklarını söylüyor.
benzado

1
@benzado - adil nokta - kısa mesajım 'işaretçilerin zor olduğu fikrini destekleyen harika bir makale' okursa geliştirilecektir. Makale, "iyi bir okuldan CS derecesi almanın" bir geliştirici kadar iyi bir başarı öngörücüsü olmadığı, "işaretçileri anladığını" (ve özyinelemenin) hâlâ olduğu anlamına geliyor.
Steve Townsend

1
@Steve Townsend: Bence Bay Spolsky'nin iddiasını kaçırıyorsunuz.
Jason

2
@Steve Townsend: Bay Spolsky, Java okullarının işaretçiler ve özyineleme bilmeyen bir nesil programcı yetiştirdiğini savunuyor, Java okullarının yaygınlığı nedeniyle işaretçilerin zor olmadığını değil. "Bunun neden zor olduğu hakkında harika bir makale var" ve bahsettiğiniz makaleye bağlı olarak belirttiğiniz gibi, ikinci yorumunuz var gibi görünüyor. Eğer yanılıyorsam affet beni.
Jason

24

Eğer "altında" bilgisine dayanmıyorsanız, çoğu şeyi anlamak daha zordur. CS'yi öğrettiğimde, öğrencilerimi hafızasını ondalık kayıtlardan ve ondalık adreslerden oluşan ondalıklı kodlu simüle edilmiş ondalık bir bilgisayar olan çok basit bir "makine" programlamaya başladığımda çok daha kolaylaştı. Örneğin, toplamı elde etmek için bir dizi sayı eklemek için çok kısa programlar koyarlardı. Sonra olanları izlemek için tek adım attılar. "Enter" tuşunu basılı tutup "hızlı" çalışmasını izleyebilirler.

Eminim SO'daki neredeyse herkes neden bu kadar basit olmanın yararlı olduğunu merak ediyor. Nasıl programlanacağını bilmemek nasıl bir şey olduğunu unutuyoruz. Böyle bir oyuncak bilgisayarla oynamak, hesaplamanın adım adım bir süreç olduğu, programları oluşturmak için az sayıda temel ilkeyi kullanan fikirler ve bellek kavramı gibi, programlayamayacağınız kavramları yerleştirir. değişkenlerin sayıların depolandığı yerler olarak değişkenin adresi veya adının içerdiği sayıdan farklı olduğu yerler. Programa girdiğiniz zaman ile "çalıştığı" zaman arasında bir ayrım vardır. Çok basit programlar, sonra döngüler ve alt rutinler, sonra diziler, sonra sıralı G / Ç, sonra işaretçiler ve veri yapısı gibi bir dizi "hız tümseğini" geçmeyi öğrenmeyi seviyorum.

Son olarak, C'ye ulaşırken, K&R onları açıklamak için çok iyi bir iş yapmış olsa da, işaretçiler kafa karıştırıyor. Onları C'de öğrenme şeklim, onları nasıl okuyacağımı bilmekti - sağdan sola. Gördüğüm zaman ki gibi int *pkafamda ben "demek pbir işaret int". C, montaj dilinden bir adım ötede icat edildi ve bu benim hoşuma giden şeydi - o "zemine" yakın. İşaretçiler, diğer her şey gibi, o topraklamaya sahip değilseniz anlamak daha zordur.


1
Bunu öğrenmenin iyi bir yolu 8 bitlik mikrodenetleyicileri programlamaktır. Anlamak kolaydır. Atmel AVR denetleyicilerini alın; gcc tarafından bile desteklenirler.
Xenu

@Xenu: Katılıyorum. Benim için Intel 8008 ve 8051 :-)
Mike Dunlavey

Benim için zamanın karanlık sislerinde MIT'de özel bir 8 bit bilgisayar ("Belki") idi.
QuantumMechanic

Mike - Bir KALP :) Öğrencilerinize almalısınız
QuantumMechanic

1
@Quantum: CARDIAC- iyi olan, bunu duymamıştı. "Belki" - tahmin edeyim, Sussman (et al) Mead-Conway kitabını okuyan ve kendi LSI yongalarını soyduğunda? Orada geçirdiğim zamandan biraz sonra.
Mike Dunlavey

17

K & R'deki açıklamayı okuyana kadar işaret alamadım. Bu noktaya kadar işaretçiler mantıklı gelmedi. İnsanların "İşaretçiler öğrenmeyin, kafa karıştırıcı ve kafanıza zarar verecek ve size anevrizmalar verecek" dedikleri bir sürü şey okudum, bu yüzden uzun süre ondan uzaklaştım ve bu zor kavramın gereksiz havasını yarattım .

Aksi takdirde, çoğunlukla düşündüğüm şey, neden yeryüzünde değerini almak için çemberlerden geçmeniz gereken bir değişken isteyesiniz ve eğer bir şeyler atamak istiyorsanız, değerlerin elde edilmesi için garip şeyler yapmak zorundaydınız bunların içine. Bir değişkenin bütün mesele bir değeri saklamak için bir şey, diye düşündüm, bu yüzden birisi neden karmaşık hale getirmek istedi, benden öteye geçti. "Yani bir işaretçi ile *operatörün değerini almak zorundasınız ??? Bu ne tür bir aptal değişken?", Düşündüm. Anlamsız, cinas yok.

Karmaşık olmasının nedeni, bir işaretçinin bir şeyin adresi olduğunu anlamamış olmamızdı . Bunun bir adres olduğunu, başka bir şeye adres içeren bir şey olduğunu ve bu adresi faydalı şeyler yapmak için manipüle edebileceğinizi açıklarsanız, bence karışıklığı giderebilir.

Bir bilgisayardaki bağlantı noktalarına erişmek / değiştirmek için işaretçiler kullanarak, farklı bellek konumlarını ele almak için işaretçi aritmetiği kullanan ve argümanlarını değiştiren daha karmaşık C koduna bakmayı gerektiren bir sınıf, işaretçilerin beni anlamsız olduğu fikrini devre dışı bıraktı.


4
Gömülü uygulamalarda olduğu gibi çalışmak için sınırlı kaynaklarınız varsa (RAM, ROM, CPU), işaretçiler hızla daha mantıklıdır.
Nick T

Nick'in yorumu için +1 - özellikle geçen yapılar için.
yeni123456

12

İşte bana duraklama veren bir işaretçi / dizi örneği. İki diziniz olduğunu varsayın:

uint8_t source[16] = { /* some initialization values here */ };
uint8_t destination[16];

Amacınız memcpy () kullanarak uint8_t içeriğini kaynak hedeften kopyalamaktır. Aşağıdakilerden hangisinin bu hedefi gerçekleştirdiğini tahmin edin:

memcpy(destination, source, sizeof(source));
memcpy(&destination, source, sizeof(source));
memcpy(&destination[0], source, sizeof(source));
memcpy(destination, &source, sizeof(source));
memcpy(&destination, &source, sizeof(source));
memcpy(&destination[0], &source, sizeof(source));
memcpy(destination, &source[0], sizeof(source));
memcpy(&destination, &source[0], sizeof(source));
memcpy(&destination[0], &source[0], sizeof(source));

Cevap (Spoiler Uyarısı!) TÜMÜ. "hedef", "& hedef" ve "& hedef [0]" aynı değerlerdir. "& destination" farklı bir tür diğer ikisinden , ancak yine de aynı değer. Aynı şey "kaynak" permütasyonları için de geçerlidir.

Bir yana, ben şahsen ilk versiyonu tercih ediyorum.


İlk sürümü de tercih ediyorum (daha az noktalama işareti).
sigjuice

++ Ben de öyle, ama gerçekten dikkatli olmalısınız sizeof(source), çünkü eğer sourcebir işaretçi ise, sizeofistediğiniz şey olmayacaktır. Bazen (her zaman değil) sizeof(source[0]) * number_of_elements_of_sourcesadece bu hatadan uzak durmak için yazıyorum .
Mike Dunlavey

hedef ve hedef ve hedef [0] aynı değildir - ancak her biri farklı bir mekanizma aracılığıyla memcpy'da kullanıldığında aynı boşluğa * dönüştürülür. Bununla birlikte, sizeof argümanı olarak kullanıldığında, iki farklı sonuç elde edersiniz ve üç farklı sonuç mümkündür.
gnasher729

Operatörün adresinin gerekli olduğunu düşündüm?
MarcusJ

7

C ve C ++ 'nın öğrendiğim ilk programlama dilleri olduğunu söyleyerek başlamalıyım. C ile başladım, sonra okulda C ++ yaptım ve sonra akıcı olmak için C'ye geri döndüm.

C öğrenirken işaretçiler hakkında beni şaşırtan ilk şey basitti:

char ch;
char str[100];
scanf("%c %s", &ch, str);

Bu karışıklık çoğunlukla, işaretçiler bana düzgün bir şekilde tanıtılmadan önce OUT bağımsız değişkenleri için bir değişkene başvuru kullanılarak tanıtılmasından kaynaklanıyordu. Dummies için C'deki ilk birkaç örneği yazdığımın atlandığını hatırlıyorum çünkü sadece yazdığım ilk programı asla işe yaramayacak kadar basitti (büyük olasılıkla bu nedenle).

Bu konuda kafa karıştırıcı olan şey, &chaslında ne demek olduğu gibistr ihtiyaç duyulmadığıydı.

Bunu öğrendikten sonra, dinamik ayırma konusunda kafanız karıştığını hatırlıyorum. Bir noktada verilere işaret eden bir tür dinamik ayırma olmadan son derece yararlı olmadığını fark ettim, bu yüzden şöyle bir şey yazdım:

char * x = NULL;
if (y) {
     char z[100];
     x = z;
}

dinamik olarak yer ayırmaya çalışmak. İşe yaramadı. İşe yarayacağından emin değildim, ama başka nasıl çalışabileceğini bilmiyordum.

Daha sonra öğrendim mallocve newgerçekten büyülü hafıza jeneratörleri gibi görünüyordu. Nasıl çalışacakları hakkında hiçbir şey bilmiyordum.

Bir süre sonra tekrar tekrar öğretiliyordum (daha önce kendi başıma öğrenmiştim, ama şimdi sınıftaydım) ve kaputun altında nasıl çalıştığını sordum - ayrı değişkenler nerede saklandı. Profesörüm "yığın üzerinde" dedi ve bana birçok şey netleşti. Bu terimi daha önce duymuştum ve daha önce yazılım yığınları uygulamıştım. Başkalarının çok önce "yığın" a başvurduklarını duymuştum, ama unutmuştum.

Bu süre zarfında, C'de çok boyutlu diziler kullanmanın çok kafa karıştırıcı olabileceğini de fark ettim. Nasıl çalıştıklarını biliyordum, ama karışmak o kadar kolaydı ki yapabildiğim her zaman onları kullanmaya çalıştım. Buradaki konunun çoğunlukla sözdizimsel olduğunu düşünüyorum (özellikle bunları işlevlerden geçirme veya işlevlerden döndürme).

Önümüzdeki yıl veya iki yıl için okul için C ++ yazdığımdan beri veri yapıları için işaretçiler kullanma konusunda çok fazla deneyimim oldu. Burada yeni bir sorunum vardı - işaretçileri karıştırmak. Ben işaretçiler (şeyler gibi node ***ptr;) birden fazla düzeyde bana gezi olurdu . Bir işaretçiyi yanlış sayıda dereference ve sonunda *deneme yanılma yoluyla kaç tane gerekli bulmak için başvurmak .

Bir noktada bir programın yığınının nasıl çalıştığını öğrendim (bir çeşit, ama artık geceleri beni tutmayacak kadar iyi). mallocBelli bir sistemde dönen işaretçiden önce birkaç bayt bakarsanız , ne kadar verinin tahsis edildiğini görebileceğinizi hatırlıyorum . Kodun mallocişletim sisteminden daha fazla bellek isteyebileceğini fark ettim ve bu bellek çalıştırılabilir dosyalarımın bir parçası değildi. Nasıl mallocçalıştığı konusunda iyi bir çalışma fikrine sahip olmak gerçekten yararlıdır.

Bundan kısa bir süre sonra, çoğu programcının muhtemelen düşündüğü kadar bana işaretçiler hakkında bilgi vermeyen bir montaj dersi aldım. Kodumun hangi meclise çevrilebileceği hakkında daha fazla düşünmemi sağladı. Her zaman verimli kod yazmaya çalışmıştım, ama şimdi nasıl yapılacağı konusunda daha iyi bir fikrim vardı.

Ayrıca biraz lisp yazmak zorunda kaldığım birkaç ders aldım . Lisp yazarken ben C olduğu gibi verimlilik ile ilgili değildi ben derlenmiş ise bu kod ne dönüştürülebilir çok az bir fikrim vardı, ama yapılan yerel adlandırılmış semboller (değişkenler) bir sürü kullanarak gibi görünüyordu biliyordum işler çok daha kolay. Bir noktada işaretçi sorunları nedeniyle C ++ ile yazmakta çok zorlandığımı biraz lisp içinde bazı AVL ağacı döndürme kodu yazdım. Yerel değişkenlerin fazlalığı olduğunu düşündüğüm şeyden kaçınmamın, bunu ve C ++ 'daki diğer birçok programı yazmamı engellediğini fark ettim.

Ayrıca derleyici dersi aldım. Bu sınıfta ileri materyale gelip statik tekli atama (SSA) ve ölü değişkenler hakkında bilgi edindim, bu da önemli değil, herhangi bir derleyicinin değişkenlerle uğraşmak için iyi bir iş yapacağını öğretmesi önemli değil. artık kullanılmıyor. Doğru türlere ve iyi isimlere sahip daha fazla değişkenin (işaretçiler dahil), işleri kafamda düz tutmama yardımcı olacağını zaten biliyordum, ancak şimdi verimlilik nedenleriyle bunlardan kaçınmanın daha az mikro optimizasyon fikirli profesörlerimden daha aptal olduğunu biliyordum. ben mi.

Bu yüzden benim için, bir programın bellek düzeni hakkında iyi bir şey bilmek çok yardımcı oldu. Kodumun hem sembolik olarak hem de donanımda ne anlama geldiğini düşünmek bana yardımcı oluyor. Doğru türde yerel işaretçiler kullanmak çok yardımcı olur. Sıklıkla benzeyen bir kod yazıyorum:

int foo(struct frog * f, int x, int y) {
    struct leg * g = f->left_leg;
    struct toe * t = g->big_toe;
    process(t);

bir işaretçi türünü berbat edersem derleyici hatasıyla sorunun ne olduğu çok açık olur. Yapsaydım:

int foo(struct frog * f, int x, int y) {
    process(f->left_leg->big_toe);

ve orada herhangi bir işaretçi türü yanlış var, derleyici hatası bulmak çok daha zor olurdu. Hayal kırıklığımdaki deneme yanılma değişikliklerine başvurmak ve muhtemelen işleri daha da kötüleştirmek cazip olurdu.


1
+1. Kapsamlı ve anlayışlı. Scanf'i unutmuştum, ama şimdi onu büyüttüğüne göre, aynı karışıklığa sahip olduğumu hatırlıyorum.
Joe White

6

Geriye dönüp baktığımda, nihayetinde işaretçileri anlamama yardımcı olan dört şey vardı. Bundan önce onları kullanabilirdim, ama tam olarak anlamadım. Yani, formları takip edersem, istediğim sonuçları elde edeceğimi biliyordum, ama formların 'nedenini' tam olarak anlamadım. Bunun tam olarak sorduğunuz şey olmadığını anlıyorum, ama bence yararlı bir sonuç.

  1. Bir tamsayıyı gösteren ve tamsayıyı değiştiren bir rutin yazma. Bu bana işaretçilerin nasıl çalıştığına dair herhangi bir zihinsel model oluşturmak için gerekli formları verdi.

  2. Tek boyutlu dinamik bellek tahsisi. 1-B bellek ayırmayı anlamak işaretçinin kavramını anlamamı sağladı.

  3. İki boyutlu dinamik bellek tahsisi. İki boyutlu bellek tahsisini anlamak bu kavramı güçlendirdi, fakat aynı zamanda bana işaretçinin kendisinin depolanması gerektiğini ve dikkate alınması gerektiğini öğretti.

  4. Yığın değişkenleri, genel değişkenler ve yığın bellek arasındaki farklar. Bu farklılıkları bulmak bana işaretçilerin işaret ettiği / yönlendirdiği bellek türlerini öğretti.

Bu öğelerin her biri, daha düşük bir seviyede neler olduğunu hayal etmeyi gerektiriyordu - atmayı düşünebileceğim her vakayı tatmin eden bir zihinsel model inşa etmek. Zaman ve çaba aldı, ama buna değdi. İşaretçileri anlamak için bu zihinsel modeli nasıl çalıştıkları ve nasıl uygulandıkları üzerine inşa etmeniz gerektiğine inanıyorum.

Şimdi orijinal sorunuza geri dönün. Önceki listeye dayanarak, başlangıçta kavramakta zorlandığım birkaç öğe vardı.

  1. Nasıl ve neden bir işaretçi kullanılır?
  2. Nasıl farklıdırlar ve dizilere benzerler.
  3. İşaretçi bilgilerinin nerede saklandığını anlama.
  4. İşaretçinin neyi ve nerede olduğunu anlama.

Hey, beni cevabında tarif ettiğin benzer bir şekilde öğrenebileceğim bir makaleye / kitaba / çizimine / doodle'a / şeye yönlendirebilir misin? İyi öğrenirken bunun temelde her şey için yolun olduğuna inanıyorum. Derin anlayış ve iyi zihinsel modeller
Alexander Starbuck

1
@AlexStarbuck - Bunun kulağa hoş gelmesi anlamına gelmiyor, ama bilimsel yöntem harika bir araç. Kendinize belirli bir senaryo için neler olabileceğini düşündüğünüz hakkında bir resim çizin. Test etmek için bir şey programlayın ve ne elde ettiğinizi analiz edin. Beklediğinizle eşleşti mi? Değilse, nerede farklı olduğunu belirleyin? Gerektiği kadar tekrarlayın, hem anlayışınızı hem de zihinsel modellerinizi test etmek için karmaşıklığı kademeli olarak artırın.
Sparky

6

C'deki bazı telefon programları üzerinde çalışan "işaretçi anım" vardı, sadece klasik C'yi anlayan bir protokol analizörü kullanarak bir AXE10 değişim emülatörü yazmak zorunda kaldım. Onlar olmadan kodumu yazmaya çalıştım (hey, "ön işaretçi" beni biraz gevşek kesilmiş) ve tamamen başarısız oldu.

Onları anlamanın anahtarı benim için & (adres) operatörü idi. Bunun &i"i adresi" *ianlamına geldiğini anladıktan sonra "i tarafından işaret edilen adresin içeriği" anlamına gelen anlayış biraz sonra geldi. Kodumu her yazdığımda veya okuduğumda hep "&" ne anlama geldiğini ve "*" ne anlama geldiğini tekrarladım ve sonunda bunları sezgisel olarak kullanmaya geldim.

Utanç için, VB ve sonra Java'ya zorlandım, böylece işaretçi bilgim bir zamanlar kadar keskin değil, ama "post-pointer" olduğum için mutluyum. Yine de * * p'yi anlamamı gerektiren bir kütüphane kullanmamı istemeyin .


Eğer &iadresi ve *iiçeriği ise, nedir i?
Thomas Ahle

2
Ben i kullanımını aşırı yüklüyorum. Rasgele değişken i için & i, "i adresi" anlamına gelir, kendi başına "i & i" nin içeriği anlamına gelir ve * i "& i" nin içeriğini adres olarak ele alır, bu adrese gider ve içerikler".
Gary Rowe

5

İşaretçilerle ilgili en büyük zorluk, en azından bana göre, C ile başlamamıştım. Java ile başladım. İşaretçilerin tüm fikri, üniversitede C'yi bilmem gereken birkaç sınıfa kadar gerçekten yabancıydı. O zaman kendime C'nin temellerini ve temel anlamında işaretçileri nasıl kullanacağımı öğrettim. O zaman bile, kendimi C kodunu okurken bulduğumda, işaretçi sözdizimini aramak zorundayım.

Bu yüzden çok sınırlı deneyimimde (1 yıl gerçek dünya + üniversitede 4 yıl), işaretçiler beni karıştırıyor çünkü asla bir sınıf ortamından başka bir şeyde kullanmak zorunda kalmadım. Ve şimdi CS'ye C veya C ++ yerine JAVA ile başlayan öğrencilerle sempati duyabilirim. Söylediğiniz gibi, 'Neolitik' çağda işaretçiler öğrendiniz ve muhtemelen o zamandan beri kullanıyorsunuz. Bizim için daha yeni insanlar için, bellek ayırma ve işaretçi aritmetiği yapma fikri gerçekten yabancıdır, çünkü tüm bu diller bunu soyutladı.

PS Spolsky makalesini okuduktan sonra 'JavaSchools' açıklaması Cornell'deki üniversitede yaşadığım gibi değildi ('05 -'09). Java'da öğretilmeyen diğer sınıfları ve işlevsel programları (sml), işletim sistemlerini (C), algoritmaları (kalem ve kağıt) aldım. Ancak tüm intro sınıfları ve seçmeli derslerin tümü java'da yapıldı, çünkü işaretçilerle bir karma tablo uygulamaktan daha yüksek bir şey yapmaya çalıştığınızda tekerleği yeniden icat etmemenin bir değeri var.


4
Dürüst olmak gerekirse, hala işaretçilerle ilgili zorluklarınız olduğu için, Cornell'deki deneyiminizin Joel'in makalesiyle büyük ölçüde çeliştiğinden emin değilim. Açıkçası beyninizin yeterli bir noktası, onu ifade etmek için bir Java zihniyeti ile bağlanmış.
jkerian

5
Wat? Java'daki referanslar (veya C # veya Python veya muhtemelen düzinelerce başka dil) aritmetik olmayan yalnızca işaretçilerdir. void foo(Clazz obj) { obj = new Clazz(); }void bar(Clazz obj) { obj.quux = new Quux(); }

1
Java'da hangi referansların olduğunu biliyorum, ancak sadece Java'da yansıma yapmamı veya CI'de anlamlı bir şey yazmamı istediyseniz, sadece bunu çözemeyeceğini söylüyorum. Her seferinde ilk kez öğrenmek gibi birçok araştırma gerektirir.
shoebox639

1
C'de akıcı olmadan C'deki bir işletim sistemleri sınıfından nasıl geçiyorsunuz? Hiçbir suç amaçlanmamıştır, sadece basit bir işletim sistemini sıfırdan geliştirmek zorunda olduğumu hatırlıyorum. Binlerce kez işaretçi kullanmalıydım ...
Yerçekimi

5

İşte bir cevap olmayan: Bunu anlamak için cdecl (veya c ++ decl) kullanın:

eisbaw@leno:~$ cdecl explain 'int (*(*foo)(const void *))[3]'
declare foo as pointer to function (pointer to const void) returning pointer to array 3 of int

4

Sözdiziminde önemli bir değişiklik yapmadan koda ekstra bir boyut eklerler. Bunun hakkında düşün:

int a;
a = 5

Değişime tek bir şey vardır: a. Yazabilirsiniz a = 6ve sonuçlar çoğu insan için açıktır. Ama şimdi düşünün:

int *a;
a = &some_int;

Bununla ailgili farklı zamanlarda ilgili iki şey vardır : gerçek değeri a, işaretçiyi ve işaretçinin "arkasında" değeri. Şunları değiştirebilirsiniz a:

a = &some_other_int;

... ve some_inthala aynı değere sahip bir yerdedir. Ancak, işaret ettiği şeyi de değiştirebilirsiniz:

*a = 6;

a = 6Sadece yerel yan etkileri olan ve *a = 6başka yerlerde bir sürü başka şeyi etkileyebilecek arasında kavramsal bir boşluk var . Demek istediğim burada değil dolaylama kavramı doğal olarak zor olduğunu, ancak bunu çünkü o hem birlikte derhal, yerel şeyi aveya dolaylı şey *a... bu insanların kafasını karıştıran şey olabilir.


4

2 yıl gibi c ++ programlanmış ve sonra Java (5 yıl) dönüştürülmüş ve asla geri baktım. Ancak, son zamanlarda bazı yerli şeyler kullanmak zorunda kaldım, işaretçiler hakkında bir şey unutmadım (hatta şaşkınlık) ve ben bile onları kullanmak kolay bulmak öğrendim. Bu, 7 yıl önce konsepti ilk kavramaya çalıştığımda yaşadığımın keskin bir tezatıydı. Yani, sanırım anlama ve beğenme programlama olgunluğu meselesidir? :)

VEYA

İşaretçiler bisiklete binmek gibidir, onlarla nasıl çalışacağınızı anladığınızda, bunu unutmazsınız.

Sonuçta, kavramak ya da kavramak zor, tüm işaretçi fikri ÇOK eğitici ve işaretçilerle bir dilde programlama yapsa da yapmasa da her programcı tarafından anlaşılması gerektiğine inanıyorum.


3

Dolaylı işaretleme nedeniyle işaretçiler zordur.


"Bilgisayar bilimlerinde bir daha fazla dolaylılık seviyesi ile çözülemeyen bir sorun olmadığı söyleniyor" (ilk kim söylese de hiçbir fikrim yok)
Arketipik Paul

Büyü gibi, yanlış yönlendirmenin insanları karıştırdığı şey (ama tamamen harika)
Nick T

3

İşaretçiler (düşük seviyeli çalışmanın diğer bazı yönleriyle birlikte), kullanıcının sihri almasını gerektirir.

Çoğu üst düzey programcı büyüyü sever.


3

İşaretçiler, bir nesneye tanıtıcı ve bir nesnenin kendisi arasındaki farkla başa çıkmanın bir yoludur. (tamam, ille de itiraz değil, ama ne demek istediğimi ve zihnimin nerede olduğunu biliyorsun)

Bir noktada, muhtemelen ikisi arasındaki farkla uğraşmak zorundasınız. Modern, üst düzey dilde bu, değere göre kopyala ve referansla kopyala arasındaki ayrım haline gelir. Her iki durumda da, programcıların kavraması genellikle zor bir kavramdır.

Bununla birlikte, belirtildiği gibi, bu problemi C'de ele almak için sözdizimi çirkin, tutarsız ve kafa karıştırıcıdır. Sonunda, gerçekten anlamaya çalışırsanız, bir işaretçi mantıklı olacaktır. Ancak, işaretçilerle işaretçilerle uğraşmaya başladığınızda ve böylece reklam müzesinde, hem benim hem de diğer insanlar için gerçekten kafa karıştırıcı oluyor.

İşaretçiler hakkında hatırlanması gereken bir diğer önemli şey de tehlikeli olmalarıdır . C, usta bir programcının dilidir. Ne halt yaptığını bildiğinizi varsayar ve böylece işleri gerçekten mahvetme gücü verir. Bazı program türlerinin hala C dilinde yazılması gerekirken, çoğu program yazmaz ve bir nesne ile onun kolu arasındaki fark için daha iyi bir soyutlama sağlayan bir diliniz varsa, onu kullanmanızı öneririm.

Aslında, birçok modern C ++ uygulamasında, genellikle gerekli herhangi bir işaretçi aritmetiğinin kapsüllenmesi ve soyutlanması söz konusudur. Geliştiricilerin her yerde işaretçi aritmetiği yapmasını istemiyoruz. İşaretçi aritmetiği en düşük seviyede yapan merkezi, iyi test edilmiş bir API istiyoruz. Bu kodda değişiklik yapılması büyük özen ve kapsamlı testlerle yapılmalıdır.


3

Bence C göstergelerinin zor olmasının bir nedeni, gerçekten eşdeğer olmayan çeşitli kavramları birleştirmeleri; yine de, hepsi işaretçiler kullanılarak uygulandığından insanlar kavramları çözmekte zorlanabilirler.

C'de işaretçiler başka şeylerin yanı sıra kullanılır:

  • Özyinelemeli veri yapılarını tanımlama

C'de, bunun gibi bir tamsayı listesi tanımlarsınız:

struct node {
  int value;
  struct node* next;
}

İşaretçi sadece oradadır, çünkü kavramın bellek adresleri gibi düşük düzeyli bir ayrıntıyla gerçekten ilgisi olmadığında, C'de özyinelemeli bir veri yapısı tanımlamanın tek yolu budur. Haskell'de işaretçi kullanımını gerektirmeyen aşağıdaki eşdeğeri düşünün:

data List = List Int List | Null

Oldukça basit - bir liste boştur veya bir değerden ve listenin geri kalanından oluşur.

  • Dizgiler ve diziler üzerinde yineleme

fooC'deki bir dizenin her karakterine bir işlevi nasıl uygulayabileceğiniz aşağıda açıklanmıştır :

char *c;
for (c = "hello, world!"; *c != '\0'; c++) { foo(c); }

Bir işaretçiyi yineleyici olarak kullanmasına rağmen, bu örneğin öncekiyle çok az ortak noktası vardır. Artırabileceğiniz bir yineleyici oluşturmak, özyinelemeli bir veri yapısı tanımlamaktan farklı bir kavramdır. Her iki kavram da özellikle bir bellek adresi fikrine bağlı değildir.

  • Çok biçimlilik elde edin

İşte glib'de bulunan gerçek bir işlev imzası :

typedef struct g_list GList;

void  g_list_foreach    (GList *list,
                 void (*func)(void *data, void *user_data),
                         void* user_data);

Oha! Bu oldukça ağız dolusu void*. Ve her şey, her üyeye bir işlev uygulayarak, her türlü şeyi içerebilecek bir liste üzerinde yinelenen bir işlevi bildirmek. Şekliyle karşılaştırmak mapHaskell buyuruldu ki:

map::(a->b)->[a]->[b]

Bu daha da basittir: mapBir dönüştüren bir işlevi alan bir fonksiyondur abir etmek bve bir liste uygular a'listesini elde etmek lar bs'. Sadece C işlevinde gibi g_list_foreach, mapo uygulanacağı için türleri hakkında kendi tanımını şey bilmek gerekmez.

Sonuç olarak:

Eğer insanlar özyinelemeli veri yapıları, yineleyiciler, polimorfizm, vb. Hakkında ayrı kavramlar olarak ilk öğrendiyse ve daha sonra işaretleyicilerin tüm bunları ezmek yerine C'de bu fikirleri uygulamak için nasıl kullanılabileceğini öğrendiysem C işaretçileri çok daha az kafa karıştırıcı olurdu. kavramları birlikte tek bir "işaretçi" konusu haline getirir.


c != NULL"Merhaba dünya" örneğinizde yanlış kullanım ... yani *c != '\0'.
Olaf Seibert

2

Bazı makine kodlarına, montajlara ve RAM'deki öğeleri ve veri yapısını nasıl temsil edeceğiyle, muhtemelen makine seviyesinden sağlam bir temel gerektirdiğini düşünüyorum. Biraz zaman, biraz ev ödevi veya problem çözme uygulaması ve biraz düşünmek gerekir.

Ama eğer bir kişi başlangıçta yüksek seviyeli diller biliyorsa (bu yanlış bir şey değildir - bir marangoz balta kullanır. Atomu bölmesi gereken bir kişi başka bir şey kullanır. Marangoz olan insanlara ihtiyacımız var ve atom okuyan insanlara sahibiz) ve Üst düzey dili bilen bu kişiye işaretçiler için 2 dakikalık bir giriş verilir ve daha sonra işaretçi aritmetiği, işaretçileri işaretleyicilere, değişken boyutlu dizelere işaretçi dizisini ve karakter dizisini vb. anlamasını beklemek zordur. Düşük seviyeli sağlam bir temel çok yardımcı olabilir.


2
Toplama işaretçileri, makine kodu veya montajının anlaşılmasını gerektirmez.
jason

Gerek, hayır. Ancak montajı anlayan insanlar, gerekli zihinsel bağlantıların çoğunu (hepsi olmasa da) zaten yapmış oldukları için büyük olasılıkla çok daha kolay işaretçiler bulacaklardır.
cHao

2

Her zaman sahip olduğum sorun (öncelikle kendi kendini öğretti) bir işaretçi kullanmak için "ne zaman". Bir işaretçi oluşturmak için başımı sözdiziminin etrafına sarabilirim, ancak hangi koşullarda bir işaretçinin kullanılması gerektiğini bilmem gerekir.

Bu zihniyete sahip olan tek kişi ben miyim? ;-)


Anladım. Benim cevabım bununla ilgileniyor.
J. Polfer

2

Bir zamanlar ... 8 bitlik mikroişlemcilerimiz vardı ve herkes montajda yazdı. İşlemcilerin çoğu, atlama tabloları ve çekirdekler için kullanılan bir tür dolaylı adresleme içeriyordu. Daha üst düzey diller ortaya çıktığında, ince bir soyutlama katmanı ekliyoruz ve onlara işaretçiler diyoruz. Yıllar geçtikçe donanımdan gittikçe uzaklaştık. Bu mutlaka kötü bir şey değil. Bir sebepten dolayı bunlara üst düzey diller denir. Nasıl yapıldığının ayrıntıları yerine ne yapmak istediğime daha fazla konsantre olabilirim.


2

Birçok öğrencinin dolaylılık kavramı ile, özellikle de dolaylılık kavramını ilk kez karşıladıklarında bir sorunu olduğu görülmektedir. Bir öğrenciyken, dersimin +100 öğrencisinden sadece birkaç kişinin gerçekten işaretçileri anladığını hatırlıyorum.

Dolaylılık kavramı, gerçek hayatta sıklıkla kullandığımız bir şey değildir ve bu nedenle başlangıçta kavramak zor bir kavramdır.


2

Son zamanlarda sadece işaretçi tıklama anı yaşadım ve kafa karıştırıcı bulduğuma şaşırdım. Herkesin bundan çok bahsettiği daha çok şeydi, biraz karanlık büyünün sürdüğünü varsayıyordum.

Anlama şeklim buydu. Tüm tanımlanmış değişkenlere derleme zamanında (yığın üzerinde) bellek alanı verildiğini düşünün. Ses veya görüntü gibi büyük veri dosyalarını işleyebilecek bir program istiyorsanız, bu potansiyel yapılar için sabit miktarda bellek istemezsiniz. Bu nedenle, bu verileri tutmak için belirli bir miktarda bellek atamak için çalışma zamanına kadar beklersiniz (yığınta).

Verilerinizi belleğe aldıktan sonra, üzerinde her işlem yapmak istediğinizde bu verileri bellek veri yolunuzun her yerine kopyalamak istemezsiniz. Resim verilerinize filtre uygulamak istediğinizi varsayalım. Görüntüye atadığınız verilerin önünden başlayan bir işaretçiniz var ve bu veriler üzerinde çalışan ve yerine değişen bir işlev var. Ne yaptığımızı bilmeseydin, büyük olasılıkla işlem boyunca veriyi çoğaltırsın.

En azından şu an bunu görüyorum!


Bir düşünce olarak, görüntüleri / ses / videoyu tutmak için sabit bir bellek tanımlayabilirsiniz, örneğin bellek sınırlı bir cihazda, ancak daha sonra bellek çözümünün içine ve dışına bir tür akışla uğraşmanız gerekir.
Chris Barry

1

Burada bir C ++ acemi olarak konuşmak:

İşaretçi sistemi, kavramdan değil, Java'ya göre C ++ sözdiziminden dolayı sindirmem için biraz zaman aldı. Kafa karıştırıcı bulduğum birkaç şey:

(1) Değişken beyanı:

A a(1);

vs.

A a = A(1);

vs.

A* a = new A(1); 

ve görünüşe göre

A a(); 

değişken bir bildirim değil, bir işlev bildirimidir. Diğer dillerde, bir değişkeni bildirmenin sadece bir yolu vardır.

(2) Ve işareti birkaç farklı şekilde kullanılır. Öyleyse

int* i = &a;

o zaman & a bir bellek adresidir.

OTOH, eğer öyleyse

void f(int &a) {}

bu durumda & a, referans by-pass parametresidir.

Bu önemsiz görünse de, yeni kullanıcılar için kafa karıştırıcı olabilir - Java'dan geldim ve Java, operatörlerin daha tek tip kullanımıyla bir dil

(3) Dizi-işaretçi ilişkisi

Anlamak biraz sinir bozucu olan bir şey, bir işaretçi

int* i

int için bir işaretçi olabilir

int *i = &n; // 

veya

int için bir dizi olabilir

int* i = new int[5];

Ve sonra sadece işleri daha karışık hale getirmek için, işaretçiler ve dizi her durumda değiştirilemez ve işaretçiler dizi parametreleri olarak geçirilemez.

Bu, C / C ++ ve onun IMO olan işaretçileriyle yaşadığım bazı temel hayal kırıklıklarını özetlemektedir.


C ++ 2011, değişken beyanlar söz konusu olduğunda işleri biraz geliştirmiştir.
gnasher729

0

Post mezuniyetimden sonra ve ilk işimden sonra bile işaretçiyi şahsen anlamadım. Bildiğim tek şey, bağlantılı liste, ikili ağaçlar ve dizileri işlevlere geçirmek için ihtiyacınız olması. İlk işimde bile durum buydu. Sadece röportaj vermeye başladığımda, işaretçi kavramının derin olduğunu ve muazzam bir kullanım ve potansiyele sahip olduğunu anlıyorum. Sonra K & R okumaya ve kendi test programını yazmaya başladım. Tüm amacım iş odaklıydı.
Şu anda, eğer iyi bir şekilde öğretilirlerse, işaretçilerin gerçekten kötü veya zor olmadığını buldum. Ne yazık ki mezuniyette C öğrendiğimde, dışarıdaki öğretmen işaretçinin farkında değildi ve ödevler bile daha az işaretçi kullanıyordu. Lisansüstü düzeyde işaretçinin kullanımı gerçekten sadece ikili ağaçlar ve bağlantılı liste oluşturmak kadar. Onlarla çalışmak için işaretçilerin doğru anlaşılmasına ihtiyaç duymadığınız bu düşünce, onları öğrenme fikrini öldürür.


0

İşaretçiler .. hah .. kafamdaki işaretçi ile ilgili her şey, referansı ne olursa olsun gerçek değerlerinin bulunduğu bir bellek adresi vermesidir. işaretçiler nasıl çalışır .. hadi beyler ... Java'da bile her şey bir referanstır ..


0

Asıl sorun insanlar neden işaretçilere ihtiyaç duyduklarını anlamıyor. Çünkü yığın ve yığın konusunda net değiller. Küçük bellek moduyla x86 için 16bit derleyiciden başlamak iyidir. Birçok kişinin yığın, yığın ve "adres" hakkında fikir edinmesine yardımcı oldu. Ve bayt :) Modern programcılar bazen size 32 bit alana hitap etmek için kaç bayt gerektiğini söyleyemez. İşaretçiler hakkında nasıl fikir sahibi olabilirler?

İkinci an gösterim: işaretçiyi * olarak bildirirsiniz, adresi & olarak alırsınız ve bu bazı insanlar için anlaşılması kolay değildir.

Ve son gördüğüm şey depolama problemiydi: yığın ve yığını anlıyorlar ama "statik" fikrine giremiyorlar.

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.