[Çözüldü] Pointer bellek sınırlaması nasıl yapılıyor?

Başlatan blacksnow, 04 Temmuz 2015 - 17:24:46

« önceki - sonraki »

0 Üyeler ve 3 Ziyaretçi konuyu incelemekte.

blacksnow

Uzun süredir aklıma takılan bir soru.Örnek vererek anlatayım.

#include<stdio.h>
void main (void)
{
char *s = "elma";
s = "buyuk elma";
}

Şimdi ilk başta "elma" değerini atadım, sonra "buyuk elma".Günümüzdeki bellekler bu tür değerler göre çok büyük olduğu için belki sorun olmuyor fakat, eğer s değişkenine "buyuk elma" değerini atayacak kadar yer olmazsa bellekte ne olur? Başka bir değişkenin üzerine yazılma riski var mı?Yoksa çalışma zamanı hatası mı alırız? Şimdiden teşekkürler.

mozhan

Tabiki devamında gelen belleğe yazar ve bu gerçekten çok sıkıntılı bi durum. o bellek adresinde herşey olabilir ve bu sistem için gerekli birşey ise bilgisayar bile kapanabilir. Pointer lerin güvenliği tehtit etmesinin asıl sebebini bu örnek iyi acıklayabilir. çalışma zamanı hatası alman için büyük elmanın yazılacağı yerin 'artan kısmı' yine o kodda bi değişkenin bulunduğu yere denk gelmesi gerekir.
When we ride on our enemies..

https://github.com/mustafaozhan

Amenofis

C' de çift tırnak arasına yazılan karakter dizileri sadece dizinin başlangıç adresini döndürür. Zaten işaretçiler de sadece adres tutabilen değişkenlerdir. Adreslerde sabit uzunlukta olduğuna göre ister "elma" yazarsın, ister "bu dünyanın en büyük elmasıdır bundan daha büyük elma hiçbiryerde yoktur...!!!" yazarsın işaretçi sadece başlangıç adresini tutar. Bu da 32 bit sistemlerde 4 byte, 64 bit sistemlerde 8 bytetır.

Yazdığın örnekteki sorun ise başka yerde. Dönen adresin türü tam olarak "const char *". Sen ise bu adresi char * türünde bir işaretçiye atıyorsun. Bunun yan etkilerine şimdi girmeyelim ama aklınızda olsun diye yazıyorum stringleri (başlangıç adreslerini) tutan işaretçiler kesinlikle const char * türünde olmalı.

thoron

#3
Hafızada data denilen stack ve heap gibi bir kısım var.

https://en.wikipedia.org/wiki/Data_segment

Senin yaptığın gibi bir atama yapıldığında onun read-only kısmına yazar ve başlangıç adresini sana verir. Sen "elma"dan sonra "büyük elma" atarsan pointera "elma" nın üzerine yazmaz. Aynı segmentin farklı bir yerinde "büyük elma" yazar, onun ilk karakterinin adresini pointera atar.  Şunu derleyip çalıştırırsan görebilirsin. Hatta strcpy ile elmanın üzerine bir şeyler yazmaya çalışırsan da segmentation fault hatası alırsın.


#include <stdio.h>

int main(){

char *ptr ="elma";

printf("stringin adresi(elma) : %p\n", ptr);
printf("pointer'ın adresi : %p\n", &ptr);

ptr = "büyük elma";

printf("stringin adresi(büyük elma) : %p\n", ptr);
printf("pointer'ın adresi : %p\n", &ptr);

return 0;
}


Gördüğün gibi aynı pointer farklı yerleri gösteriyor. "elma"nın yazılı olduğu yer tekrar kullanılabilir mı kullanılamaz mı bilmiyorum ama compiler bir şeyler yapıyordur mutlaka. Onu araştırayım bir ara.

Sonuç olarak çalışma zamanı hatası alacağını düşünüyorum ben, hafıza doluyken allocate etmeye kalktığındaki gibi. Ha tabii ki pointer ile hafızanın kendine ait olmayan kısımlarına yazabilirsin, hatta sana void pointer ile ilgili konunda dangling pointer hakkında döküman atmıştım. Ama bunda pointerın gösterdiği yere gidip bir şey yazmıyorsun, farklı bir yer kullanıyorsun. Pointerın gösterdiği yere yazıyor olsaydın evet, bir şeylerin üzerine yazardın. fgets gibi fonksiyonlar bunu önlemek için zaten. Sadece kendi programındaki değişkenlerin değil, başka yazılımların verilerinin üzerine yazma ihtimalin de var diye biliyorum. Özetle yapmıyor. :D

Amenofis

