C'de Bilinirlik Alanı ve Ömür Kavramı

10:55

Bilinirlikte dar kapsamlı değişken geniş kapsamlı değişkeni maskeleyecektir. C ‘de İsim gizlemesi diye bir kavram vardır.
Fonksiyon tanımlaması yaparken (int x,int y) benzeri parantez içi ifadelerin fonksiyonun kapanış parantezine kadar aktif olduğu bilinmesi gerekmektedir.

image

Bir alanda hem üst bir kapsama sahip hem de yerel bir değişken varsa üstü kullanmanın bir yolu yoktur. Örnek vermek gerekirse

image

C’de ömür kavramı 3’e ayrılır;
  1.   Statik
  2.   Otomatik
  3.   Dinamik
  • Statik Ömür : Hayatını programın başından sonuna kadar sürdürmektedir.
  • Otomatik Ömür : Bir kodun yürütülmesi sırasında bellekte kalan kodun çalışması sonlandığında bellekten silinen bir ömür çeşitidir.
  • Dinamik Ömür : Programın çalışıp bitmesiyle ilgili bir ömür anlayışı olmayan ne zaman istersek hayata geçen ne zaman istersek hayatı sonlanacaktır.

Atama (ingilizce overwrite ) hayatta olan bir nesneye yapılır. ilk değer verme hayata gelirken aldığı değer olacaktır.
  • Global değişkenler Static Ömürlüdür.
  • Fonksiyon içinde ve parametre olarak tanımlananlar Otomatik Ömürlüdür.
  • Static Ömürlü olan değişkenler ilk değer verilmezse 0 değeri otomatik olarak verilir.

ÖNEMLİ: Stringler yani iki tırnak içinde olan ifadeler C'de static ömürlüdür. Mesela printf(“Hello World”) ifadesi içinde “Hello World” ifadesi static ömürlüdür ve program sonlanana kadar ömrünü sürdürür.
  • Program main ile başlar ve main bittiğinde program sonlanır. main içine yazılan değişken program sonuna kadar ömrünü sürdürse de bu o değişkenin static değişken olduğu anlamına GELMEZ.
  • Static ömürlü değişkenler hayata main fonksiyonu çağrılmadan gelirler.
  • Static ömürlü bir değişkenle static ömürlü olan global değişken birbirinden farklıdır. Static ömürlü değişkenler tanımlandığı alanda kullanılır ve program sonlana kadar saklanır ve o alanda kullanılır fakat global değişkenlerde static’tir ve her yerden erişme imkanı vardır.

Çöp değer (garbage value) bellekte var olan silinmiş yani üzerine yazılabilir (eski değerlerdir). Otomatik ömürlü değişkenler çöp değerle başlar ve set edilmezse yanlış sonuçlar doğurur. Sonuç olarak otomatik ömürlü değişkenlere değer atamadan kullanmak hatalı olacaktır.

Kısaca otomatik ömürlü değişkenler garbage value ile başlar. Bu yüzden kullanmadan önce set etmek gerekir.

Geçici Nesne:
Çağrılan bir fonksiyon geri dönüş değerini geçici bir değişkene atar. Fonksiyonu çağrında bu değeri bu geçici değişkenden alarak işlem yapar. Ve orada geçici değişken sonlanmaktadır. Fakat fonksiyon çağrıldığında fonksiyon geçici değişkene bir şey göndermez ( bu C ‘de olası bir durumdur ) ise  (Programcının hatalı kod yazımından dolayı) bu durumda geçici değişken dinamik ömürlü olduğundan dolayı garbage value değeri üzerinden işlem yapılır ve hatalı sonuçlar oluşabilir.

C'de Tamponlama Mekanizmasının Degistirilmesi ( setvbuf - setbuf Fonksiyonları )

10:39

“fopen” ile bir dosyayı açtığımızda fopen açılan her dosya için bir tampon oluşturur. Tampon <stdio.h> da bildirilen BUFSIZ sembolik sabitinin belirttiği uzunluktadır. C'de tamponlama mekanizması ( Tampon  stratejisi ve tamponlama boyutu ) iki ayrı fonksiyonla değiştirilebilmektedir . Bunlar setbuf ve setvbuf fonksiyonlarıdır. Standartlara göre setbuf ve setvbuf fonksiyonları dosya fopen'la açıldıktan ve hiçbir işlem yapmadan önce çağrılmalıdır.


Tamponun default boyutu stdio.h içinde bildirilmiş olup BUFSIZE ile belirtilmiştir. Bizim bu sembolik sabit değerini değiştirmemiz tamponlama mekanizmasının boyutunu değiştirmez. Tamponlama boyutunu ve stratejisini değiştirmek için setbuf veya setvbuf fonksiyonlarını kullanmamız gerekir. 

Tamponlama stratejisi;

  • _IOFBF :Full Buffered
  • _IOLBF :Line Buffered
  • _IONBF :No Buffered

