C++ Da Pointerlar.
-
Yaklasik bir haftadir lynda.com un c++ dersleri ile beraber Deitel in C++ How to program isimli kitabi ile c++ ogreniyoru(du)m. dum diye ekledim cunku ilk baslarda gayet iyi giderken pointerlar dan oteye gidemiyorum.
Simdi
int i = 5;
int j;
int *pointer;
j = i; // bu sekilde j ye 5 degerini atiyoruz
eeee
pointer = i; // bunada 5 degerini atadik.
o zaman bu pointer zimbirtisina ne gerek var?
video da soyle birsey var.
int x = 1;
int y = x;
int *ip;
ip = &x;
int y = *ip;
Yukaridaki daha kolay degilmi? Ne diye sol elimizle sag kulagimizi gostermeye calisiyoruz burada?
-
Hocam pointer denilen şey adres tutucudur. Yani değişkenin ram üzerindeki adresini üzerine alır.
Pointer kullanmak kolay değildir, takibi zordur vs vs.. Ama belleğe direkt erişim sağladığın için çok fazla performans artışı sağlar. Ayrıca adrese müdahale etme gereksinimi duyulan uygulamalar vardır. Oyundaki hileleri vsleri düşünebilirsin.
edit. anlam kargaşası
sLeymN tarafından 18/Eki/12 17:27 tarihinde düzenlenmiştir -
tamam hafiz,
int x = 5; tanimida hafizada yer tutuyor.
Sanirim program calisirken int x in bellekteki yeri ha bire degisip durmuyor. Yani bellekte x icin bir alan olusturuluyor ve onun uzerinden devam edip gidiyor.
Yani ne diye int y = x; duruken ne diye x in bellekteki adresini cagiralim.
int *p;
*p = &x;
cout << *p; ile cout << x ayni degeri verecek.
-
suraya goz at..
The advantage of pointers in c are
kknd tarafından 18/Eki/12 20:02 tarihinde düzenlenmiştir
1.Dynamic memory allocation is possible with pointers.
2.passing arrays and structures to functions.
3.passing addresses to functions.
4.creating data structures such as trees,linked lists etc. -
Hocam şimdi otursam saatlerce açıklarım sana faydalarını. Ama sana şöyle söyleyeyim ki program içindeki esnetilebilirliği çok şekilde arttırabiliyor pointer denen kavram. Küçük bir iş için tüm veri değil sadece 1 byte lık adres kopyalama işlemiyle yapabiliyorsun. Yani bellekte 1 GB veri de işlem yapmak için 4 byte lık bir veri kullanman yeterli oluyor. Ayrıca verdiğin örnekte int *pointer; int i; diyip pointer = i; yapamazsın. Derleyici hata verir. Çok deneme ve alıştırma yapman lazım sonra zaten mantığını kavrarsın.
-
Sorun verilen örnekte, * operatörünün kullanımı gösterilmek istenmiş ama yoksa öyle bir amaç için pointer kullanılmaz tabi. C++ da pointer kullanımı tavsiye edilmez ve zorunlu kalmadıkça kullanılmaz ama, hem C programcıları için, hem de senin (yinede) anlaman açısından genele uzuuunca birşeyler yazdım :) tabi DrKill in dediği gibi tüm kullanıldığı yerleri ve faydalarını anlatsak sayfalar sürer, ama mantığını anlatmaya çalıştım.
En önemli kullanım alanı fonksiyonlara parametre olarak nesneyi değil adresini yollamaktır. Mesela bir A nesnesini yarattın, bu A nesnesini fonksiyona direkt yollarsan, A nesnesinin birebir kopyası bellekte yaratılır B nesnesi olarak, fonksiyon bu B nesnesini görür ve üzerinde işlem yapar.
Aşağıdaki örnekte fonksiyonun gördüğü nesne kendisine parametre olarak gelen B, fonksiyona yollanan nesne ise A, bunlar aynı nesneler değil birbirinin kopyası.
void Fonksiyon(NESNE B){...}
Fonksiyon(A);
Birincisi temel türler dışında hiç bir nesne fonksiyona direkt geçilmemelidir, çünkü gereksiz bellek kopyalaması yapılmaktadır. A nesnesi 10 mb olsaydı, fonksiyon her çağırıldığında 10 mb data kopyalanması gerekecekti.
İkincisi, B nesnesi tamamen farklı bir nesne olduğu için (içerik olarak A ile aynı ama farklı bir adreste farklı bir nesne) , B üzerinde yapılan değişiklikler, A yı etkilemez. Dahası, fonksiyon bittiğinde B nesnesi kullanılmaz hale gelir (ömrü biter) A ilk haliyle aynı kalır.
Diğer şekilde düşünelim, A nesnesini direkt olarak fonksiyona geçmedin ve adresini verdin fonksiyona. Bu durumda, yine bir P nesnesi, fonksiyon içinde yaratılır, ancak bu sefer P sadece 4 bayt (32 bit sistemler için), ve içinde A nesnesinin adresi olan bir pointer.
void Fonksiyon(NESNE *P){...}
Fonksiyon(&A);
Görüldüğü gibi her hangi bir bellek kopyalaması olmadığı gibi, P nesnesi üzerinden A nesnesine her an erişip bu nesneyi okuyup yazabilirsin. İşte * operatörü genelde bu durumlarda işe yarıyor. Normalde direkt eriştiğin bir nesneye *nesne şeklinde, yapi.eleman şeklinde eriştiğin nesneye yapi->eleman şeklinde erişiyorsun. Tabi bu durumda yaptığın her işlem orijinal nesneyi(A) etkiliyor.
Mesela GetSystemInfo fonksiyonu, SYSTEM_INFO adlı bir yapının adresini alır, içini sistemdeki işlemci ile ilgili bazı bilgilerle doldurur, sonra fonksiyon sonlanır. Ancak yapı içindeki bilgilere birşey olmaz çünkü yapı fonksiyonun içinde yaratılmadı, biz sadece ona adresini yolladık.
Dizilerde nesneyi direkt yollamak gibi bir şansın zaten yok, dizinin adını yazdığında fonksiyona yine başlangıç adresi gider.
C++ da pointer kullanımı zaten tavsiye edilmez (bkz: reference) ve hata yönetimi exception yöntemi ile yapılır, ancak C de exception kavramı olmadığı için, fonksiyonun hata verip vermediği geri dönüş değeri ile kontrol edilir. Bazı durumlarda, dönüş değeri farklı anlamlarda kullanılmak istenir , tüm değerler (- sayilar, 0, + sayilar) normal olarak dönebilir, bu durumda, geri dönüş değerini tutacak nesne, fonksiyona parametre olarak verilebilir.
int IslemYap(int p1, int *sonuc);
Bunlar dışında queue, linkedlist vb veri yapılarında çok sık kullanılır pointer ancak daha kısıtlı bir kullanım alanı. Biraz daha ileri seviyeye geçersek, C dilinin tasarımı tamamen pointer kavramı üzerine kuruludur. C++ da C nin nesne yönelimli versiyonu olduğu için ve mümkün olduğunca C yi kapsadığı için pointerları içerir. Ancak kullanılmasından mümkün olduğunca kaçınılır.---Burdan sonrası konuyla direkt ilgili değil ve biraz daha ileri konular---
Şimdi C'nin tasarımı ve adres kavramı üzerine birşeyler yazayım. C de başlangıçta dizi dışında özelleştirilebilir bir veri yapısı yoktu (struct, union gibi). Dizilerin ve fonksiyonların isimleri aynı zamanda başlangıç adresleridir, Bu C nin en büyük tasarım özelliğidir. Fonksiyon çağırmak kavramının fonksiyonun adı ile ilgisi yoktur, tamamen () operatörleri ile ilgilidir, diziler için de aynı şeyler geçerli, örneklere bakalım.
Fonksiyon(p1, p2, p3);
şeklinde fonksiyon çağırıldığında, () operatörü, öncelikle p1, p2, p3 ü belleğe iteler :), ardından, geri dönüş adresini belleğe push eder ve Fonksiyon isminin belirttiği adres çağırılır (CALL instructionu ile).
Mesela printf("%X\n", Fonksiyon) yazarak, fonksiyonun hangi adreste bulunduğunu görebilirsin. Veya daha iyisi, direkt adres ile
0x2070FF(p1, p2, p3);
Şeklinde fonksiyon bile çağırabilirsin :) konuyla direkt ilgili olmadığı için pointer cast işlemini yazmadım. Bu durumda şu kodlar çalışır arka planda,
push p3
push p2
push p1
call 0x2070FF ( ya da fonksiyonun adı -- call geri dönüş değerini arka planda kendisi belleğe push eder en son)Görüldüğü gibi işi yapan () operatörleri.
Aynı şeyi diziler için düşünelim. [] operatörü , solundaki adresi (yani dizi ismini) başlangıç kabul ederek, dizi türünün boyutu * içerdeki değer kadar ilerler ve oradan aldığı veriyi döndürür. Örnek
int dizi[5];
dizi[3];
İfadesinde, int türü boyutu 4 bayttır, dizi adresi 0x207010 adresi olsun, [] operatörü, 0x207010 + (4*3) yani 0x20701C adresine gider, ve ordaki 4 baytlık datayı alır döndürür. 3 içerdeki değer, 4 ise dizi türünün (int) standart boyutu.
Yine printf("%X\n", dizi) yazarak, dizinin başlangıç adresini görebilirsiniz. Aynı şekilde 0x207010[3] şeklinde bile kullanabilirsiniz, yani bu operatörler, adres alırlar ve çok esneklerdir, hatta şu iki ifade birbiri ile tamamen aynıdır.
dizi[4];
4[dizi];
Nedenini anlamayı size bırakıyorum :)
Shellcode kavramını duymuş olanlar için söyleyim, burda mesela asm ile yazılmış 250 baytlık bir fonksiyonun kodları kopyalanır (hex editörle mesela) ve C de bir dizinin içine yapıştırılır. Sonra dizi ismi yani fonksiyon kodunun başlangıç adresi, cast ile fonksiyon göstericisi gibi gösterilerek, o baytların fonksiyon gibi çalıştırılması sağlanır.
Bu biraz cevaptan ziyade dökümana benzedi ama birilerine faydası dokunur umarım.
dipnot: yukarıda atladığım casting olayını biraz açayım, 0x207010[3] dediğiniz zaman, bu adresin türünü derleyici bilemez dolayısıyla kodu derleyemez, o yüzden önce o adresin ne tür bir adres olduğunu tanıtmak için cast etmemiz gerek. ((int*)0x207010)[3] , biraz daha çirkin görünüyor dimi. Fonksiyon için gereken casting ise şöyle ((int(*)(int,int,int))0x2070FF)(p1, p2, p3); fonksiyon göstericilerinin yazımı biraz karmaşıktır, ancak bir kere anlarsanız çok basit yukarıdaki cast şunu söylüyor, bu adreste int fonksiyon(int,int,int) prototipli bir fonksiyon var.
-
Verdiginiz cevaplar icin tesekkurler.
Simdi benim buradan anladigim;
int x = 100; // diyelimki x degiskene 100 byte lik yer ayrildi
int y = x; // y degiskenini x e esitleyerek bellekte 100 byte lik baska bir yer daha ayrildi.
int z = y; // bellekte baska bir 100 byte lik alan acildi boylece tek bir deger icin bellekte bos yere 200 byte lik yer acilmis oldu.
ama
int x = 100;
int *y, *z;
*y = &x;
*z = &x;
seklinde yaparsak elimizde 3 ayri degisken olacak ama sadece 100 byte lik alan kaplayacagiz.
Dogrumu acaba?
-
Abi bir tane swap islemi yapan bir fonksiyon yapmayi dene anliycaksin en basitinden ne ise yaradiklarini
prgoramin her yerinden o degiseknin kendisini degistirebiliyorsun
Ilk verdigin ornekte
int i = 5;
int j;
int *pointer;
j=i deyince evet j de 5 oluyor
pointer = i deyince pointer 5 olmuyor. Sadece i nin bellekte degerini bulundugu bolumu gosteriyor
EGER i degisirse j de i nin degerini gosterdigi icin degisir
Fakat j i nin kopyasini aldigi icin yine 5 kalir
Bak simdi sana bir ornek yazdim atayim okursan anliyacaksin
#include<iostream.h> #include<conio.h> int main(int argc, char* argv[]) { int i = 5; int j= i; int *pointer = &i; //Burada i ve pointer ayni cikti veriyor cout << "i = "<<i<<" j = "<<j<<" pointer = "<<*pointer<<endl; //Sadece i yi degistiriyoruz ve pointerde onu gosterdigi icin //degismis gibi duruyor i=12; cout << "i = "<<i<<" j = "<<j<<" pointer = "<<*pointer<<endl; //j yi degistirince ise sadece j degisiyor j=99; cout << "i = "<<i<<" j = "<<j<<" pointer = "<<*pointer<<endl; //Ama pointeri degistirirsek i da degisiyor *pointer= 55; cout << "i = "<<i<<" j = "<<j<<" pointer = "<<*pointer<<endl; getch(); return 0; } -
Hafiz ornek icin tesekkurler.
Simdi olayi kavradim gibi. Ama surada bir sorun var
ilk basta int j = i; yaptik, daha sonra asagidaki satirda int i = 12; yaptik.
Yani int j degeri ilk atadigimiz deger olan 5 te sabit kaliyor ve bellekte misal 5 byte lik bir alani int j icin aciyor.
int i = 12; yapinca int i nin degeri degisiyor ama int j halen int i nin eski degerini gosteriyor.
*pointer ise int i nerede degisirse degissin (programin basinda ortasinda veya sonunda) onun adresini tuttugu icin otomatikman ayni degeri aliyor.
Yani *pointer aslinda deger falan almiyor sadece int i nin adres bilgisini tutuyor ve bellekte *pointer icin extradan yer tahsisi yapilmiyor.
Dogrumudur?