Yazmaya üşendiğim şeyleri sağolsun thoron yazmış. İşte bunun için işaretçi const olmalı ki derleyici sizi engelleyebilsin.

Ek bilgi olarak işaretçiye atanmış stringler hiçbir zaman silinmezler (yine const sebebi). Ayrıca bir işaretçi ile asla sistemi bozamazsınız. Bütün adreslere 0 yazsanız yine olmaz, sadece kendi uygulamanız "size ait olmayan bölgeye erişmeye çalışmak" tan dolayı sonlandırılır.

blacksnow

#5
Anlattıklarınızda daha anlamadığım birçok yer var.Buraya hepsini geçmek istemedim.Her şey çok karışık gibi.Nerden başlayacağımı bile tam bilemiyorum.Yine de teşekkürler.

@mozhan ve @Amenofis, şuan herşeyden önce merak ettiğim şey, yazdığım bir program bellekte çalışan başka programların kullandığı alanları kullanabilir mi?Biriniz kullanır bilgisayar bile kapanabilir, diğeriniz ise programın sonlandıralacağını söylüyorsunuz.
Alıntı Yap* tabi ki devamında gelen belleğe yazar ve bu gerçekten çok sıkıntılı bi durum. o bellek adresinde * her şey olabilir ve bu sistem için gerekli * bir şey ise bilgisayar bile kapanabilir. Pointer lerin güvenliği tehtit etmesinin asıl sebebini bu örnek iyi acıklayabilir. çalışma zamanı hatası alman için büyük elmanın yazılacağı yerin 'artan kısmı' yine o kodda bi değişkenin bulunduğu yere denk gelmesi gerekir.
Alıntı YapEk bilgi olarak işaretçiye atanmış stringler hiçbir zaman silinmezler (yine const sebebi). Ayrıca bir işaretçi ile asla sistemi bozamazsınız. Bütün adreslere 0 yazsanız yine olmaz, sadece kendi uygulamanız "size ait olmayan bölgeye erişmeye çalışmak" tan dolayı sonlandırılır.

@thoron, örneği çalıştırdığımda ne anlatmak istediğinizi anladım fakat birkaç sorum var.Öncelikle belleklerle ilgili çok bilgim olmadığını belirtmek isterim.Yaptığım atama bellekte read-only kısmına yazılıyor dediniz, o zaman üstünde değişiklik nasıl yapabiliyorum?Ayrıca strcpy ile neden segmetation fault hatası aldık anlamadım.
Alıntı YapSenin yaptığın gibi bir atama yapıldığında onun read-only kısmına yazar ve başlangıç adresini sana verir. Sen "elma"dan sonra "büyük elma" atarsan pointera "elma" nın üzerine yazmaz. Aynı segmentin farklı bir yerinde "büyük elma" yazar, onun ilk karakterinin adresini pointera atar.  Şunu derleyip çalıştırırsan görebilirsin. Hatta strcpy ile elmanın üzerine bir şeyler yazmaya çalışırsan da segmentation fault hatası alırsın.

@Amenofis, değişkenin türünü "const char *" olarak belirlersem üzerinde değişiklik yapmak istediğimde ne yapacağım?
Alıntı YapYazdığın örnekteki sorun ise başka yerde. Dönen adresin türü tam olarak "const char *". Sen ise bu adresi char * türünde bir işaretçiye atıyorsun. Bunun yan etkilerine şimdi girmeyelim ama aklınızda olsun diye yazıyorum stringleri (başlangıç adreslerini) tutan işaretçiler kesinlikle const char * türünde olmalı.

thoron

#6
@blacksnow

read only : ro
read write : rw

Öncelikle pointera atadığın stringin üzerinde değişiklik yapamıyorsun. strcpy ile o yüzden hata alırsın zaten, strcpy ilk stringin üzerine yazmaya çalışır. Senin yaptığın yeni bir yer ayırıp orada yeni bir string oluşturuyor. İlk oluşturduğun duruyor. Hatta


#include <stdio.h>

int main(){

char *ptr = "elma", *temp;
temp = ptr;
ptr = "büyük elma";

puts(temp);
puts(ptr);

return 0;
}


Çıktının elma, büyük elma şeklinde olması gerekli. Teknik kısmı şöyle:

Pointer rw kısımda oluşuyor, onun değeri tabi ki değiştirilebilir. Pointerın adresini gösterdiği string ro kısımda. Yani oradaki veriyi değiştiremezsin, sadece okuyabilirsin. İlk kodu çalıştırdığında gördüğün gibi stringlerin adresleri farklı, pointerın aynı. Yani aynı pointer öncekinden farklı bir yeri gösteriyor. "elma"nın üzerine yazıyor olsaydı stringlerin başlangıç adresleri aynı olurdu pointerın adresi gibi. 