setbuf:

setbuf aşağıda belirtilen örnekte olduğu gibi sadece tamponlama yerini değiştirir. Fonksiyon çağrısından sonra tamponlama olarak tmp isimli alanı kullanmaya başlar.


setvbuf:

setvbuf fonksiyonu ise hem tamponlama yerini hem tamponlama boyutunu hem de stratejisini değiştirir.


iki örneğinde çıktısı aşağıdaki gibidir. 

C' de fork ve exec Kavramı


fork()

fork fonksiyonu bir process'in bire bir kopyasını oluşturmak için kullanılır. fork işlevi işletim sisteminde thread kavramı olmadan önce işletim sisteminin çalışma zamanından daha fazla kazanmak için kullanılmaktaydı. Fork işlemi ile aynı process'ten birden fazla oluşturularak işlemler daha kısa zamanda yapılması sağlanmaktaydı.


 fork fonksiyonu çağrıldığı zaman child process id'si sıfır ile parent process id'si sıfır dışı değerle yukarıdaki gibi kontrol edilerek istenilen kodların çalıştırılması sağlanır.


 fork fonksiyonun hala kullanıldığı ikinci kısım ise bir proces çalışma anında başka bir process'i daha çalıştırmak isterse bu durumda yapması gereken şey process'in bir kopyasını oluşturmak ve child veya parent process'i çalıştırılmak istenen process haline getirmek.bu işlevin ikinci kısmı içinde exec fonksiyonu kullanılmaktadır.

execl()

execl fonksiyonunun çalışma mantığı fonksiyon çağrıldığı anda fonsiyonun parametre ifadesinde girilen fonksiyon çalışmaya başlayacak ve o process artık çağrılan process olarak yaşamına devam edecek. Kısaca artık execl fonksiyonunun altında olan kodların o process için çalışması mümkün olmayacak.


kısaca fork fonksiyonun process'in bir klonunun oluştururken exec process'i mutasyona uğratıp başka bir process olarak çalışmasını sağlayacaktır.


Yukarıda hem fork hem de exec işlemi gerçekleştirilmiştir. önce fork ie iki tane process oluşturulmuş ardından child process exec ile main process'ine dönüşmüş parent process ise çalışmasına devam etmiştir.





stdin,stdout,stderr dosyaları

00:57


Windows, Unix/Linux işletim sistemlerinde STDIN, STDOUT ve STDERR birer dosya gibi ele alınmaktadır. Biz bu dosya üzerinde işlem yaparken  bu dosyalar nerelere yönlendirilmiş ise davranış biçimi o yönde olacaktır. Genel olarak stdin dosyası klavyeye yönlendirilmiştir. stdout ve stderr ise ekrana yönlendirilmiştir. Yani biz stderr veya stdout'a yazdığımız zaman bu yazılanlar ekrana çıkacak eğer stdout veya stderr yazıcıya yönlendirilmiş olsaydı o zaman yazıcıdan çıkış olacaktı. stdin dosyası genelde klavyeye yönlendirilmiştir. Dolayısıyla biz klavyeden doğrudan okuma yapmıyoruz stdin dosyasından okuma yapıyoruz ve klavye stdin dosyasına yazma yaptığı için doğrudan klavyeden okuma yaptığımızı farzederiz.Aslında işletim sisteminin stdin dosyasından okuma yapmaktayız.

Kısaca ekran sanki bir dosyadır, biz bu dosyaya yazma yaptığımızda ekrana yazmış oluruz. Klavye de bir dosya gibidir, biz bu dosyadan okuma yaptığımızda klavyeden okuma yapmış oluruz.

getchar, gets ve scanf gibi giriş fonksiyonları aslında birer dosya fonksiyonudur ve stdin dosyasından okuma yapmaktadır. printf, puts, putchar gibi fonksiyonlar da birer dosya fonksiyonudur. Default olarak stdout dosyasına yazma yapmaktadırlar.

İşletim sistemlerinde aygıtların birer dosya gibi işlem görülmesinin nedeni aygıtların kullanımı daha basite indirgemektir. Biz sadece bu dosyalarla işlem yaparken işletim sisteminin dosya alt sistemi bu dosyaların birer aygıta ilişkin olduğunu anlar ve aygıtlara yönelir. Ve bu noktada işin önemli bir kısmını işletim sistemi üzerine almış olur. UNIX türevi işletim sistemlerinde bu tasarıma Sanal Dosya Sistemi (Virtual File System) denilmektedir.

C programlama dilinde STDIN, STDOUT ve STDERR dosyaları işletim sisteminin  STDIN, STDOUT ve STDERR dosyalarıyla çalışmaktadır ama C programlama dilinde bu dosyalara erişimde tamponlama mekanizmasının kullanıldığı bilgisi akıldan çıkarılmamalıdır.

