Bir fonksiyona geçirilen yapı kopyalanır. Bu, iki int'lik bir nokta için sorunsuz, 200 baytlık bir kayıt için israftır - ve değiştirmeyi imkânsız kılar, çünkü fonksiyon yalnızca kendi kopyasını görür. Bir yapıyı göstermek her iki sorunu da aynı anda çözer ve büyüyen yapılar kurmanın da tek yoludur: listeler, ağaçlar, çizgeler.
Bir Yapıya Gösterici Almak
Özel bir şey gerekmez. & size adresi verir ve tip "yapıya gösterici"dir:
Üçü de 3 yazdırır. Son ikisi iki farklı şekilde yazılmış aynı işlemdir.
(*ptr).x Neden Parantezlerine İhtiyaç Duyar
Onları atıp *ptr.x yazmayı deneyebilirsiniz. Bu oldukça farklı bir şeye derlenir ya da daha sık olarak hiç derlenmeyi reddeder - çünkü üye operatörü ., dereferans *'dan daha yüksek önceliğe sahiptir:
*ptr.x // *(ptr.x) diye ayrıştırılır -- yanlış
(*ptr).x // önce dereferans, sonra üye -- doğru
ptr->x // aynı şey, kısaca söylenmiş
ptr.x, göstericinin bir üyesini ister ve bir göstericinin üyesi yoktur. Derleyicinin şikâyeti ("request for member 'x' in something not a structure or union") kılık değiştirmiş bir öncelik hatasıdır.
O parantezli biçim çirkin ve yanlış yapılması kolay olduğu için C, -> sağlar. Pratikte gerçek kodda (*p).x'i neredeyse hiç görmeyeceksiniz; p->x'e uzanın ve uzun biçimin var olduğunu, yalnızca okun ne anlama geldiğinin açıklaması dışında, unutun.
Bir Yapıyı Gösterici ile Geçirmek
Bir gösterici parametresi fonksiyona çağıranın asıl yapısını verir, dolayısıyla değişiklikler kalıcı olur:
Burada edinmeye değer iki alışkanlık:
- Salt okunur göstericileri
constişaretleyin.const struct Point *p, fonksiyonunp'nin gösterdiğini değiştirmeyeceğine söz verir. Biri sonradan içinep->x = 0eklerse hata ürüne gitmek yerine derleme başarısız olur. Ayrıca niyeti çağrı yerinde belgeler. - Okurken bile büyük yapılar için gösterici geçirin. 64 baytlık bir isim tamponu olan bir yapı, değerle geçirildiğinde çağrı başına 64 bayt kopyalamaya mal olur; bir gösterici 8'e mal olur.
Küçük yapıları değerle geçirmek yine sorunsuzdur - 8 baytlık struct Point bir gösterici kadar ucuza kopyalanır ve değerle geçirilen sürüm NULL olamaz, bu da bütün bir başarısızlık biçimini ortadan kaldırır.
Yapı Dizilerinin İçine Göstericiler
Gösterici aritmetiği, yapı dizilerinde int dizilerinde olduğu gibi çalışır: p + 1, dolgu dahil bütün bir yapı ilerler.
Bir dizi adı ilk elemanına bir göstericiye dönüşür, dolayısıyla staff çağrıda zaten bir struct Employee *'dır. Fonksiyonun n'i ayrıca geçirilmiş olarak istemesinin sebebi de budur - fonksiyonun içindeki sizeof diziyi değil göstericiyi ölçerdi.
Öbekte Bir Yapı Tahsis Etmek
Yığındaki yapılar kapsamlarının sonunda ölür. Onu oluşturan fonksiyondan uzun yaşayan bir tane yapmak - ya da girdinin istediği kadar çok yapmak - için malloc ile tahsis edin:
O küçük fonksiyondaki dört ayrıntının hepsi yük taşır:
sizeof(struct Employee)değil,sizeof *e. "eneyi gösteriyorsa onun boyutu" diye okunur, dolayısıyla tip değişirse tahsis kendiliğinden izler. İkisinin birbirinden kopmasının yolu yoktur.NULLiçin denetleyin.malloc, isteği karşılayamadığındaNULLdöndürür. Boş bir gösterici üzerindene->idyazmak bir segmentation fault'tur.- Her üyeye ilk değer verin.
mallocbelleği sıfırlamaz; yapı çöp dolu gelir. (calloc(1, sizeof *e)onu sizin için sıfırlar.) - Birinin
freeetmesi gerekir. Tahsis edilmiş bir gösterici döndürmek o görevi çağırana devreder. Bunu bir yorumda söyleyin - belirsiz bir sahiplik kuralı, bellek sızıntılarının başlama biçimidir.
Kazanç: Bir Bağlı Liste Düğümü
İşte yapıların göstericiler olmadan yapamayacağı şey. Bir yapı kendisini içeremez - bu sonsuz büyük olurdu - ama kendi türünden birine bir gösterici içerebilir ve o tek numara C'deki her bağlı veri yapısını kurar.
struct Node *next;'in, struct Node hâlâ bildirilirken struct Node'a atıfta bulunduğuna dikkat edin. Bu tam olarak, bir göstericinin neyi gösterdiğinden bağımsız olarak bilinen bir boyutu olduğu için geçerlidir - derleyicinin henüz tam tanıma ihtiyacı yoktur. Aynı zamanda, bir typedef de yazmış olsanız bile struct Node etiketini kullanmak zorunda olduğunuz tek yerdir: typedef adı, kendi tanımının içinde henüz var olmaz.
free_list döngüsü, free(head) çağırmadan önce head->next'i saklar. head->next'i free'den sonra okumak bir serbest-bırakma-sonrası-kullanımdır - bellek artık sizin değildir ve okuduğunuz değer tahsis edicinin oraya koyduğu şeydir.
Sık Yapılan Hatalar
- İlk değersiz ya da
NULLbir göstericidep->x. Yapı yoğun kodda çökmenin en yaygın sebebi. GöstericilereNULLilk değeri verin ve dereferans etmeden önce denetleyin. - Yerel bir yapıya gösterici döndürmek.
struct Point *bad(void) { struct Point p = {1,2}; return &p; }, fonksiyon döndüğü anda var olmayı bırakan belleğin adresini döndürür. Yapıyı değerle döndürün ya da tahsis edin. - İki kez serbest bırakmak ya da bırakmayı unutmak. Her
malloc, tam olarak birfreeile eşleşir. Serbest bıraktıktan sonra göstericiyiNULLyapın, böylece sonraki kazara bir kullanım belleği sessizce bozmak yerine yüksek sesle çöker. - Gösterici içeren bir yapıyı kopyalamak. Yapı ataması, gösterdiği şeyi değil göstericinin değerini kopyalar. Artık her iki yapı da aynı tamponu referans eder ve hangisi önce onu serbest bırakırsa diğerini sarkar bırakır.
Sıkça Sorulan Sorular
C'de -> operatörü ne yapar?
-> operatörü ne yapar?p->x, x üyesine p göstericisi üzerinden erişir. Tam olarak (*p).x ile eşdeğerdir - göstericiyi dereferans et, sonra üyeyi al. Ok, yapılara göstericiler o kadar yaygın olduğu için vardır; uzun biçim kodu dağıtır.
Bir yapı göstericisi için *p.x neden çalışmaz?
*p.x neden çalışmaz?Çünkü ., *'dan daha sıkı bağlanır. *p.x, *(p.x) olarak ayrıştırılır - p'nin (bir yapı değil, bir gösterici) x üyesini al ve onu dereferans et. (*p).x'e ihtiyacınız var ya da yalnızca p->x yazın.
C'de bir yapıyı değerle mi gösterici ile mi geçirmeliyim?
Fonksiyonun çağıranın yapısını değiştirmesi gerektiğinde ya da yapı büyük olup kopyalamak israf olacağında gösterici ile geçirin. Kopyanın ucuz ve kodun daha basit olduğu küçük salt okunur yapılar için değerle geçirin. Salt okunur gösterici parametrelerini const struct T * işaretleyin, böylece derleyici sözü uygular.
C'de bir yapı malloc ile nasıl tahsis edilir?
struct Node *n = malloc(sizeof *n); - sizeof *n biçimi, n neyi gösteriyorsa onun boyutunu ister, dolayısıyla sonradan tipi değiştirirseniz doğru kalır. Sonucu NULL ile karşılaştırın, her üyeye ilk değer verin ve işiniz bitince free(n) yapın.