char *ptr = "string"  ve
char str[]="string" aynı kapıya çıkmıyor.

İlkinde rw kısımda sizeof(char*) kadar yer ayırıp pointer oluşturursun, sonra ro kısımda string oluşturursun ve stringin adresini pointera atarsın.

İkincisinde sizeof(char)*len(str)+1 kadar rw kısımda yer ayırırsın, stringi oraya yazarsın. O zaman stringin üzerine yazabilirsin. Hatta benim kodumu biraz değiştirip,
char str[]="elma" yapıp üzerine strcpy (string.h'ı include etmeyi unutma) ile "büyük elma" yazıp adreslerini yazdırırsan aynı adresi verir.  Aynısını üstteki kodda yaparsan ikisi de büyük elma yazar.

blacksnow

#7
@thoron, sayenizde bilgi eksikliğim olduğunu gördüm.Ben " char * ptr = "elma" " şeklinde bir atamanın üzerinde oynama yapılabileceğini düşünüyordum.Bu durumda @Amenofis' e sorduğum son soruda saçma kalıyor.Çünkü "const char *" olarak tanımlarsam ve üzerinde değişiklik yapmak istersem, derleme esnasında beni uyarıyor ki çalışma esnasında "segmentation fault" hatası almayalım."char *" olarak tanımladığımızda ne gibi yan etkiler oluyor merak ediyorum.

Daha önce dangling pointer hakkında bir bağlantı vermiştiniz.İngilizcem çok iyi olmadığından pek anlamamıştım.Şimdi kendim biraz daha araştırdım.Diğer açtığım konuda bellekten yer ayrılan fonksiyonda bir dangling pointer oluşuyor anladığım kadarıyla.Gördüğüm kadarıyla programın çalışmasında bir sorun yok.Bu dangling pointer ne gibi sorunlara yol açabilir tam anlayamadım.
void *yeni (size_t d) /* bellek ayirma */
{
void *g;                                             /* g işaretcisi dangling pointer oluyor. */
if ((g=malloc(d))!=NULL)
return g;                 /* tamam */
fprintf(stderr, "SIRALA: Yetersiz bellek");
exit(1); /* programi kes */
} /* yeni */

Amenofis

Bir işaretçi ile sistemi ya da başka bir uygulamayı bozabilmen için sistemin "gerçek mod" da çalışması gerekir. Eski Ms-dos işletim sistemi gerçek modda çalışıyordu mesela, bütün adresler fizikseldi. Bu yüzden sistem aynı anda birden fazla uygulamanın çalışmasına müsade etmiyordu çünkü birden fazla uygulama çalışırsa birisinin hatası bütün sistemi çökertirdi.

Günümüzdeki sistemler korumalı modda çalışıyor, hem de cpu ve mmu tarafından donanımsal destekli. Sistemden bellek tahsisi yaptığın zaman sana verdiği adres fiziksel değil sanal bir adres, yani sadece bir anahtar. Sistem aynı adresi iki farklı uygulamaya verse bile bunlar bellekte farklı noktaları işaret eder ve birbirine karışmazlar. Dolayısıyla senin uygulaman sadece ona tahsis edilmiş olan bölgeyi kurcalayabilir ve başka bişeye zarar veremez.

blacksnow

#9
Sonuç olarak anladıklarımı anlatacağım ve eğer yanlışlarım olursa düzeltmenizi bekliyorum.

En başta verdiğim örnekte, " s = "buyuk elma" " atamasını, bellekte yer yoksa bellekte bulunan başka programların üzerine yazma gibi bir riski yok.Eğer atama programdaki değişkenlerin üzerine yapılmaya çalışılırsa, çalışma zamanında hata almalıyım.Yani programın üzerindeki değişkenlerin üzerine de yazma riski yok.

Alıntı yapılan: AmenofisSistem aynı adresi iki farklı uygulamaya verse bile bunlar bellekte farklı noktaları işaret eder ve birbirine karışmazlar.
Sormadan geçmek istemedim, iki programda da aynı adres var.Nasıl oluyor da farklı iki adresi işaret ediyorlar?

thoron

@blacksnow

Hafızada sana ait olmayan bir yeri gösteren pointera denir. Kullanmaya çalışmadığın sürece programı etkilemez de güvenlik açığı yaratır. -buffer overflow diye araştırabilirsin.-
Hafızayı serbest bıraktıktan sonra pointera NULL atamaya alış diye gönderdim. :)

blacksnow

