ANSI C etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
ANSI C etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

atexit Fonksiyonu

06:13

Bir program exit yaparken programdan çıkmadan önce çağrılmasını istediğimiz fonksiyonlar varsa bunlar için atexit fonksiyonunu kullanmamız gerekir. Bu fonksiyon parametre olarak aşağıda görüldüğü gibi geri dönüşü void parametre almayan bir fonksiyon adresini istemektedir. Bu fonksiyon istenildiği gibi çağrıldıktan sonra exit yapılırken devreye girer ve belirtilen fonksiyonlar çağrılarak çalıştırılır ve program sonlanır.



Yukarıda görüldüğü gibi foo ve bar foksiyonları doğrudan çağrılmamasına rağmen çalışmış ve program sonlanmıştır. Çalışma sırası stack veri yapısına uygun sırada olmaktadır.

printf fonksiyonunun türevleri [ printf, sprintf, snprintf, fprintf ]

23:17

Program çıktılarını ekrana, dosyaya vb. yerlere yazmak için kullanılan printf, sprintf, snprintf, fprintf fonksiyonlarını kısaca inceleyelim:

printf:


printf formatlı olarak ekrana yazmak için kullanılan bir fonksiyondur. Ortalama herkesin bildiği bir fonksiyondur.

int printf(const char *format, ...);

sprintf:

sprintf ise formatlı olarak bir veri yapısına yazan fonksiyondur.

int sprintf(char *str, const char *format, ...);




snprintf:

snprintf ise sprintf ile benzer biçimde çalışsa da farkı 2. parametre olarak aldığı değer kadar veri yapısına yazmasıdır.

int snprintf(char *str, size_t size, const char *format, ...);




fprintf:


fprintf ise formatlı olarak dosyaya yazan bir fonksiyondur.

int fprintf(FILE *stream, const char *format, ...);




if deyimi ile ilgili tüyolar

10:37

  • if deyimi yazılırken if’in içinde birden fazla kontrol yapılacaksa if(exp1 && exp2 && exp3 … gibi durumlarda) başa en az maliyetli olan konur ki çalışma anında kontrol maliyeti düşsün ya da olumsuz olma ihtimali yüksek olan varsa başa gene bu ifade konulur ki maliyet açısından avantajlı çalışabilsin.
  • Bütün deyimler basit deyim ise virgül operatörü kullanılarak fazlalıklar elenebilir.

image
  • if(Foo()) ifadesinde Foo fonksiyonu geriye bir şey döndürmüyorsa logic true olacaktır hata vermeyecektir ve if ifadesinin içi her zaman çalışacaktır.
  • C’de redundancy kavramı fazladan ,fuzuli kod yazma anlamına gelmektedir. C’de bir kodu yazmadan da oluyorsa o kodu yazmamak genellikle doğru olacaktır. Mesela
image

olarak ta yazabiliriz. Burada görüldüğü gibi else ifadesine gerek yoktur.

image

ifadesi aslında y=x anlamına gelmekedir.

image

  ifadesi aslında z=y==x ifadesiyle aynı çalışacaktır.

image

Yukarıdaki else hangi if’e aittir. Bu else ingilizcede dangling else denir. Ve C ‘de bu else içteki if’e aittir.

  • if else veya switch-case’lerde sıralama yapılırken olma olasılığı yüksek olan ifadeler yukarı yazılır ki fazla kontrolden sakınılmış olsun.


C'de extern anahtar sözcüğü

11:21

Bir kaynak dosya içinde tanımlanan bir değişkene ya da işleve, yalnızca tanımlandığı kaynak dosya içinde değil, projeyi oluşturan diğer kaynak dosyalarda da erişilebiliyorsa, nesne ya da işlevin dış bağlantısı “external linkage” vardır denir.

Bir kaynak dosya içinde tanımlanan bir değişkene ya da işleve, yalnızca tanımlandığı kaynak dosya içinde erişilebiliyorsa, projeyi oluşturan diğer kaynak dosyalarda erişilemiyorsa, nesne ya da işlevin iç bağlantısı “internal linkage” vardır denir.

extern anahtar kelimesi derleyiciye değişkenin dışarıda tanımlandığını bildirmek için kullanılan bir keyword'tür. Compiler extern keyword'unu gördüğünde değişken için alan tahsisinde bulunmaz. 





Yukarıdaki a.c | b.c | c.c şeklinde üç c dosyası bulunmaktadır. b.c dosyası içinde g_x değişkeni bildirilmiş a.c ile c.c dosyalarında extern keyword'u ile dış bağlantıya ait g_x değişkeni kullanılmıştır.



eğer b.c dosyasında da int g_x bildirimi olmasaydı veya orada da extern g_x bildirilimi yapılsaydı compile aşamasında olmasada link aşamasında hata verecekti.



scanf fonksiyonunun türevleri [ scanf, sscanf, fscanf ]

10:58


scanf, sscanf, fscanf fonksiyonları okuma amaçlı kullanılan fonksiyonlardır. Kısaca her birine değinmek gerekirse:

scanf:

scanf stdin dosyasından formatlı okuma yapmak için kullanılan bir fonksiyondur.  

int scanf(const char *format, ...);




sscanf:

sscanf ise bir veri yapısından formatlı okuma yapmak için kullanılan bir fonksiyondur.

int sscanf(const char *str, const char *format, ...);



fscanf:

fscanf ise dosyadan formatlı okuma yapmak için kullanılan bir fonksiyondur.

int fscanf(FILE *stream, const char *format, ...);


ÖZET: scanf format bilgisini ve bu formata göre aldığı değerleri yazacağı adresi ister. fscanf ise dosyadan formatlı okuma yapmak için kullanılır. scanf'e ek olarak dosya handle değerini ister. sscanf ise dosya yerine string gibi bir veri yapısından formatlı okuma yapar.


C'de Operatörler

11:15

ATAMA OPERATÖRLERİ

C' de bir den fazla atama operatörü vardır. Diğer dillerde atama operatörünün değer üretmesi gibi bir durum yoktur. C’de bu operatörde değer üretir.
image
C dilinde atama operatörü bir değer üretir.
  • Atama operatörünün ürettiği değer nesneye atanan değerdir.
  • Atama operatörünün öncelik yönü sağdan sola doğrudur.
x=y=z=t=func();
image

  • Well Formed : Çalışma Anında ne olacağı belli olan bunun garantisi verilen programlardır. Bazı kodlar çalışma anında ne olacağı kesin değildir. Kod legal ama çalışma anında ne olacağı, nasıl cevap vereceği belli değildir. Run time ‘da her şey olabilir. (ill formed)
  • Undefined Behaviour : Syntax hatası olmamasına karşın run time’de nasıl çalışacağına ilişkin bir garanti olmamasıdır.
image

image

  • Sequence point : yaptığınız işlemin etkisini gösterdiği noktaya denir.

image

image

Noktalı virgülden sonra ve yukarıda ki 4 operatörden sonra ve ( ) ‘den sonra yan etki noktası vardır.
Bir yan etki noktasından önce yan etkiye maruz kalan bir değişkeni tekrardan kullanmayın.
  • Tanımsız davranış demek hatalı kod demektir.
C’de bazı durumlarda nasıl davranacağı belirtilmemiştir. Bu durumlara ise Undefined Behavior denir. Yani ne sonuc vereceği belirsizdir.

image

Mesela 15%-2 işleminin sonucu +1 de olabilir -1 ‘de Derleyiciden derleyiciye değişen davranışlardır. Standartlar bu durumun uclarını açık bırakmışlardır. Bu sebeple genel olarak derleyiciler öğretmen olarak kullanmak yanlış bir yargıya ulaşmamıza sebep olabilir.

VİRGÜL OPERATÖRÜ

Diğer programlama dillerinde virgül operatörü yoktur. C’de virgül hem operatör hem de syntax’ın bir parçasıdır.
image

  • for (i=3,k=5;k<x;k++) :ifade virgül operatörü sayesinde yapılmıştır. Virgül operatörünün de geri dönüş değeri vardır. Virgül operatörünün sağındaki operand
x=10, y=20;                 
z=(y,x); z 10’a eşit olur.

  • C’de yapılan en büyük hatalardan biri "." konulması gereken yere "," konulmasıdır.


image

LOGIC OPERATÖRLER


  • !          :logical DEĞİL
  • ||          :logical OR 
  • &&     :logical AND
  • |           :bitwise OR
  • &        :bitwise AND

Bazı aynı anlamlı ifadeler:


  • x<y      :    y>x
  • x>y      :    y<x
  • x>=y    :    !(x<y)
  • x<=y    :    !(y<x)
  • x==y    :    [!(x>y)&&!(x<y)]
  • x!=y     :    ((x<y)||(y<x))


C’de kısa devre kuralı
 :


  if(x>0 && y<-5) if’in içinde bulunan koşullardan biri yanlışsa ve operatöründe sola bakmak anlamsızdır bu nedenle C’de bu duruma kısa devre kuralı denir. Kısa devre kuralında bir Ve için bir yanlış yeterli iken veya için bir doğru yeterlidir.



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. 

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.






















C'de Atama ile İlk Değer Verme Arasındaki Fark

10:34


Yerel degisken bildirimi fonksiyonun herhangi bir yerinde tanimlanabilir. AMA yürütülebilir deyimden sonra bildirim deyimi kullanilamaz. (C89)
image
,
Atama ile ilk deger vermeyi birbirinden ayirmak gerekir. Örneğin = isaretinin kullanildigi iki farkli yer var bunlar atama operatörü “ x=30 " ve bildirim deyimi " int x=30 ”. Bu iki “=" birbirinden farkli anlamlara gelmektedir.
image
C++’da böyle bir sorun söz konusu değildir. Dosyamızın uzantısını .cpp yaparak derlersek aşağıda görüldüğü gibi herhangi bri hata vermeyecektir.
image
Program sorunsuzca çalışacaktır.
image
Tekrar bildirim ile atama işlemlerinin farkına değinmek gerekirse;
image
image
Atama var olan -hayatta olan- bir nesneye yapılır. ( Derleyici int tmp=0 dedigimizde tmp’i aramaz. ) Bu kural'ın getiriliş amacı DEC mimarilerine uygunluk olarak kabul edilmiştir. C'nin sonraki standartlarında bu kural kaldırılmıştır.