Linux shell komutlarından örnek vermek gerekirse:

komut1  >  file.txt     : komut1'in stdout'u file yönlendirilmiştir. Dolayısıyla mesela komut1 bir c programı olsaydı printf ile ekrana basmak istediğimiz karakterler dosyaya yazılacaktı.

komut1  |   komut2   : komut1'in stdout'u komut2'ye stdin olarak yönlendirilmiştir.Dolayısıyla mesela komut1 bir c programı olsaydı printf ile ekrana basmak istediğimiz karakterler ikinci c programında stdin dosyasından okuma yapılan mesela gets fonksiyonu ile alınabilecekti.

Bir C Kodunun Çalıstırılabilir Dosya Haline Gelme Süreci

10:36


Yazdığımız bir C kodunun çalıştırılabilir dosya haline gelirken arka planda hangi aşamalardan geçtiğini öğrenmek hem bakış açımızı hem de programlama bilgisini genişletecektir. gcc derleyicileri önce preprocessor'ü devreye sokar ardından compiler devreye girer ve ardından linker devreye girer ve çalıştırılabilir dosyamız elde edilmiş olur. Bu arada yazdığımız programın assembly karşılığını gcc bize üretmektedir.



 Yazdığımız program basit bir "Hello World" programı ve şimdi bu hello.c programının üzerinden çalışmaya başlayalım;





  • .c dosyaları kaynak kod dosyaları
  • .i dosyaları ön işlemci çıktısının ardından oluşturulan dosya
  • .o dosyaları compiler ardından oluşan object file dosyası
  • main ise linkerin ardından oluşan çalıştırılabilir dosya


Pre-processing

Kısaca ön işlemci # (diyez) ile başlayan ifadeler üzerinde değişiklik yapan programdır. Ön işlemci kaynak kod üzerinde değişiklik yapan bir süreçtir. 


gcc -E main.c dediğimizde ön işlemci çalışır ve çıktıyı ekrana basar. Ben bu çıktıyı prepro.c' ye yönlendirdim. Şimdi vim ile bu dosyanın içeriğine bakalım;


Yukarıda ve aşağıda görüleceği üzere kaynak kodda linker için gerekli düzenlemeler yapılmış include' ların tam yol adresleri girilmiştir.


Compilation


Compilation yada compiler aşaması ise derleyici kaynak kodu link işlemleri dışında object dosyaya dönüştürme işlemlerine gerçekleştirdiği aşamadır. Derleme işlemi bittikten sonra linker' a gönderilir ve linker'dan sonra çalıştırılabilir makine koduna tamamiyle dönüştürülmüş olur.


Linking
Linker işleminin artından artık çalıştırılabilir kodlar üretilmiş olmaktadır.




Ext: Assembly

.s isimli dosya assembly dosyası ve assembly üzerinde inceleme yapmak dosya açılıp incelenebilir.




gcc assembly çıktısı intel veya AT&T veya INTEL syntax'ına göre çıkartılabilir. Örnek kullanım aşağıdaki gibidir:

gcc main.c -S -masm=att -o asm.s
gcc main.c -S -masm=intel -o asm.s

C'de Dinamik Hafıza Yönetimi

07:33


C' de dinamik memory yönetiminde kullanılabilir 4 tane fonksiyon vardır. Bunlar:
  1. malloc
  2. calloc
  3. realloc
  4. free 

    fonksiyonlarıdır. Aşağıdaki örnekte tüm bu fonsiyonların kullanıldığı bir örnek program yazılmış, şimdi bu örnek üzerinden sırasıyla fonksiyonları incelemeye başlayalım.



malloc:

malloc fonksiyonu dinamik olan alan tahsis edilmesi için kullanılır. Prototipi aşağıdaki gibidir:

void *malloc(size_t size)

Fonksiyona parametre olarak verilen boyut kadar alan oluşturur ve alanın başlangıç adresini void * türünden bir adres olarak geri döndürür. 

calloc:

calloc fonksiyonu da dinamik olan alan tahsis edilmesi için kullanılır. malloc ile farkı ise calloc’un ayırdığı hafıza bloğunun içeriğini sıfırlaması olmaktadır. Prototipi aşağıdaki gibidir:


void *calloc(size_t nitems, size_t size)


realloc:

realloc fonksiyonu ise daha önce malloc veya calloc fonksiyonu ile tahsis edilmiş olan alanı büyütmek için kullanılır.
void *realloc(void *ptr, size_t size)
birinci parametre malloc,calloc veya realloc ile tahsis edilmiş olan alanın başlagıç adresi ikinci parametre ise yeni boyut olarak bildirilmektedir.

Free:

Free fonksiyonu parametre olarak aldığı başlangıç adresinde tahsis edilmiş olan alanın boşaltılmasını sağlar.
En başta yazılan programın çıktısı da aşağıdaki gibidir.