Alıntı yapılan: thoronHafızada sana ait olmayan bir yeri gösteren pointera denir.
Yukarıda verdiğim örnekte fonksiyon g pointerını geri döndürüyor ve ben o ayrılan alanı kullanıyorum.Burada neresi bana ait olmayan alan anlayamıyorum.Vaktiniz varsa bir örnek paylaşabilir misiniz bu dangling pointerların zararları ile ilgili?

thoron

Alıntı yapılan: blacksnow - 06 Temmuz 2015 - 00:23:42
Alıntı yapılan: thoronHafızada sana ait olmayan bir yeri gösteren pointera denir.
Yukarıda verdiğim örnekte fonksiyon g pointerını geri döndürüyor ve ben o ayrılan alanı kullanıyorum.Burada neresi bana ait olmayan alan anlayamıyorum.Vaktiniz varsa bir örnek paylaşabilir misiniz bu dangling pointerların zararları ile ilgili?

Orası sana ait. free(g) yaptığın anda değil. O yüzden diyorum ki free(g)'den sonra g = NULL; yapmayı alışkanlık haline getir.

Senin için google'a yazayım bak. :P
http://www.google.com.tr/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&ved=0CDoQFjAD&url=http%3A%2F%2Fweb.itu.edu.tr%2F~orencik%2FBilgMimYenYakl2007%2FEser_Aygun%2FEserAygun_rapor.doc&ei=vyuaVfnUG8eBywO8oJ6IDw&usg=AFQjCNH_iUwzWbtTlocZrp0ikMkKJyWszQ&bvm=bv.96952980,d.bGQ&cad=rja

blacksnow

Alıntı yapılan: thoronOrası sana ait. free(g) yaptığın anda değil. O yüzden diyorum ki free(g)'den sonra g = NULL; yapmayı alışkanlık haline getir.
Bir şeyi neden yaptığımı bilmeden yapmak hoşuma gitmiyor.O yüzden tekrar tekrar soruyorum.Çünkü nedense anlam veremedim bu dangling pointerlara.
Verdiğiniz kaynak bir örnek var.Burada q için ayrılan yer belleğe geri verildiğinde, q ve p->next işaretcilerinin dangling pointer olduğunu söylüyor.Ben q pointerı için ayrılan yeri geri verdiğime göre artık kullanmayacağım, o zaman ne anlamı kalıyor dangling pointer olup olmamasının? Deneyip görmeden anlayamıyorum.
class Node {
int value;
Node *next;
};
...
Node *p = new Node();
Node *q = new Node();
Node *r = new Node();
p->next = q;
q->next = r;
                           // a
r = NULL;
                          // b
delete q;
                          // c
...

thoron

#14
 @blacksnow
Çok ayrıntılı olarak bilmiyorum ben de. İlk başta ezbere yapıp zaman geçtikte anlıyorum genelde. :D

blacksnow

@thoron, bir şeyleri anlamakta iyi değilim. :) O yüzden sürekli sorular soruyorum.Bu konu benim için yarım kaldı.Sanırım ileride hatalar yaparak, araştırarak eksiklerimi tamamlamaya çalışacağım.Teşekkür ederim.

thoron

#16
Alıntı yapılan: blacksnow - 06 Temmuz 2015 - 16:18:32
@thoron, bir şeyleri anlamakta iyi değilim. :) O yüzden sürekli sorular soruyorum.Bu konu benim için yarım kaldı.Sanırım ileride hatalar yaparak, araştırarak eksiklerimi tamamlamaya çalışacağım.Teşekkür ederim.

Estağfurullah, ben de konuyu pek bilmediğimden anlatamadım :)

Amenofis

Yahu free(g) yaptığın zaman g içindeki adres geçersiz oluyor işte dangling pointer bu. Sonra kazayla bu geçersiz adres üzerinde işlem yapmamak için NULL atarız ki anlamı "bu işaretçi hiçbirşeyi göstermiyor" dur. Çünkü bir işaretçi bir adres tutuyorsa o adresin geçerli olması beklenir, geçersiz ise null atarak adresi silersin.

blacksnow

Alıntı yapılan: Amenofis - 06 Temmuz 2015 - 21:48:55
Yahu free(g) yaptığın zaman g içindeki adres geçersiz oluyor işte dangling pointer bu. Sonra kazayla bu geçersiz adres üzerinde işlem yapmamak için NULL atarız ki anlamı "bu işaretçi hiçbirşeyi göstermiyor" dur. Çünkü bir işaretçi bir adres tutuyorsa o adresin geçerli olması beklenir, geçersiz ise null atarak adresi silersin.
Bir pointerı free yaptıktan sonra üzerine başka bir adres atamadan zaten kullanmam fakat elbette hatalar yapılabilir bu durumda NULL yapmanın faydasını görebiiyorum.Teşekkür ederim.